Views: | 23252 |
Number of votes: | 6 |
Average rating: |
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.
» Creating a Custom EPiSever Community Module - Part Two
» Creating a Custom EPiSever Community Module - Part Three
» Creating a Custom EPiSever Community Module - Part Four
» Download the full source code for this article
A basic EPiServer Community module usually consists of these components:
All of these components will be discussed in this article.
Many of the modules in the EPiServer Community framework also contain
I’ll leave those for another article.
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.
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.
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
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; }
}
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.
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
{
}
}
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.
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;
}
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
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