Generic Entity Base Class

This is part of a series on using generics in C# to make code more resuable. Other articles in this series:

Generics in C# are hugely powerful, allowing amazing levels of code reuse. In future articles, I'll get into some of the powerful things you can achieve with things like generic repositories or even entire controllers. However, to achieve any of that requires that all your entities are on a level playing field. Let's get right to the code, since that's the fun part, anyways.

Code

Interfaces

public interface IModifiableEntity
{
    string Name { get; set; }    
}

public interface IEntity : IModifiableEntity
{
    object Id { get; set; }
    DateTime CreatedDate { get; set; }
    DateTime? ModifiedDate { get; set; }
    string CreatedBy { get; set; }
    string ModifiedBy { get; set; }
    byte[] Version { get; set; }
}

public interface IEntity<T> : IEntity
{
    new T Id { get; set; }
}

IModifiableEntity is a template for creating view models. I long despised the need to duplicate entity properties in view models that represent them, and especially the maintenance nightmare that usually entails: having to remember to mirror changes to the entity to its view models. This interace is a concession. If I have a view model for an entity, I can make it implement IModifiableEntity and then at the very least, I will get compile time errors if the view model starts to diverge from the entity. Here, there's just one property, Name, which admittedly is not ground-breaking. However, this can be applied more broadly, as you'll see later in this article. The other five properties on IEntity, are nothing that should be modified via a view model, which is why they are not included.

IEntity is fairly unremarkable, but suffice to say that, for my purposes, it holds standard properties I like to have on any database-peristed object: creation and modification dates, and who created or last modified it. Version is for dealing with concurrency in Web Api scenarios. It's worth noting that Id is typed as object. That will be important later.

IEntity<T> holds a single property, Id, which is generically typed. This obviously is the primary key property and by making it generic, we can utilize anything here, such as int, long or Guid, depending on the needs of the entity.

Implementation

public abstract class Entity<T> : IEntity<T>
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public T Id { get; set; }
    object IEntity.Id
    {
        get { return this.Id; }
    }

    public string Name { get; set; }

    private DateTime? createdDate;
    [DataType(DataType.DateTime)]
    public DateTime CreatedDate
    {
        get { return createdDate ?? DateTime.UtcNow; }
        set { createdDate = value; }
    }

    [DataType(DataType.DateTime)]
    public DateTime? ModifiedDate { get; set; }

    public string CreatedBy { get; set; }

    public string ModifiedBy { get; set; }

    [Timestamp]
    public byte[] Version { get; set; }
}

This is our implementation of IEntity<T> (along with IEntity and IModifiableEntity, via inheritance). It's abstract because we don't actually want a table for this, itself. It's worth looking closely at the implemtation of Id. Via data annotations, we define it as the key for any table that inherits from this class and use DatabaseGenerated to inform Entity Framework that it should also be the "identity". In the case of types such as int and long, this will result in an autoincrementing primary key. For something like Guid, it will use uniqueidentifier to apply a unique GUID on insert. IEntity<T>.Id is implemented implicitly, but then we explicitly implement IEntity.Id. This may seem a little odd, but it's not without merit. With this in place, we can use IEntity in places where we don't necessarily care about the type of Id, but still need to access the property.

The rest of the implementation is fairly straight-forward. CreatedDate is implemented with custom getter and setter so that if no value is set, it will default to the current date and time in UTC. This is so we don't have to worry about expressly setting a CreatedDate.

Usage

All entities in your application, then, will now inherit from Entity<T>:

public class Foo : Entity<int>
{
    // Other properties specific to `Foo`
}

In further articles in this series, we'll see how the IEntity<T> and IEntity interfaces endear themselves.

Bonus

There's two other sets of interfaces/abstract classes I utilize. I'll provide these as an example, but you can freely throw them away, modify them to your purposes, or create something entirely different.

Interfaces

public enum PublishStatus
{
    [Display(Name = "Draft")]
    Draft,
    [Display(Name = "Pending Review")]
    PendingReview,
    [Display(Name = "Published")]
    Published
}

public interface IModifiablePublicationEntity : IModifiableEntity
{
    PublishStatus Status { get; set; }
    DateTime? PublishDate { get; set; }
    DateTime? ExpireDate { get; set; }
}

public interface IPublicationEntity : IModifiablePublicationEntity, IEntity
{
}

public interface IPublicationEntity<T> : IPublicationEntity, IEntity<T>
{
}

public interface IModifiablePageEntity : IModifiablePublicationEntity
{
    string Title { get; set; }
    string Slug { get; set; }
    string Description { get; set; }
    string Abstract { get; set; }
    string Content { get; set; }
}

public interface IPageEntity : IModifiablePageEntity, IPublicationEntity
{
}

public interface IPageEntity<T> : IPageEntity, IPublicationEntity<T>
{
}

Here we have "publication" and "page" interfaces. You'll notice they follow the same pattern as the entity interfaces above. Namely, there's "modifiable" versions of each, for the purposes of view models. Again, these are properties that should be allowed to be changed via a view model, which in the two cases here, just happens to be every property. Still, by separating it out this way, we can create an interface inheritance chain that can be used for the divergent purposes of an entity class versus a view model, without unduly restricting either.

Implementations

public abstract class PublicationEntity<T> : Entity<T>, IPublicationEntity<T>
{
    private PublishStatus? status;
    public PublishStatus Status
    {
        get { return status ?? PublishStatus.Draft; }
        set { status = value; }
    }

    private DateTime? publishDate;
    [DataType(DataType.DateTime)]
    public DateTime? PublishDate
    {
        get
        {
            if (!publishDate.HasValue && Status == PublishStatus.Published)
            {
                publishDate = DateTime.UtcNow;
            }
            return publishDate;
        }
        set { publishDate = value; }
    }

    [DataType(DataType.DateTime)]
    public DateTime? ExpireDate { get; set; }
}

public abstract class PageEntity<T> : PublicationEntity<T>, IPageEntity<T>
{
    public string Title { get; set; }

    public string Subtitle { get; set; }

    [Index]
    [StringLength(80)]
    [Required]
    public string Slug { get; set; }

    [DataType(DataType.MultilineText)]
    [StringLength(155)]
    public string Description { get; set; }

    [DataType(DataType.Html)]
    public string Abstract { get; set; }

    [DataType(DataType.Html)]
    public string Content { get; set; }
}

Fairly straight-forward implementations, each. PublicationEntity<T> uses custom getter and setter on Status to default to PublishStatus.Draft and on PublicationDate to set the default the date and time to now, in UTC, but only if the status is set to PublishStatus.Published. Otherwise, it remains null, unless explicitly set.

Notice that PublicationEntity<T> inherits from Entity<T>, and likewise, PageEntity<T> inherits from PublicationEntity<T>. This is important, as C# supports only single-inheritance. As I stated previously, all your entities need to inherit from Entity<T>, so therefore, any further base classes you want to use, must also inherit from Entity<T>. Since a "page" is a thing which is very likely to be "published", it benefits from both abstract classes it in its inheritance hierarchy.

Conclusion

The power of creating these interfaces and abstract classes cannot be overstated. Not only do they allow you to minimize a great bit of otherwise repeated code at the start, but they also serve to classify your entities into meaningful groupings that can help you further reduce code down the line. For example, if I have some bit of code that acts on a "publication" entity, by utilizing the interface, instead of the actual entity class, I can now apply that code broadly to any other entity that also has "publication" properties.

In the next article, we'll look at how to use some of this magic to our benefit by creating a truly generic repository.

Addendum: ASP.NET Identity

ASP.NET Identity requires that your "user" class (ApplicationUser by default) inherit from IdentityUser. As discussed above, C# only supports single inheritance. Does that mean, then, that our generic base class can't be used with Identity? Well, yes and no. No, you cannot inherit from both IdentityUser and Entity<T>, but via the interface, you can still utilize your "user" entity like any other entity inheriting from Entity<T>.

public class ApplicationUser : IdentityUser, IEntity<string>
{
    // Other properties

    object IEntity.Id
    {
        get { return this.Id; }
    }

    public string Name { get; set; }

    private DateTime? createdDate;
    [DataType(DataType.DateTime)]
    public DateTime CreatedDate
    {
        get { return createdDate ?? DateTime.UtcNow; }
        set { createdDate = value; }
    }

    [DataType(DataType.DateTime)]
    public DateTime? ModifiedDate { get; set; }

    public string CreatedBy { get; set; }

    public string ModifiedBy { get; set; }

    [Timestamp]
    public byte[] Version { get; set; }
}

Basically, we just add in the implementation from Entity<T> directly into the ApplicationUser class to satisfy the IEntity<T> interface. By default, Identity types the Id on IdentityUser as string, so likewise, we use IEntity<string>. If you use the generic version of IdentityUser to customize the primary key type, then you just need to use that same type in place of T in IEntity<T>. We don't implicitly implement IEntity<T>.Id here, because that's covered by IdentityUser, but we still must explicitly implement IEntity.Id.

This isn't ideal, admittedly, since you've now got some duplicated code, but unfortunately, there's no way around that. In this way, though, as long as you always use the interface rather than the abstract base class, you can now pop in ApplicationUser any where you could with the rest of your entities that actually inherit from Entity<T>.

comments powered by Disqus