Try our conversational search powered by Generative AI!

Views: 20005
Number of votes: 4
Average rating:

Creating a Custom EPiServer Community Module - Attribute Data Types

In a previous series of articles I described how to create a basic EPiServer Community module. In this article I will build on those articles and describe how to take the example project, Movies, one step further by allowing the Movie entity type to be used as attribute values for other EPiServer Community entities.

If you haven’t read the previous articles, I recommend you to do so as this article takes off where those articles ended.

Other articles in this series

» Creating a Custom EPiSever Community Module - Part One

» 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

The goal

The goal of this article is to be able to add instances of the Movie type as values to attributes for other entity types. In other words when we click on the Attributes link (1) in the Community tab and then on the Create Attribute button (2), we want the ExampleProjects.Movie.Movie type to be selectable in the Of type drop down list (3).
 


 

Required Components

In order for a type to be usable as a value for attributes in the platforms attribute system three components are needed:

  • A class that implements the IAttributeDataType interface (in the EPiServer.Common.Attributes.DataTypes namespace). This class describes how objects of the type (in our case the Movie type), or references to objects of the type, should be persisted as a primitive value to a data source, and vice versa. It also provides information about if and how the type can be displayed in the administrative GUI as well as with what AttributeValue class it should be mapped in the query system (see the last item in this list).
     
  • A class that implements the IAttributeDataTypeProvider (also in the EPiServer.Common.Attributes.DataTypes namespace). This class provides instances of one or several types that implement the IAttributeDataType interface.
     
  • A class that inherits from the AttributeValue<T> abstract base class (in the EPiServer.Common.Queries namespace) which describes how attributes of the type should be mapped in queries in the frameworks query system.

Luckily, creating the three components are really easy for types that inherit from the FrameworkEntityBase class, which our Movie class does, as there are base classes for all of the three required components that requires almost no implementation at all.

The MovieAttributeDataTypeProvider Class

Let’s begin by building the first component in the list above, our implementation of the IAttributeDataType interface, the MovieAttributeDataType class. To follow the standard in the community framework we’ll put this, along with out implementation of the IAttributeDataTypeProvider interface, in its own namespace named DataTypes under the modules root namespace.

The implementation of the class looks like this:

using System.Collections.Generic;
using EPiServer.Common.Attributes.DataTypes; namespace ExampleProjects.Movie.DataTypes
{
    public class MovieAttributeDataType :
        ComplexAttributeDataTypeBase<Movie>
    {
        public MovieAttributeDataType(List<object> dbValues)
            : base(dbValues,
                    typeof(Queries.MovieAttributeValue), null)
        {}
    }
}


In the above code we inherit from the ComplexAttributeDataTypeBase<Movie> class in the EPiServer.Common.Attributes.DataTypes namespace.  We also create a public constructor that passes along a list of primitive values to the base class’ constructor, along with the type of our implementation of the AttributeValue<T> base class (described later in this article) and the path to the usercontrol which will be used to display and edit the attribute in the administrative interface. As we wont create such a control in this example we send in null as the path.

The MovieAttributeDataTypeProvider Class

Our implementation of the IAttributeDataTypeProvider interface is called MovieAttributeDataTypeProvider and we place it in the same namespace as the MovieAttributeDataType class:

using System;
using System.Collections.Generic;
using EPiServer.Common.Attributes.DataTypes; namespace ExampleProjects.Movie.DataTypes
{
    public class MovieAttributeDataTypeProvider :
        IAttributeDataTypeProvider
    {
    }
}


Just like with the MovieEntityProvider we don’t want to instantiate the class each time we use it so we create a static method that returns a reference to an instance of the class.

private static MovieAttributeDataTypeProvider _instance
    = new MovieAttributeDataTypeProvider(); public static IAttributeDataTypeProvider GetProviderInstance()
{
    return _instance;
}


Note that while the GetProviderInstance method is not required by the IAttributeDataTypeProvider interface you will get runtime exceptions if you omit it.

A method that is required by the interface however is the GetDataTypeInstance method which we implement like this:

public IAttributeDataType GetDataTypeInstance(
    Type complexType, List<object> dbValues)
{
    if (complexType == typeof(Movie))
        return new MovieAttributeDataType(dbValues);     string errorMessage = string.Format(
        "Request for unsupported type {0}.", complexType);
    throw new NotSupportedException(errorMessage);
}

Just as with the GetEntityInstance method in our entity provider we validate that the requested type is supported by the provider and if it is we return a new instance of the type based on the primitive values from the data source in the dbValues parameter.

As a last step we must also create the SupportedTypes property which should return an array with the types that the provider supports, in our case only the Movie type.

public Type[] SupportedTypes
{
    get { return new [] { typeof(Movie) }; }
}

The MovieAttributeValue Class

The last step is to create a class that inherits from AttributeValue<Movie> (in EPiServer.Common.Queries) which is required for the query system to be able to work with the Movie type as attribute values. Note though that just creating this class is not enough. We also need to create Hibernate mappings, but I’ll leave that for another article.

Just creating the class will enable us to compile the module and will suffice for all aspects except when writing queries.

To follow the standard in the framework we place the MovieAttribute class in a namespace named Queries below the modules root namespace.

using EPiServer.Common.Queries; namespace ExampleProjects.Movie.Queries
{
    public class MovieAttributeValue : AttributeValue<Movie>
    {
    }
}

All done!

Usage Example

An example of how to use the Movie type as attribute values is to enable users to save a list of their favorite movies. Here’s an example of how that could work, given that there is a attribute named “FavoriteMovies” of the type Movie for the IUser type (see the image in The Goal section for an illustration on how to create such an attribute):


//Create and add a new movie
Movie theGodfatherMovie = new Movie(
    "The Godfather", CurrentUser);
theGodfatherMovie.RuntimeMinutes = 100;
theGodfatherMovie = MovieHandler.AddMovie(
    theGodfatherMovie); //Create and add another new movie,
//this time with runtime
Movie gardenStateMovie = new Movie(
    "Garden State", CurrentUser);
gardenStateMovie.RuntimeMinutes = 102;
gardenStateMovie = MovieHandler.AddMovie(
    gardenStateMovie); //Add the two newly created movies as favorites for the
//current user.
IUser currentUser = (IUser)CurrentUser.Clone();
currentUser.SetAttributeValue<Movie>("FavoriteMovies",
    new List<Movie>{ theGodfatherMovie, gardenStateMovie });
Community.CommunitySystem.CurrentContext.DefaultSecurity.UpdateUser(
    currentUser); //Retrieve the current users favorite movies,
//loop through them and print their titles
foreach (Movie favorite in CurrentUser.GetAttributeValues<Movie>(
    "FavoriteMovies"))
{
    Response.Write(favorite.Title + "<br />");

Comments

Please login to comment.