More on Repository Pattern with Entity Framework Code First

I got a comment on my first post on this pattern that pointed out unnecessary use of reflection as well as a way to make my sample cleaner.  I took an opportunity to go ahead and update the sample.  I added a bit of new functionality to illustrate how you can also pass in where clause and order by clause usage.  I added a couple of functions to my base repository class to illustrate how to do this.  Here is the function that returns filtered and sorted data:

    /// <summary>

    /// Select data from database using a where clause

    /// </summary>

    /// <typeparam name="TItem">Type of data to select</typeparam>

    /// <param name="whereClause">Where clause / function</param>

    /// <param name="orderBy">Order by clause</param>

    /// <returns></returns>

    public IOrderedQueryable<TItem> Select<TItem>(

      Expression<Func<TItem, bool>> whereClause,

      Expression<Func<TItem, object>> orderBy)

       where TItem : class, new()

    {

      IOrderedQueryable<TItem> data = context.Set<TItem>().Where(whereClause).OrderBy(orderBy);

      return data;

    }

 

 

Here is how I would call this function:

        var activeOrderedProducts =

          repository.Select<Product>(one => one.IsActive, one => one.ProducName).ToList();

 

Pretty simple so far.  Now I am going to take it a step further and extract an interface from my repository.  Ideally, I would create an interface first, but this is an experiment.  Interface would look as follows:

using System;

using System.Linq.Expressions;

using System.Linq;

using System.Data.Entity;

using System.Data.Entity.Infrastructure;

namespace RepositoryEFCodeFirst

{

  public interface IEFRepository<TContext> : IDisposable

     where TContext : DbContext, IObjectContextAdapter, new()

  {

 

    void Delete<TItem>(TItem item) where TItem : class, new();

   

    TItem Insert<TItem>(TItem item) where TItem : class, new();

   

    IQueryable<TItem> Select<TItem>() where TItem : class, new();

   

    IQueryable<TItem> Select<TItem>(Expression<Func<TItem, bool>> whereClause) where TItem : class, new();

   

    IOrderedQueryable<TItem> Select<TItem>(Expression<Func<TItem, bool>> whereClause, Expression<Func<TItem, object>> orderBy)

      where TItem : class, new();

   

    TItem Update<TItem>(TItem item) where TItem : class, new();

  }

}

 

 

The last step would be to configure dependency injection and use an interface to resolve repository.  Ordinarily, you would configure unity container in global.asax file for example for WCF service or a web application.  Code would look as following:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using Microsoft.Practices.Unity;

 

namespace RepositoryEFCodeFirst

{

  class Program

  {

    static UnityContainer container = new UnityContainer();

 

    static void Main(string[] args)

    {

 

      container.RegisterType<IEFRepository<ProductContext>, EFRepository<ProductContext>>(

        new ContainerControlledLifetimeManager());

 

Now my code is even cleaner – I use interfaces everywhere and my “business code” does not deal with data access at all, considering that I would register my types elsewhere, not in my business layer.

Here is the final program code that uses repository:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using Microsoft.Practices.Unity;

 

namespace RepositoryEFCodeFirst

{

  class Program

  {

    static UnityContainer container = new UnityContainer();

 

    static void Main(string[] args)

    {

 

      container.RegisterType<IEFRepository<ProductContext>, EFRepository<ProductContext>>(

        new ContainerControlledLifetimeManager());

 

      using (var repository = container.Resolve<IEFRepository<ProductContext>>())

      {

        Console.WriteLine("Total products in DB : " +

            repository.Select<Product>().Count().ToString());

 

        Product newProduct = new Product();

        newProduct.ProducName = "Plates";

        newProduct.ProducNumber = "001";

        newProduct.Notes = "SOme notes for plates";

        newProduct.IsActive = true;

 

        newProduct = repository.Insert(newProduct);

 

        var ordered = repository.Select<Product>()

          .Where(one => one.IsActive == true)

          .OrderBy(one => one.ProducNumber).ToList();

 

        Console.WriteLine("New id is " + newProduct.ProductId.ToString());

 

 

        var aProduct = repository.Select<Product>().Where(one => one.IsActive).First();

 

        var activeProducts = repository.Select<Product>(one => one.IsActive).ToList();

 

        var activeOrderedProducts =

          repository.Select<Product>(one => one.IsActive, one => one.ProducName).ToList();

 

        aProduct.ProducName = "Updated Plates";

 

        aProduct = repository.Update(aProduct);

 

        aProduct = repository.Select<Product>()

          .Where(one => one.ProductId == newProduct.ProductId).First();

 

        Console.WriteLine("Update name is: " + aProduct.ProducName.ToString());

 

        repository.Delete(aProduct);

 

      }

    }

  }

}

 

I am also pasting in entire repository class (updated):

using System;

using System.Configuration;

using System.Data.Entity;

using System.Data.Entity.Infrastructure;

using System.Linq;

using System.Linq.Expressions;

using Microsoft.Practices.Unity;

 

namespace RepositoryEFCodeFirst

{

  /// <summary>

  /// Repository base class used with DbContext

  /// </summary>

  /// <typeparam name="TContext">Type of DdContext that this repository operates on</typeparam>

  public class EFRepository<TContext> : IDisposable, IEFRepository<TContext>

      where TContext : DbContext, IObjectContextAdapter, new()

  {

    private TContext context;

 

    /// <summary>

    /// Create new instance of repository

    /// </summary>

    /// <param name="connectionStringName">Connection string name from .config file</param>

    public EFRepository(string connectionStringName)

    {

      context = new TContext();

      context.Database.Connection.ConnectionString =

          ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString;

    }

 

    [InjectionConstructor]

    public EFRepository()

    {

      context = new TContext();

      context.Database.Connection.ConnectionString =

          ConfigurationManager.ConnectionStrings["ProductConnection"].ConnectionString;

    }

 

    /// <summary>

    /// Dispose repository

    /// </summary>

    public void Dispose()

    {

      if (context != null)

      {

        context.Dispose();

        context = null;

      }

    }

 

 

    /// <summary>

    /// Select data from database

    /// </summary>

    /// <typeparam name="TItem">Type of data to select</typeparam>

    /// <returns></returns>

    public IQueryable<TItem> Select<TItem>()

       where TItem : class, new()

    {

      DbSet<TItem> set = context.Set<TItem>();

      return set;

    }

 

    /// <summary>

    /// Select data from database using a where clause

    /// </summary>

    /// <typeparam name="TItem">Type of data to select</typeparam>

    /// <param name="whereClause">Where clause / function</param>

    /// <returns></returns>

    public IQueryable<TItem> Select<TItem>(Expression<Func<TItem, bool>> whereClause)

       where TItem : class, new()

    {

      IQueryable<TItem> data = context.Set<TItem>().Where(whereClause);

      return data;

    }

 

    /// <summary>

    /// Select data from database using a where clause

    /// </summary>

    /// <typeparam name="TItem">Type of data to select</typeparam>

    /// <param name="whereClause">Where clause / function</param>

    /// <param name="orderBy">Order by clause</param>

    /// <returns></returns>

    public IOrderedQueryable<TItem> Select<TItem>(

      Expression<Func<TItem, bool>> whereClause,

      Expression<Func<TItem, object>> orderBy)

       where TItem : class, new()

    {

      IOrderedQueryable<TItem> data = context.Set<TItem>().Where(whereClause).OrderBy(orderBy);

      return data;

    }

 

    /// <summary>

    /// Insert new item into database

    /// </summary>

    /// <typeparam name="TItem">Type of item to insert</typeparam>

    /// <param name="item">Item to insert</param>

    /// <returns>Inserted item</returns>

    public TItem Insert<TItem>(TItem item)

        where TItem : class, new()

    {

      DbSet<TItem> set = context.Set<TItem>();

      set.Add(item);

      context.SaveChanges();

      return item;

    }

 

    /// <summary>

    /// Update an item

    /// </summary>

    /// <typeparam name="TItem">Type of item to update</typeparam>

    /// <param name="item">Item to update</param>

    /// <returns>Updated item</returns>

    public TItem Update<TItem>(TItem item)

        where TItem : class, new()

    {

      DbSet<TItem> set = context.Set<TItem>();

      set.Attach(item);

      context.Entry(item).State = System.Data.EntityState.Modified;

      context.SaveChanges();

      return item;

    }

 

    /// <summary>

    /// Delete an item

    /// </summary>

    /// <typeparam name="TItem">Type of item to delete</typeparam>

    /// <param name="item">Item to delete</param>

    public void Delete<TItem>(TItem item)

       where TItem : class, new()

    {

      DbSet<TItem> set = context.Set<TItem>();

      var entry = context.Entry(item);

      if (entry != null)

      {

        entry.State = System.Data.EntityState.Deleted;

      }

      else

      {

        set.Attach(item);

      }

      context.Entry(item).State = System.Data.EntityState.Deleted;

      context.SaveChanges();

    }

 

  }

}

 

You can download entire solution here.

9 Comments

  1. Hey Sergey,

    Your new post raises a couple interesting questions that I am currently dealing with:

    1. You have three Select methods but you can accomplish the same task using the one with no params. I have seen this type of overloading create confusion, but at the same time I understand the motivation. Where is the balance between making the features of LINQ available and at the same time providing a nice API for other devs?

    2. You use Select but do not offer up the Expression<Func> selector param. At first I had a similar implementation however it looked awkward when I tried to do a projection.

    var activeProductNumbers = repository.Select().Select(p=> p.ProductNumber).Where(p => p.IsActive);

    When I began running into situations like this, I started to question if I was implementing the repository pattern “correctly”. Pre-LINQ, according to Martin Fowler, a repository is supposed to be a “collection-like interface for accessing domain objects”. So does that mean we only need to implement IEnumerable or IQueryable (more on this choice later)? You can see from my IRepository interface below what my current (as of last night) decision is on this.

    3. Should the repository methods return IQueryable, IList, etc…

    On one hand, returning IQueryable allows for maximum flexibility. This flexibility however puts more responsibility on the consuming dev to understand when and how many times the query will execute.

    var activeProducts = repository.Select().Where(p => p.IsActive);

    Then lets say they are going through a collection of recent orders to make sure that only active products were selected (a bit of a contrived example).

    // how many DB hits will there be for 10 orders?
    foreach(var order in orders)
    {
    bool validOrder = activeProducts.Any(p=> order.ProductNumber == p.ProductNumber);
    }

    I realize you know the answer to this question because of your use of ToList().Others however, myself included at times, may not always get this right.

    Please note that the current project I’m working on while developing the following repository implementation has been rather simple, which is probably why I have been able to get away with only offering a few selection methods (All, FindAll, Single, etc).

    public interface IRepository : IDisposable where TEntity : IPersistentEntity, class
    {
    TEntity Add(TEntity entity);
    void Remove(TEntity entity);
    int SaveAll();
    TEntity FindById(int id);
    IList FindAll(Func predicate);
    TEntity SingleOrDefault(Func predicate);
    TEntity Single(Func predicate);
    TEntity Create();
    bool IsValid(TEntity entity);
    bool IsValid();
    int? GetId(Func predicate);
    IList All();
    }

    Thanks

  2. Hey, Barry,
    On 1: I put extra methods to demonstrate a patterns and ability to pass data to IQueryable. I would go one way or the other – either only have one Select, or a number in base class, with ability to add more to specific repository. Latter is probably cleaner, but will require more code.
    On #2 that is awkward, so they answer is likely be in picking one of the patterns above. Both offer a solution.
    On 3: cleanest answer is IEnumerable, but IQueryable cuts down on code significantly. Hard to say what is better, but if one were to assume that IQueryable would be good for 5-10 years (and I think this is the case), I would go with IQueryable today.

  3. Considerring the implement for “Insert”, “Update”, “Delete”:
    When I want to add many entity to the Dbset, the “Insert” function will have much access to the database. So, Should remove the “SaveChanges” in the implement?

  4. I would suggest adding a parameter to those methods called bool save = true (or false). This way you can use the same method and optionally tell it to save or not immediately.
    Something like this:

    public void Update(T item, bool save = true)
    where T : class, new()
    {
    var set = db.Set
    ();
    set.Attach(item);
    db.Entry(item).State = System.Data.EntityState.Modified;
    if (save)
    {
    db.SaveChanges();
    }
    }

  5. So how can we use a connection string with this? I’ve tried this:

    using (var repository = container.Resolve<IEFRepository>(connectionString))

    but I get a “Resolution Failed Exception”:

    Microsoft.Practices.Unity.ResolutionFailedException was unhandled
    Message=Resolution of the dependency failed, type = …
    Exception occurred while: while resolving.
    Exception is: InvalidOperationException – The current type, …, is an interface and cannot be constructed. Are you missing a type mapping?

    Any ideas?

  6. Thanks for the quick response, Sergey.

    However, I am calling the .RegisterType method prior to the .Resolve call exactly like your sample code above:

    container.RegisterType<IEFRepository, EFRepository>(
    new ContainerControlledLifetimeManager());

    Does you sample code work with a connection string passed to the Resolve call?

  7. Nope, I do not. Unity will call the constructor with the most parameters, so you need to mark one as injection constructor (parameterless). You probably should not pass conn. string there anyway, as the context should know itself what it needs to connect to. Just my 2 cents.

    [InjectionConstructor]
    public EFRepository()
    {
    context = new TContext();
    context.Database.Connection.ConnectionString =
    ConfigurationManager.ConnectionStrings[“ProductConnection”].ConnectionString;
    }

Leave a Reply

Your email address will not be published. Required fields are marked *