суббота, 1 сентября 2012 г.

Entity Framework Repository Pattern


This is my first attempt at an Entity Framework (EF) based Repository Pattern.  I took a stab at this while working on a new project, and I am really liking the ease of use this is providing me.  So I thought I would share…
Before I get started, I wanted to make a special call out to Dane Morgridge’s EF Repository on CodePlex which is based on T4 templates.  His project originally got my gears turning as to how I would make my own repository pattern for EF.  Also I wanted to pre-text this blog post with the actual code which I have posted up on GitHub Gist:

First Things First

The first thing we need to do is abstract the ObjectContext so that we can unit test against our repositories, but with out saying more on the subject here is the interface we need:
1
2
3
4
5
6
7
8
9
public interface IObjectContext : IDisposable
{
    ObjectContextOptions ContextOptions { get; }
 
    TEntity CreateObject() where TEntity : class;
    IObjectSet CreateObjectSet() where TEntity : class;
 
    int SaveChanges();
}
This provides us with a nice defined interface for already implemented properties and methods on the ObjectContext.  And you extend your ObjectContext with the stub interface by doing the following:
1
2
3
4
5
6
7
public partial class YourContainer : IObjectContext
{
    IObjectSet IObjectContext.CreateObjectSet()
    {
        return CreateObjectSet();
    }
}
The YourContainer class is the one generated by EF.  Next thing we need is the Repository interface that is used for extending your entities.  You will probably recognize this because it is pretty standard CRUD interfaces that you will find on most repository patterns:
1
2
3
4
5
6
7
8
9
10
11
12
13
public interface IRepository
{
    IQueryable All();
    IQueryable Where(Expressionbool>> expression);
 
    TEntity Create();
 
    void Add(TEntity entity);
    void Delete(TEntity entity);
 
    void Attach(TEntity entity);
    void Detach(TEntity entity);
}
You can see the base implementation of this interface on Gist.  Next and the part I love the most about my implementation is session wrapper object that works much like the System.Transaction objects in .NET and standard ObjectContext container examples that you will see on the web:
1
2
3
4
5
public interface IRepositorySession : IDisposable
{
    IObjectContext Container { get; }
    void Commit();
}
By adding the IDisposable interface to the session interface, I can wrap my repository calls in a using { … } block.  Which I thinks makes for a nice programing experience, and easier to read code where you don’t constantly have to set the connection you want the repository to run off of.  This pattern is based on my work from FluentCassandra.  You can view the implementation of this interface on Gist.

Using The Repository

The next part of this is to actually implement the repository for your entity.  The typical usage of this would be something like the following:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class BlogRepository : Repository
{
    public BlogRepository ()
        : base(RepositorySession.Current) { }
 
    public BlogRepository (IObjectContext container)
        : base(new RepositorySession(container)) { }
 
    public BlogRepository (IRepositorySession session)
        : base(session) { }
 
    public Blog Get(int id)
    {
        return Where(x => x.Id == id).SingleOrDefault();
    }
 
    // ... put your other methods ...
}
The constructor methods are probably what sticks out the most, the first one is the most important because it automatically instantiates it self based on the current repository session.  This automatic usage of the current sessions probably brings up more questions than answers, so hopefully this example of use answers most of those questions for you.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public ActionResult Update(int id)
{
    using (var db = new RepositorySession())
    {
        var repo = new BlogRepository();
        var blog = repo.Get(id);
 
        TryUpdateModel(blog, new[] {
            "Title", "Content", "Category", "Tags"
        });
 
        if (!ModelState.IsValid)
            return View(ModelState.GetErrors());   
         
        db.Commit();
 
        return View(blog);
    }
}
That looks pretty elegant and easy to understand, everything wrapped inRepositorySession using block automatically uses the container specified in the session.  And gets saved to the database when db.Commit() is called.
So now that we have gone through showing how you would use this repository pattern with your entities.  The last part to setting this up is providing your entity container type to the session class, so that it knows the entity type to instantiate when you create your session like above.
To use this pattern in your web project you would add the following registration code to your Application_Start in your Global.asax file. 
1
RepositorySession.DefaultContainerType = typeof(YourContainer);
This has dual use not only does it set the ObjectContext container for use in your production server, but with out needing to use IoC in your project (because sometimes it just isn’t necessary… gasp!), you can mock the IObjectContext in your tests so that you don’t need a database present to test. 

Conclusion

I just wanted to provide something useful, because me and my wife are expecting a baby girl in the next couple of days, and I don’t know how much time I will have to post to this blog before the new year.  Happy Holidays and wish me luck.

Комментариев нет: