Views: 23278
Number of votes: 6
Average rating:

Creating a Custom EPiServer Community Module - Part One

The EPiServer Community framework provides a wide variety of modules, from core features such as user management to modules suitable for intranets such as webmail and document archives to modules suitable for modern online communities such as video galleries, chat and blogs. Notwithstanding that, when building a large community you often need to extend the framework with custom modules.

In this article I will describe the basics of doing just that. I will describe the same methodology as many of the native modules are built with in order to also offer an insight into the inner workings of the Community framework. This article is one in a series of articles on this subject, the rest will follow within shortly.

Other articles in this series

» Creating a Custom EPiSever Community Module - Part Two

» Creating a Custom EPiSever Community Module - Part Three

» Creating a Custom EPiSever Community Module - Part Four

Get the Source Code

» Download the full source code for this article

Components in an EPiServer Community Module

A basic EPiServer Community module usually consists of these components:

  • One or more entity classes – Such as Blog, Image, User etc.
  • Collection classes – Strongly typed collections of the entity classes.
  • An entity provider – Provides instances of one or more entity types.
  • A factory class – Responsible for persisting entities to, and retrieving entities from a data source. The class’s methods are usually not exposed outside the assembly.
  • A handler class – Handles caching and provides a public interface to the factory.
  • A module class – Usually used for interfacing (ensuring correct start up order and listening to events) with other modules.

All of these components will be discussed in this article.

Many of the modules in the EPiServer Community framework also contain

  • Attribute data types – Describes how the entity types in the module are persisted as attribute values for other entity types.
  • Hibernate mappings – Used by the query system (see below).
  • Queries – Strongly typed query classes that provides a way to write queries in code that the system transforms into database queries.

I’ll leave those for another article.

The Example Module – Movies

We have been tasked to develop a movie site. On the site users can submit movies and the movies that a user has submitted is shown on his or hers mypage. A movie consists of a title, the author (the user that submitted the movie), and optionally the movies runtime. It should also be possible to categorize the movie as being in one or several genres and users should also be able to rate the movie.

In order to satisfy the above specification we will need a movie entity class, CRUD (Create, Read, Update, Delete) methods for the movie entity class and a method for getting movies submitted by a specific user.

Setting Up the Project

While you can build your module in your web project I’d recommend you to do it in a separate class library. That’s what I’ll do in this example. I’ll be using the namespace and assembly name ExampleProjects.Movie. To follow this example you will have to add references to EPiServer.Common.Cache.dll, EPiServer.Common.Data.dll, EPiServer.Common.Framework.dll, EPiServer.Common.Framework.Impl.dll, EPiServer.Common.Security.dll and EPiServer.Community.dll.

Creating the Module Class

Once we have our project set up we’ll begin by creating the module class, MovieModule:

using EPiServer.Community;
using EPiServer.Community.Modules;

namespace ExampleProjects.Movie
{
    public class MovieModule
    {

    }
}

The module class should implement the EPiServer.Community.Modules.IModule interface which means that we will have to implement one method, “OnApplicationStart”, and three properties, “UniqueName”, “AdministrationControl” and “Name”.

public class MovieModule : IModule

Implementing the IModule Properties

We’ll begin by implementing the properties first. UniqueName should be a truly unique name among all of the EPiServer Community modules in the system. Apart from returning names of other modules, such as “forum” we can return pretty much anything we like. It should however preferably be something that describes the module, so we’ll go with “movie”.

public string UniqueName
{
    get { return "movie"; }
}

The Name property should return the modules name as it should be displayed in the administrative interface.

public string Name
{
    get { return "Movies"; }
}

The AdministrationControl property should return the path to a control shown in the administration interface. The path should be relative to <site Root>\EPiServerCommunity\Start. As we won’t create such a control we will return an empty string.

public string AdministrationControl
{
    get { return string.Empty; }
}

Implementing OnApplicationStart and Creating a "Singleton Property"

We don’t want to create a new instance of the class each time we need to access its properties and methods. Therefore we’ll create a static property named Instance which we’ll set in the OnApplicationStart method.

public static MovieModule Instance
{
    get;
    private set;
}

 

public void OnApplicationStart(CommunityContext context)
{
    Instance = this;
}

OnApplicationStart is also perfect for adding listeners to handlers in other modules so that you can perform actions when entities in other modules are added, updated or removed. These actions usually involve clearing the cache for a method or deleting entities that depend on other entities.

Creating the Entity Classes (Movie)

With MovieModule set up we can proceed to create the Movie class.

using EPiServer.Common;
using EPiServer.Common.Data;
using EPiServer.Common.Security;

namespace ExampleProjects.Movie
{
    public class Movie
    {
    }
}

Creating the Properties

The class should have three properties to satisfy the specifications. Let’s begin with Title and RuntimeMinutes:

public string Title { get; set; }

public int? RuntimeMinutes { get; set; }

Now that wasn’t very interesting. Luckily the Author property is a bit more to talk about:

private int _authorUserID;

public IUser Author
{
    get
    {
        return EntityProviderHandler.GetEntityProvider(
            typeof (IUser)).GetEntityInstance(typeof (IUser),
            _authorUserID) as IUser;
    }
   
    set
    {
        _authorUserID = value.ID;   
    }
}

Note that we don’t save the reference to the actual user object but the integer ID. This is very important for two reasons. First of all we want lazy loading as a request to get a Movie instance would otherwise require us to also fetch the user object from the database. If that in turn didn’t implement lazy loading but referenced other entities directly as well we might end up loading quite a lot of entities that we have no interest in.

The second reason is that if we were to have a reference to the actual user object in the Movie class it would have to be serialized and put in the cache along with the Movie object when a Movie object is cached. This would mean that we would use up a lot of cache with the same object. We would also have a problem when the user object is updated or removed as we would either have to clear the cache for all Movie objects or experience inconsistencies in the website.

Also worth mentioning is how we retrieve the IUser instance. Note that we don’t use the EPiServer.Community.CommunitySystem.CurrentContext.DefaultSecurity.GetUser method but instead ask the EntityProviderHandler for which entity provider is responsible for providing entities of the IUser type. Doing so means our module will still work even if the IUser type is remapped to be provided by a different entity provider.

Creating a Public Constructor

With our properties set up we can create a public constructor. As all movies should have a title and an author it’s a good idea to communicate that to users of the class by demanding those as parameters to the public constructor(s).

public Movie(string title, IUser author)
{
    Title = title;
    _authorUserID = author.ID;
}

Inheriting from FrameworkEntityBase

As we want users to be able to rate the movie the EPiServer Community framework is perfect for us as it has a built in rating system. In order to make instances of the Movie class ratable we should therefore implement the IRatableEntity interface. But we also want to be able to cache Movie objects using the built in caching system, so we also need to implement ICachable. Oh, and then we want to be able to use the categorization system for putting the movie into genres. We’ll need to implement ICategorizableEntity for that. 

That’s quite a few interfaces to implement, and the list could easily grow longer. Luckily there is a perfect class that we can inherit from, FrameworkEntityBase. By doing so we’ll get a lot of functionality for free. Movie objects will not only be possible to rate, categorize and cache but we will also be able to tag them, log each time they are shown and have a bunch of other features without us writing a single line of code. So, let’s inherit from FrameWorkEntityBase:

public class Movie : FrameworkEntityBase
When a class inherits from FrameworkEntityBase it must have a “CacheKey” property that returns a string array and all constructors must pass the objects integer ID to the base constructor. For objects that have not yet been persisted -1 should be passed as ID as that value is used to flag that the object has not yet been saved to a datasource.

So, let’s begin by modifying the public constructor that we previously created:

public Movie(string title, IUser author)
    : base(-1)
{
    Title = title;
    _authorUserID = author.ID;
}

While we’re at it, lets also create an internal constructor that we can use when we create an instance of the class based on data from the database and know the ID:

internal Movie(int id, string title, int? runtime, int authorID)
    : base(id)
{
    Title = title;
    RuntimeMinutes = runtime;
    _authorUserID = authorID;
}

With that done we still need to create the “CacheKey” property. The “CacheKey” property is a non-static property that returns the cache key for the specific instance. We will however need to be able to build a cache key for a movie object without creating an instance later in our handler class (described later). Therefore we’ll create a static method named “ConstructCacheKey” in the Movie class and let the “CacheKey” property use that:

internal static string[] ConstructCacheKey(int id)
{
    return new [] { MovieModule.Instance.UniqueName,
        "Movie", id.ToString() };
}

public override string[] CacheKey
{
    get { return ConstructCacheKey(ID); }
}

The cache key should be an array of strings that is unique for each entity in the system. We therefore build it up as a combination of the modules unique name, the name of the entity type and finally the objects ID.


This is part one of this article. The other part is:

» Creating a Custom EPiSever Community Module - Part Two

Comments

Brian Burge
Brian Burge Dec 13, 2013 06:25 PM

Hello, I cannot find any of the files you said to reference, I did a fresh install of EpiServer locally and installed relate, but I cannot find them, can you tell me where I should be looking?
Please disregurard found them at C:\Program Files (x86)\EPiServer\CommonFramework\7.0.844.1\bin

Please login to comment.