Creating the data persistent class

A data persistent class is a class that is used for storage in the context of Sitefinity. Such classes are also called model classes. Depending on the design, you can create more than one model class.

Because the Products module is a content based module, the data persistent class must be derived from the Content class. To create your class, you must perform the following:

  1. From the context menu of the folder Model in the ProductsModule project, click Add » Class.
  2. Name the class file ProductItem.cs and click Add.
  3. Open the file ProductItem.cs.
  4. Add the following using statements:

    using System.Runtime.Serialization;
    using Telerik.OpenAccess;
    using Telerik.Sitefinity;
    using Telerik.Sitefinity.ContentViewAttributes;
    using Telerik.Sitefinity.GenericContent.Model;
    using Telerik.Sitefinity.Lifecycle;
    using Telerik.Sitefinity.Model;
    using Telerik.Sitefinity.Model.ContentLinks;
    using Telerik.Sitefinity.Security;
    using Telerik.Sitefinity.Security.Model;
    using Telerik.Sitefinity.Versioning.Serialization.Attributes;
    using Telerik.Sitefinity.Versioning.Serialization.Extensions;
    using Telerik.Sitefinity.Versioning.Serialization.Interfaces;
    using Telerik.Sitefinity.Workflow.Model.Tracking;
  5. Make the ProductItem class inherit the Content class and to implement the IApprovalWorkflowItemISecuredObject,ILocatableISitefinityCustomTypeSerialization and ILifecycleDataItemGeneric interfaces:

    public class ProductItem : Content, IApprovalWorkflowItem, ISecuredObject, ILocatable, ISitefinityCustomTypeSerialization, ILifecycleDataItemGeneric
    {
    }

    Inheriting the Content base class enables you to use the following features in your Products module:

  6. Define the Price, QuantityInStock, WhatIsInTheBox, Content properties:

    private decimal price;
    private int quantityInStock;
    private Lstring whatIsInTheBox;
    private Lstring content;
     
    [DataMember]
    [FieldAlias("price")]
    public decimal Price
    {
        get
        {
            return this.price;
        }
     
        set
        {
            this.price = value;
        }
    }
     
    [DataMember]
    [FieldAlias("quantityInStock")]
    public int QuantityInStock
    {
        get
        {
            return this.quantityInStock;
        }
     
        set
        {
            this.quantityInStock = value;
        }
    }
     
    [DataMember]
    [MetadataMapping(true, false)]
    public virtual Lstring WhatIsInTheBox
    {
        get
        {
            if (this.whatIsInTheBox == null)
            {
                this.whatIsInTheBox = this.GetString("WhatIsInTheBox");
            }
     
            return this.whatIsInTheBox;
        }
     
        set
        {
            this.whatIsInTheBox = value;
            this.SetString("WhatIsInTheBox", this.whatIsInTheBox);
        }
    }
     
    [DataMember]
    [MetadataMapping(true, true)]
    [UserFriendlyDataType(UserFriendlyDataType.LongText)]
    [CommonProperty]
    [ScaffoldInfo(@"<sitefinity:HtmlField runat=""server"" DisplayMode=""Read"" Value='<%# Eval(""Content"")%>' />")]
    public Lstring Content
    {
        get
        {
            if (this.content == null)
            {
                this.content = this.GetString("Content");
            }
     
            return this.ApplyContentFilters(this.content);
        }
     
        set
        {
            this.content = value;
            this.SetString("Content", this.content);
        }
    }

    The DataMember attribute allows the properties to be serialized for usage in a WCF service. The ScuffoldInfo attribute specifies that the value of the content property must be displayed in a specific markup element.

    The Content property is of type LString. This type is used for values that you want to localize.

  7. Create a class that implements the UrlData abstract class.

    1. From the context menu of the folder Model in the ProductsModule project, click Add » Class.
    2. Name the class file ProductItemUrlData.cs and click Add.
    3. Open the file ProductItemUrlData.cs.
    4. Add the following using statements:

      using Telerik.OpenAccess;
      using Telerik.Sitefinity.GenericContent.Model;
      using Telerik.Sitefinity.Model;
      using Telerik.Sitefinity.Versioning.Serialization.Attributes;
    5. Make the ProductItemUrlData class inherit the UrlData abstract class:

      public class ProductItemUrlData : UrlData
      {
      }
    6. Implement the UrlData abstract class in the following way:

      public ProductItemUrlData()
          : base()
      {
          this.Id = Guid.NewGuid();
      }
       
      private ProductItem parent;
       
      [FieldAlias("parent")]
      [NonSerializableProperty]
      public override IDataItem Parent
      {
          get
          {
              if (this.parent != null)
                  ((IDataItem)this.parent).Provider = ((IDataItem)this).Provider;
              return this.parent;
          }
          set
          {
              this.parent = (ProductItem)value;
          }
      }

      The parent property returns the parent ProductItem of the instance.

    7. Mark the ProductItemUrlData class with the Persistent attribute.

      [Persistent]
      public class ProductItemUrlData : UrlData

      The Persistent attribute allows the class to be persisted in the database.

    The implementation of the UrlData class associates your items with a URL.

  8. In the ProductItem class, implement the ILocatable interface. This requires from you to create the following members:

    private ProviderTrackedList<ProductItemUrlData> urls;
     
    [NonSerializableProperty]
    public bool AutoGenerateUniqueUrl
    {
        get { return false; }
    }
     
    [FieldAlias("urls")]
    [NonSerializableProperty]
    public virtual IList<ProductItemUrlData> Urls
    {
        get
        {
            if (this.urls == null)
            {
                this.urls = new ProviderTrackedList<ProductItemUrlData>(this, "Urls");
            }
     
            this.urls.SetCollectionParent(this);
     
            return this.urls;
        }
    }
     
    [NonSerializableProperty]
    IEnumerable<UrlData> ILocatable.Urls
    {
        get
        {
            return this.Urls.Cast<UrlData>();
        }
    }
     
    public void RemoveUrls(Func<UrlData, bool> predicate)
    {
        if (this.urls != null)
            this.urls.RemoveUrls(predicate);
    }
     
    public void ClearUrls(bool excludeDefault)
    {
        if (this.urls != null)
            this.urls.ClearUrls(excludeDefault);
    }

    The ILocatable interface is responsible for associating each of your items with a URL. Note that the methods required by the ILocatableinterface, use the ProductItemUrlData class.

  9. Because the Products module uses permissions, you must implement the ISecuredObject interface in the model class. Following is the code for the required methods:

    private bool canInheritPermissions;
     
    [FieldAlias("canInheritPermissions")]
    [NonSerializableProperty]
    public bool CanInheritPermissions
    {
        get
        {
            return this.canInheritPermissions;
        }
     
        set
        {
            this.canInheritPermissions = value;
        }
    }
     
    private bool inheritsPermissions;
     
    [FieldAlias("inheritsPermissions")]
    [NonSerializableProperty]
    public bool InheritsPermissions
    {
        get
        {
            return this.inheritsPermissions;
        }
     
        set
        {
            this.inheritsPermissions = value;
        }
    }
     
    IList<PermissionsInheritanceMap> permissionChildren;
     
    [FieldAlias("permissionChildren")]
    [NonSerializableProperty]
    public IList<PermissionsInheritanceMap> PermissionChildren
    {
        get
        {
            if (this.permissionChildren == null)
            {
                this.permissionChildren = new TrackedList<PermissionsInheritanceMap>();
            }
     
            return this.permissionChildren;
        }
        set
        {
            this.permissionChildren = value;
        }
    }
     
    private ProviderTrackedList<Permission> permissions;
     
    [FieldAlias("permissions")]
    [NonSerializableProperty]
    public IList<Permission> Permissions
    {
        get
        {
            if (this.permissions == null)
            {
                this.permissions = new ProviderTrackedList<Permission>(this, "Permissions");
            }
     
            this.permissions.SetCollectionParent(this);
            return this.permissions;
        }
    }
     
    private IDictionary<string, string> permissionsetObjectTitleResKeys;
     
    [NonSerializableProperty]
    public IDictionary<string, string> PermissionsetObjectTitleResKeys
    {
        get
        {
            return permissionsetObjectTitleResKeys;
        }
     
        set
        {
            permissionsetObjectTitleResKeys = value;
        }
    }
     
    private string[] supportedPermissionSets;
     
    [NonSerializableProperty]
    public string[] SupportedPermissionSets
    {
        get
        {
            return supportedPermissionSets;
        }
     
        set
        {
            supportedPermissionSets = value;
        }
    }
  10. Because the Products module uses Approval workflow, you must implement the IApprovalWorkflow interface. Following is the code for the members, that are required by the interface:

    private Lstring approvalWorkflowState;
     
    [DataMember]
    [Database(DBType = "VARCHAR", DBSqlType = "NVARCHAR")]
    public Lstring ApprovalWorkflowState
    {
        get
        {
            if (this.approvalWorkflowState == null)
            {
                this.approvalWorkflowState = this.GetString("ApprovalWorkflowState");
            }
     
            return this.approvalWorkflowState;
        }
        set
        {
            this.approvalWorkflowState = value;
            this.SetString("ApprovalWorkflowState", this.approvalWorkflowState);
        }
    }
     
    private ApprovalTrackingRecordMap approvalTrackingRecordMap;
     
    [FieldAlias("approvalTrackingRecordMap")]
    [NonSerializableProperty]
    public ApprovalTrackingRecordMap ApprovalTrackingRecordMap
    {
        get
        {
            return this.approvalTrackingRecordMap;
        }
        set
        {
            this.approvalTrackingRecordMap = value;
        }
    }
  11. Implement the ISitefinityCustomTypeSerialization interface:

    public void Serialize(object obj, out Dictionary<string, object> serializedObject)
    {
     
        var unfilteredObjects = obj.GetSerializationPropertyValueCollection();
        serializedObject = new Dictionary<string, object>(unfilteredObjects.Count);
     
        //Removing of ContentLink objects is neccesary since we cannot handle deserialziation of this type
        //ContentLinks are persisted outside the ProductItem and deserialization causes side effects
        foreach (var item in unfilteredObjects)
        {
            if (!(item.Value is ContentLink))
            {
                serializedObject.Add(item.Key, item.Value);
            }
        }
     
        if (Urls.Count > 0)
        {
            var urls = obj.GetListSerializationItemsItems("Urls");
            serializedObject.Add("URLS", urls);
        }
    }
     
    public void Deserialize(ref object obj, Dictionary<string, object> serializedObject)
    {
        VersioningUtilities.SetSerializationPropertyValueCollection(ref obj, serializedObject);
     
        ((ProductItem)obj).Urls.Clear();
        if (serializedObject.ContainsKey("URLS"))
        {
            VersioningUtilities.SetListDeserializedItems(ref obj, "Urls", "Parent", this, serializedObject["URLS"]);
        }
    }
  12. Implement the ILifecycleDataItem interface:

    private TrackedList<string> publishedTranslations;
    private TrackedList<LanguageData> languageData;
     
    [Searchable(false)]
    [NonSerializableProperty]
    public virtual IList<string> PublishedTranslations
    {
        get
        {
            if (this.publishedTranslations == null)
                this.publishedTranslations = new TrackedList<string>();
            return this.publishedTranslations;
        }
    }
     
    [Searchable(false)]
    [NonSerializableProperty]
    public IList<LanguageData> LanguageData
    {
        get
        {
            if (this.languageData == null)
                this.languageData = new TrackedList<LanguageData>();
            return this.languageData;
        }
    }
  13. Create a constructor that sets default values to the ISecuredObject members:

    public ProductItem()
    {
        // set default values
        this.inheritsPermissions = true;
        this.canInheritPermissions = true;
        this.supportedPermissionSets = new string[] { ProductsConstants.Security.PermissionSetName, SecurityConstants.Sets.Comments.SetName };
    }
  14. Mark the ProductItem class with the DataContract, ManagerType and Persistent attributes.

    [DataContract(Namespace = "http://sitefinity.com/samples/productsmodule", Name = "ProductItem")]
    [ManagerType("ProductsModule.Data.ProductsManager, ProductsModule")]
    [Persistent(IdentityField = "content_id")]
    public class ProductItem : Content, IApprovalWorkflowItem, ISecuredObject, ILocatable, ISitefinityCustomTypeSerialization, ILifecycleDataItemGeneric

    The Persistent attribute allows the class to be persisted in the database. Set the IdentityField property to content_id.

    The DataContract attribute allows the class to be serialized for usage with WCF services. The ManagerType attribute specifies the manager class that will use the persistent class. Its argument must contain the type of the manager and the name of the assembly that it is located in.

    NOTE: The manager class will be implemented later in this tutorial. For more information, see Implementing the manager class.

Related topics:

Feedback

How useful is this article?

Tell us more

Submit
Your message was successfully sent.

We appreciate your feedback.

Your message could not be sent.

OK