How to create a ContentHandler
- 100%
- 6.0
- Enterprise
- Community
Planned
Contents |
Overview
The content handler defines custom programmed logic of a content type implemented in .Net code (ie. C#). Attached business logic can be added to a content type by implementing a custom content handler. In this article we describe how to develop a content handler.Steps
In this section I will show you how to create a new content handler step-by-step and explain the parts of the implementation.
1. Create a new class
2.Writing the minimal content handler
Rename your created class to MyContentHandler.cs and paste the code below. We put the ContentHandler attribute onto the class, inherit from GenericContent (or any class that is Node) and write the constructors:
using SenseNet.ContentRepository; using SenseNet.ContentRepository.Storage; using SenseNet.ContentRepository.Schema; namespace MyContentHandler { [ContentHandler] public class MyContentHandler : GenericContent { // =================================================================================================== Constructors // for initialize a new MyContentHandler instance public MyContentHandler(Node parent) : this(parent, null) { } // for initialize a new MyContentHandler inherited instance public MyContentHandler(Node parent, string nodeTypeName) : base(parent, nodeTypeName) { } // for initialize instance in the loading operation protected MyContentHandler(NodeToken nt) : base(nt) { } } }
This is the minimal but usable content handler.
The first constructor calls the base with null parameter as nodeTypeName. This means that the name of the associated content type definition will be the same as the name of this class (in our case MyContentHandler). If you want to use a different name for the associated CTD, use a string in this parameter:
// for initialize a new MyContentHandler instance public MyContentHandler(Node parent) : this(parent, "DifferentTypeName") { }
3. Implement properties
Let's implement two properties. Define a property that can store the birth date and another property that calculates the age from the birth date. The birth date will be stored in the database so mark this property with a RepositoryProperty attribute. The Age property stores nothing because it is calculated so it is a simple read only property:
// =================================================================================================== Properties private const string BIRTHDATE = "BirthDate"; [RepositoryProperty(BIRTHDATE, RepositoryDataType.DateTime)] public virtual DateTime BirthDate { get { return base.GetProperty<DateTime>(BIRTHDATE); } set { base.SetProperty(BIRTHDATE, value); } } private const string AGE = "Age"; public virtual string Age { get { return (DateTime.Now - BirthDate).ToString(); } }
4. Implement property routing
Both new properties are readable so override the GetProperty method and place the appropriate routing logic inside the required switch block. Override the SetProperty method and place routing logic inside the switch block for the writeable BirthDate property. You should also not forget to call the base implementation in the default branches of the switch blocks.
// =================================================================================================== Property routing public override object GetProperty(string name) { switch (name) { case BIRTHDATE: return this.BirthDate; case AGE: return this.Age; default: return base.GetProperty(name); } } public override void SetProperty(string name, object value) { switch (name) { case BIRTHDATE: this.BirthDate = (DateTime)value; break; default: base.SetProperty(name, value); break; } }
5. Implement custom business logic
In the most common scenario you must validate the whole object considering all property values and other things when the object is being saved. This kind of validation can be very complicated but the skeleton is simple. Override the Save(NodeSaveSettings) method, check conditions and call the base if the whole content is valid:
public override void Save(NodeSaveSettings settings) { if (CheckAllRequirements()) base.Save(settings); else throw new InvalidOperationException("Content cannot be saved because it is invalid."); } private bool CheckAllRequirements() { //TODO: return false if a condition isn't satisfied. return true; }
6. Create the associated content type definition
Right click on the project node in the Solution Explorer containing your content handler and select Add / New item..., select Data / XML file, type into the Name textbox: MyContentHandlerCTD.xml, and click Add button.
The minimal requirements are the following:
- Unique name: MyContentHandler.
- Existing parent type: GenericContent.
- Existing content handler type: MyContentHandler.MyContentHandler (after installation this will exist).
- Display name and description of the CTD
- All fields in the Fields element
- Field name that equals to the desired property or right property binding
- Appropriate field type
- Display name of the field
<?xml version="1.0" encoding="utf-8" ?> <ContentType name="MyContentHandler" parentType="GenericContent" handler="MyContentHandler.MyContentHandler" xmlns="http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition"> <DisplayName>My Content Type</DisplayName> <Description>For demonstration only.</Description> <Fields> <Field name="BirthDate" type="DateTime"> <DisplayName>Birth date</DisplayName> </Field> <Field name="Age" type="ShortText"> <DisplayName>Age</DisplayName> </Field> </Fields> </ContentType>
The Content Type Definition schema can help you to write easier the CTD if you copy it into the your project. Right click on the project node in the Solution Explorer, select Add / Existing Item, browse the schema that is in your Sense/Net code: "...\Source\SenseNet\ContentRepository\Schema\ContentTypeDefinition.xsd". If the schema is in your project and you edit an XML element that's namespace is the CTD's namespace, the Visual Studio code completion will work:
7. Deploy your content handler
Compile the project (F6, or CTRL+SHIFT+B for newer versions of Visual Studio). If you implemented your content handler in a separate project other than your web application project copy your dll into the bin directory of the Sense/Net's web folder and start the portal. Follow the How to create a Content Type article to install your content type definition. After that you have finished the development and your Content Handler is usable in the Sense/Net content repository.
8. Test your content handler
You can test the following:
- when you save a content of MyContentHandler type, the Age field will not be stored in the database even if it is presented with an editable control,
- when you save a content of MyContentHandler type, your custom validation code may cancel saving and the provided error message will be displayed,
- you can create a content of MyContentHandler type from code using strong type reference and you also get intellisense:
var node = new MyContentHandler(Node.LoadNode("/Root/Sites/Default_Site")); node.Name = "mycontent"; node.Save();