Hooking InComing from working with Rails, there are things that I miss when using .NET (as exhibited by my last post). Today I’m going to tell you about a way to bring a bit of Active Record to Entity Framework.

When using Active Record, you can hook into the process at various points to perform actions. Let’s say you want to do something before a model is created, like set a date for audit purposes.

ASIDE: Audit columns of created_at and updated_at are automatically handled by Active Record (by default). I wish this sort of functionality existed in Entity Framework, but we’ll be able to do just that if you keep reading!

Here’s a simple Rails example of hooking into the before_create callback (This example is from the Rails docs)

class Subscription < ActiveRecord::Base
  before_create :record_signup

  private
    def record_signup
      self.signed_up_on = Date.today
    end
end

Now, if there was only a way to do the same thing with Entity Framework… ;)

NOTE: You can, of course, implement your own “hooks” in Entity Framework by overriding the SaveChanges method, but that gets messy.

Introducing EFHooks

In my quest to find something similar to Active Record’s callbacks, I stumbled upon the EFHooks project. This project is simply awesome! Instead of messing with overriding SaveChanges, I’m able to inject various bits of code into the save process (e.g. Pre/Post, Insert/Update/Delete hooks). Let’s take that Active Record example from above and see how we’d accomplish the same thing using Entity Framework.

Easy Hooking

Let’s start with a PreInsertHook. Creating a hook is just a simple class, which inherits from the EFHook class (PreInsertHook in our case), then just override the Hook method.

public class TimestampPreInsertHook : PreInsertHook<ITimeStamped>
{
    public override void Hook(ITimeStamped entity, HookEntityMetadata metadata)
    {
        entity.CreatedAt = DateTime.Now;
    }
}

The EFHook classes are generic, so they will take in any object type. You can use an interface (like ITimeStamped in the example above) or a specific class (e.g. Post or User).

What we’ve said here is: “Anytime there is an insert of an object that implements ITimeStamped, set the CreatedAt property to the current data/time.”

All we need to do is simply call SaveChanges() on our context.

That’s the hook, but we need to tweak our DbContext class to tell it to use the hook. Simply derive your DbContext from the EFHooks.HookedDbContext and register the hooks.

public class AppContext : HookedDbContext
{
    public AppContext() : base()
    {
        this.RegisterHook(new TimestampPreInsertHook());
    }

    public DbSet<User> Users { get; set; }
    public DbSet<Post> Posts { get; set; }
}

Once your context inherits from HookedDbContext, your SaveChanges calls are enhanced to call any hooks that you have registered.

EFHooks vs. VisoftInc.EFHooks

What are you waiting for? Head over to NuGet and grab the EFHooks package… Whoops, wait! There are two different packages. EFHooks and VisoftInc.EFHooks. What’s going on here?

The EFHooks project was started by Kevin McKelvin. Back in 2011, I submitted a pull request, but sadly, a new NuGet package was never published. As of today, the latest official version of EFHooks was published on 2011-09-18, and is v1.0.2.

I wanted to use EFHooks in multiple projects, and that is what spurred me to create a new NuGet package, VisoftInc.EFHooks.

Since that first change where I added the ability to use the existing context within a hook, I’ve also changed EFHooks to support stub entities (e.g to build relationships without loading objects in an MVC app). Just recently, I added .NET 4/4.5 support and changed the Entity Framework base version to 5.0.0. All of these changes have been published to the VisoftInc.EFHooks package. I’ve made these changes while using EFHooks in multiple projects. These changes have made a big difference, especially working within a .NET 4.5 project where the version of Entity Framework 5 differs between .NET 4 and 4.5.

I would be happy to push all my changes to the main EFHooks branch if Kevin will still actively maintain the project and publish changes to NuGet, but alas, for now, I use (and would recommend) the VisoftInc.EFHooks package. If you find any issues with VisoftInc.EFHooks, let me know on GitHub, or better yet, send me a pull request!