Please help us by completing this survey

Go to survey
Documentation

Docs / Development / Content Handler

Content Handler

The Content Handler defines custom “code behind” business logic of a Content Type implemented in .Net code (ie. C#). Content Handlers can override mapping between object properties and Content Repository fields, and they may also define properties that are not stored in the Content Repository (properties that are either calculated or retrieved from another data source).

The Content Handler is considered to be on the lower layer of the sensenet API. You may choose to work with the upper layer, represented by the unified Content class that you can use to work with all kinds of content stored in the Content Repository. In that case you will have access to fields (higher level metadata) but without the strongly typed properties of the Content Handler layer.

When to create a custom Content Handler

A Content Handler should be created in one of the following cases:

Reference Content Handler

The following code acts as a reference snippet that can be used as a common starting point when implementing a Content Handler. It defines the necessary constructors, examples for all types of properties and the property routing methods for these properties.

namespace MyNamespace
{
    [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) { }
 
 
        // =================================================================================================== Properties
        private const string STRINGPROPERTY = "StringProperty";
        [RepositoryProperty(STRINGPROPERTY, RepositoryDataType.String)] // Text, Int, DateTime
        public virtual string StringProperty
        {
            get { return base.GetProperty<string>(STRINGPROPERTY); }
            set { base.SetProperty(STRINGPROPERTY, value); }
        }
 
        private const string BOOLPROPERTY = "BoolProperty";
        [RepositoryProperty(BOOLPROPERTY, RepositoryDataType.Int)]
        public virtual bool BoolProperty
        {
            get { return base.GetProperty<int>(BOOLPROPERTY) != 0; }
            set { base.SetProperty(BOOLPROPERTY, value ? 1 : 0); }
        }
 
        private const string DECIMALPROPERTY = "DecimalProperty";
        [RepositoryProperty(DECIMALPROPERTY, RepositoryDataType.Currency)]
        public virtual Decimal DecimalProperty
        {
            get { return Convert.ToDecimal(this[DECIMALPROPERTY]); }
            set { this[DECIMALPROPERTY] = Convert.ToDecimal(value); }
        }
 
        private const string REFERENCEPROPERTY = "ReferenceProperty";
        [RepositoryProperty(REFERENCEPROPERTY, RepositoryDataType.Reference)]
        public virtual Node ReferenceProperty
        {
            get { return base.GetReference<Node>(REFERENCEPROPERTY); }
            set { base.SetReference(REFERENCEPROPERTY, value); }
        }
 
        private const string BINARYPROPERTY = "BinaryProperty";
        [RepositoryProperty(BINARYPROPERTY, RepositoryDataType.Binary)]
        public virtual BinaryData BinaryProperty
        {
            get { return this.GetBinary(BINARYPROPERTY); }
            set { this.SetBinary(BINARYPROPERTY, value); }
        }
 
 
        // =================================================================================================== Property routing
        public override object GetProperty(string name)
        {
            switch (name)
            {
                case STRINGPROPERTY:
                    return this.StringProperty;
                case BOOLPROPERTY:
                    return this.BoolProperty;
                case DECIMALPROPERTY:
                    return this.DecimalProperty;
                case REFERENCEPROPERTY:
                    return this.ReferenceProperty;
                case BINARYPROPERTY:
                    return this.BinaryProperty;
                default:
                    return base.GetProperty(name);
            }
        }
        public override void SetProperty(string name, object value)
        {
            switch (name)
            {
                case STRINGPROPERTY:
                    this.StringProperty = (string)value;
                    break;
                case BOOLPROPERTY:
                    this.BoolProperty = (bool)value;
                    break;
                case DECIMALPROPERTY:
                    this.DecimalProperty = (Decimal)value;
                    break;
                case REFERENCEPROPERTY:
                    this.ReferenceProperty = (Node)value;
                    break;
                case BINARYPROPERTY:
                    this.BinaryProperty = (BinaryData)value;
                    break;
                default:
                    base.SetProperty(name, value);
                    break;
            }
        }
    }
}

Assigning a Content Handler to a content

When you create a Content Type Definition you have to assign a Content Handler to the type so that the content will be saved, displayed, loaded in the appropriate manner. Here below is an extract from the Content Type Definition of the Car content type. As you can see the handler is assigned in the first line of the code.

<ContentType name="Car" parentType="GenericContent"
 handler="SenseNet.ContentRepository.GenericContent"
 xmlns="http://schemas.sensenet.hu/SenseNet/ContentRepository/ContentTypeDefinition">
    {...}
</ContentType>

As you can see, the Car Content Type is derived from the GenericContent Content Type, and its Content Handler is SenseNet.ContentRepository.GenericContent. You may develop new handlers to any Content Type, but if you don’t, GenericContent Content Handler is capable of handling your type.

It is very important to note that the Content Type hierarchy in the repository and the Content Handler class hierarchy has to be the same. This means whenever you want to create a new Content Type (e.g. CustomFolder as a child type of Folder) with a code-behind handler, your c# class has to inherit from the content handler class of the parent Content Type (in this case the Folder class).

Example

The following xml is the Content Type Definition of the Domain Content Type. As you can see a custom Content Handler is assigned to the type:

<?xml version="1.0" encoding="utf-8"?>
<ContentType name="Domain" parentType="Folder" handler="SenseNet.ContentRepository.Domain" xmlns="http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition">
  <DisplayName>Domain</DisplayName>
  <Description>A centrally-managed group of users and/or computers. sensenet has a built-in domain (BuiltIn), but you can syncronyze external LDAP directories as well.</Description>
  <Icon>Domain</Icon>
  <Fields>
    <Field name="Name" type="ShortText">
      <Configuration>
        <VisibleBrowse>Show</VisibleBrowse>
        <VisibleEdit>Show</VisibleEdit>
        <VisibleNew>Show</VisibleNew>
      </Configuration>
    </Field>
    <Field name="Title" type="ShortText">
      <Title>Title</Title>
      <Configuration>
        <VisibleBrowse>Hide</VisibleBrowse>
        <VisibleEdit>Hide</VisibleEdit>
        <VisibleNew>Hide</VisibleNew>
      </Configuration>
    </Field>
    <Field name="Description" type="LongText">
      <Configuration>
        <VisibleBrowse>Advanced</VisibleBrowse>
        <VisibleEdit>Advanced</VisibleEdit>
        <VisibleNew>Advanced</VisibleNew>
      </Configuration>
    </Field>
    <Field name="SyncGuid" type="ShortText">
      <Title>SyncGuid</Title>
      <Description>GUID of corresponding AD object.</Description>
      <Configuration>
        <VisibleBrowse>Advanced</VisibleBrowse>
        <VisibleEdit>Advanced</VisibleEdit>
        <VisibleNew>Advanced</VisibleNew>
      </Configuration>
    </Field>
    <Field name="LastSync" type="DateTime">
      <Title>LastSync</Title>
      <Description>Date of last synchronization.</Description>
    </Field>
  </Fields>
</ContentType>

The assigned Content Handler is a simple class:

using  SenseNet.ContentRepository.Schema;
using SenseNet.ContentRepository.Storage;
 
namespace SenseNet.ContentRepository
{
    [ContentHandler]
    public class Domain : Folder
    {
        public static readonly string NodeTypeName = "Domain";
        public static readonly string BuiltInDomainName = "BuiltIn";
 
        public Domain(Node parent) : this(parent, Domain.NodeTypeName) { }
        public Domain(Node parent, string nodeTypeName) : base(parent, nodeTypeName) { }
        protected Domain(NodeToken token) : base(token) { }
 
        //////////////////////////////////////// Public Properties ////////////////////////////////////////
 
        public bool IsBuiltInDomain
        {
            get { return Name == BuiltInDomainName; }
        }
    }
}

The above code extends the Content Type Definition with the following logic:

Please note that the Content Handler in the above example only extends the Domain Content Type’s default logic and does not override it. This means that the default behavior still applies to the Domain Content Type just as if no Content Handler was assigned:

To use the above defined Content Handler to create a new Domain content the following code can be written in C#:

var parent = Node.LoadNode("/Root/IMS");
var domain = new Domain(parent);
domain.Name = "MyCustomDomain";
domain.Save();

The following example shows how to use the extended logic of the Content Handler:

public bool CheckValidDomain(Domain domain)
{
    if (domain == null)
        return false;
 
    if (domain.ParentName != "IMS")
        return false;
 
    // domain is a built in domain
    if (domain.IsBuiltInDomain)
        return false;
 
    return true;
}

Is something missing? See something that needs fixing? Propose a change here.