Creating the base data provider class

The Sitefinity modules are based on the provider pattern. They use data providers for their data access layer. A good practice, when creating a data provider, is to create a base abstract class that provides a common implementation for the provider. Then use it to implement concrete data providers for the different data stores. This spares you of writing the common part of the code for each of the concrete implementations. Even if you intend to have only one data provider in your module, consider using this base provider class.

To create the abstract base class for the data provider, perform the following:

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

    using System.Collections;
    using ProductsModule.Model;
    using Telerik.Sitefinity.Configuration;
    using Telerik.Sitefinity.Data;
    using Telerik.Sitefinity.Data.Linq.Dynamic;
    using Telerik.Sitefinity.GenericContent.Model;
    using Telerik.Sitefinity.Model;
    using Telerik.Sitefinity.Modules.GenericContent;
    using Telerik.Sitefinity.Security;
    using Telerik.Sitefinity.Security.Configuration;
    using Telerik.Sitefinity.Security.Model;

  5. Make the ProductsDataProvider class inherit the ContentDataProviderBase class and ILanguageDataProvider:

    public abstract class ProductsDataProvider : ContentDataProviderBase, ILanguageDataProvider
    {
    }

  6. Implement the ContentDataProviderBase class.

    To implement it, you must have the following members in the ProductsDataProvider class:

    • RootKey - in this property you must store an identifier for the provider.
    • GetParentTypeFor - this method is used for modules with parent-child relationships, such as Lists and List Items, and Blogs and Blog Posts. If the module doesn't have such relationship, you must return null.
    • GetUrlTypeFor - in this method, you must return the UrlData implementation created for the persistent class.
    • GetKnownTypes - in this method you must return a list of types that your provider handles.
    • GetItemsByTaxon - this method allows you to retrieve ProductItem objects by tag or category using the Sitefinity taxonomy system.

    Following is the code for the implementation of these members:

    public override IEnumerable GetItemsByTaxon(Guid taxonId, bool isSingleTaxon, string propertyName, Type itemType, string filterExpression, string orderExpression, int skip, int take, ref int? totalCount)
    {
        if (itemType == typeof(ProductItem))
        {
            this.CurrentTaxonomyProperty = propertyName;
     
            IQueryable<ProductItem> query = this.GetProducts();
            if (!String.IsNullOrWhiteSpace(filterExpression))
                query = query.Where(filterExpression);
            if (!String.IsNullOrWhiteSpace(orderExpression))
                query = query.OrderBy(orderExpression);
            if (isSingleTaxon)
            {
                var query0 = from i in query
                                where i.GetValue<Guid>(this.CurrentTaxonomyProperty) == taxonId
                                select i;
     
                query = query0;
            }
            else
            {
     
                var query1 = from i in query
                                where (i.GetValue<IList<Guid>>(this.CurrentTaxonomyProperty)).Any(t => t == taxonId)
                                select i;
                query = query1;
            }
     
            if (totalCount.HasValue)
            {
                totalCount = query.Count();
            }
     
            if (skip > 0)
                query = query.Skip(skip);
            if (take > 0)
                query = query.Take(take);
            return query;
        }
     
        throw GetInvalidItemTypeException(itemType, this.GetKnownTypes());
    }
     
    public override Type GetParentTypeFor(Type contentType)
    {
        return null;
    }
     
    public override Type GetUrlTypeFor(Type itemType)
    {
        if (itemType == typeof(ProductItem))
        {
            return typeof(ProductItemUrlData);
        }
     
        throw GetInvalidItemTypeException(itemType, this.GetKnownTypes());
    }
     
    private static Type[] knownTypes;
     
    public override Type[] GetKnownTypes()
    {
        if (knownTypes == null)
        {
            knownTypes = new Type[]
            {
                typeof(ProductItem)
            };
        }
     
        return knownTypes;
    }
     
    public override string RootKey
    {
        get { return "ProductsDataProvider"; }
    }

  7. Create abstract methods for the following operations:

    • Create product
    • Get product
    • Get products
    • Delete products
    • Create language data
    • Get language data

    Following is the definitions for the methods:

    [MethodPermission(ProductsConstants.Security.PermissionSetName, ProductsConstants.Security.Create)]
    public abstract ProductItem CreateProduct();
     
    [MethodPermission(ProductsConstants.Security.PermissionSetName, ProductsConstants.Security.Create)]
    public abstract ProductItem CreateProduct(Guid id);
     
    [ValuePermission(ProductsConstants.Security.PermissionSetName, ProductsConstants.Security.View)]
    public abstract ProductItem GetProduct(Guid id);
     
    [EnumeratorPermission(ProductsConstants.Security.PermissionSetName, ProductsConstants.Security.View)]
    public abstract IQueryable<ProductItem> GetProducts();
     
    [ParameterPermission("product", ProductsConstants.Security.PermissionSetName, ProductsConstants.Security.Delete)]
    public abstract void DeleteProduct(ProductItem product);
     
    public abstract Telerik.Sitefinity.Lifecycle.LanguageData CreateLanguageData();
     
    public abstract Telerik.Sitefinity.Lifecycle.LanguageData CreateLanguageData(Guid id);
     
    public abstract Telerik.Sitefinity.Lifecycle.LanguageData GetLanguageData(Guid id);
     
    public abstract IQueryable<Telerik.Sitefinity.Lifecycle.LanguageData> GetLanguageData();

    You mark the methods with permission attributes to specify the permissions for the respective method, when Sitefinity invokes it.

  8. Override the respective methods of the ContentDataProviderBase class and call the abstract methods from them.

    public override object CreateItem(Type itemType, Guid id)
    {
        if (itemType == null)
        {
            throw new ArgumentNullException("itemType");
        }
     
        if (itemType == typeof(Comment))
        {
            return this.CreateComment(typeof(ProductItem), id);
        }
     
        if (itemType == typeof(ProductItem))
        {
            return this.CreateProduct(id);
        }
     
        throw GetInvalidItemTypeException(itemType, this.GetKnownTypes());
    }
     
    public override object GetItem(Type itemType, Guid id)
    {
        if (itemType == typeof(Comment))
        {
            return this.GetComment(id);
        }
     
        if (itemType == typeof(ProductItem) || itemType == null)
        {
            return this.GetProduct(id);
        }
     
        return base.GetItem(itemType, id);
    }
     
    public override object GetItemOrDefault(Type itemType, Guid id)
    {
        if (itemType == typeof(Comment))
        {
            return this.GetComments().Where(c => c.Id == id).FirstOrDefault();
        }
     
        if (itemType == typeof(ProductItem))
        {
            return this.GetProducts().Where(n => n.Id == id).FirstOrDefault();
        }
     
        return base.GetItemOrDefault(itemType, id);
    }
     
    public override IEnumerable GetItems(Type itemType, string filterExpression, string orderExpression, int skip, int take, ref int? totalCount)
    {
        if (itemType == null)
        {
            throw new ArgumentNullException("itemType");
        }
     
        if (itemType == typeof(Comment))
        {
            return SetExpressions(this.GetComments(), filterExpression, orderExpression, skip, take, ref totalCount);
        }
     
        if (itemType == typeof(ProductItem))
        {
            return SetExpressions(this.GetProducts(), filterExpression, orderExpression, skip, take, ref totalCount);
        }
     
        throw GetInvalidItemTypeException(itemType, this.GetKnownTypes());
    }
     
    public override void DeleteItem(object item)
    {
        if (item == null)
        {
            throw new ArgumentNullException("item");
        }
     
        var itemType = item.GetType();
        this.providerDecorator.DeletePermissions(item);
     
        if (itemType == typeof(Comment))
        {
            this.Delete((Comment)item);
            return;
        }
     
        if (itemType == typeof(ProductItem))
        {
            this.DeleteProduct((ProductItem)item);
            return;
        }
     
        throw GetInvalidItemTypeException(item.GetType(), this.GetKnownTypes());
    }

    In the overrides of methods, you must check for whether the item type is Comment.

  9. Override the CommitTransaction method and mark it with the TransactionPermission attribute in the following way:

    [TransactionPermission(typeof(ProductItem), ProductsConstants.Security.PermissionSetName, SecurityConstants.TransactionActionType.Updated, ProductsConstants.Security.Modify)]
    public override void CommitTransaction()
    {
        base.CommitTransaction();
    }

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