Repository pattern is a popular architecture pattern that is used to create data access code / layer for an application. Basic principal behind the pattern is that business object in your application never talks to database directly, but instead talks to repository that takes POCO classes and also returns POCO classes as results of a query. This way your business layer does not take on a dependency on a specific database or data access layer implementation. As we see from experience, data access technology does not see to be handing around for very long.
As I was working on entity framework code first, I decided to take a few minutes to develop a number of convenience classes that would make it easier to repositories that internally use entity framework code first. Typically, you would create a repository for a set of classes, and this repository will be responsible for querying some data as well as CUD operations. For example, if you had a product table, you might have repository with methods such as CreateProduct(Product product), UpdateProduct(Product product), DeleteProduct(Product product), GetProductsOrderedByName(int pageNumber, int rowsPerPage), GetProductsOrderedByNumber(int pageNumber, int rowsPerPage), etc… As you develop more of your application, you might end up with a whole number of repositories. A lot of them might have similar or the same methods.
I would like to simplify this approach by creating a generic repository that works with DbContext. So, I am going to create a generic repository for context type.
/// <summary>
/// Repository base class used with DbContext
/// </summary>
/// <typeparam name="TContext">Type of DdContext that this repositiory operates on</typeparam>
public class EFRepository<TContext> : IDisposable
where TContext : DbContext, IObjectContextAdapter, new()
{
private TContext context;
private EFRepository()
{
}
/// <summary>
/// Create new instance of repository
/// </summary>
/// <param name="connecstionStringName">Connection string name from .config file</param>
public EFRepository(string connecstionStringName)
{
context = new TContext();
context.Database.Connection.ConnectionString =
ConfigurationManager.ConnectionStrings[connecstionStringName].ConnectionString;
}
/// <summary>
/// Dipose repository
/// </summary>
public void Dispose()
{
if (context != null)
{
context.Dispose();
context = null;
}
}
As you can see, I am using DbContext as a parameter. I also force to user to supply connection string. In constructor I am creating new instance of the context. As a good little developer, I am using IDisposable interface to dispose of the context. So far pretty simple. Next, I would like to write generic query mechanism via Select method.
/// <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()
{
PropertyInfo property = GetDbSet(typeof(TItem));
DbSet<TItem> set = property.GetValue(context, null) as DbSet<TItem>;
return set;
}
private PropertyInfo GetDbSet(Type itemType)
{
var properties = typeof(TContext).GetProperties().
Where(item => item.PropertyType.Equals(typeof(DbSet<>).MakeGenericType(itemType)));
return properties.First();
}
My select method returns IQueryable. This way the user of the repository can supply strongly types where and order by clauses as well as Top() and Skip() methods for paging. Here is an example:
using (EFRepository<ProductContext> repository = new EFRepository<ProductContext>("ProductConnection"))
{
var ordered = repository.Select<Product>().Where(one => one.IsActive == true).OrderBy(one => one.ProducNumber).ToList();
This way I can avoid a whole slew of Select methods and the only dependency I take on is IQueryable, which is a generic interface.in System.Linq namespace located in System.Core. This to me is a small price to pay for code reduction in repository.
Next, insert code. This is super simple of course.
/// <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 = GetDbSet(typeof(TItem)).GetValue(context, null) as DbSet<TItem>;
set.Add(item);
context.SaveChanges();
return item;
}
Again, I am using a bit of reflection to find the property DbSet property in the context. Once I find the set, I add new item to it, fire Save and return new item. I am using Identity field, and conveniently enough returned item will have the value populated.
Here is an update method:
/// <summary>
/// Update na 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 = GetDbSet(typeof(TItem)).GetValue(context, null) as DbSet<TItem>;
set.Attach(item);
context.Entry(item).State = System.Data.EntityState.Modified;
context.SaveChanges();
return item;
}
Update code is only slightly more complicated. I am simply setting the state to modified after I am attaching the item in order to force the update. Delete statement is about the same:
/// <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 = GetDbSet(typeof(TItem)).GetValue(context, null) as DbSet<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();
}
The only difference is that I am testing for entity not being in the context, which is really should not be necessary ordinarily, but I ran into an issue like this in my test project, so I added this code to it.
That is all there is to it. Here is the full class code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;
using System.Linq.Expressions;
using System.Reflection;
using System.Configuration;
using System.Data.Entity.Infrastructure;
using System.Data.Objects;
namespace RepositoryEFCodeFirst
{
/// <summary>
/// Repository base class used with DbContext
/// </summary>
/// <typeparam name="TContext">Type of DdContext that this repositiory operates on</typeparam>
public class EFRepository<TContext> : IDisposable
where TContext : DbContext, IObjectContextAdapter, new()
{
private TContext context;
private EFRepository()
{
}
/// <summary>
/// Create new instance of repository
/// </summary>
/// <param name="connecstionStringName">Connection string name from .config file</param>
public EFRepository(string connecstionStringName)
{
context = new TContext();
context.Database.Connection.ConnectionString =
ConfigurationManager.ConnectionStrings[connecstionStringName].ConnectionString;
}
/// <summary>
/// Dipose 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()
{
PropertyInfo property = GetDbSet(typeof(TItem));
DbSet<TItem> set = property.GetValue(context, null) as DbSet<TItem>;
return set;
}
/// <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 = GetDbSet(typeof(TItem)).GetValue(context, null) as DbSet<TItem>;
set.Add(item);
context.SaveChanges();
return item;
}
/// <summary>
/// Update na 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 = GetDbSet(typeof(TItem)).GetValue(context, null) as DbSet<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 = GetDbSet(typeof(TItem)).GetValue(context, null) as DbSet<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();
}
private PropertyInfo GetDbSet(Type itemType)
{
var properties = typeof(TContext).GetProperties().Where(item => item.PropertyType.Equals(typeof(DbSet<>).MakeGenericType(itemType)));
return properties.First();
}
}
}
Here is sample code that uses this repository:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace RepositoryEFCodeFirst
{
class Program
{
static void Main(string[] args)
{
using (EFRepository<ProductContext> repository = new EFRepository<ProductContext>("ProductConnection"))
{
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<Product>(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();
aProduct.ProducName = "Updated Plates";
aProduct = repository.Update<Product>(aProduct);
aProduct = repository.Select<Product>().Where(one => one.ProductId == newProduct.ProductId).First();
Console.WriteLine("Update name is: " + aProduct.ProducName.ToString());
repository.Delete<Product>(aProduct);
}
}
}
}
I captured the result of the query (in bold) in profiler of my select with where and order by to ensure the operation takes place on the server. Here is the query.
SELECT
[Extent1].[ProductId] AS [ProductId],
[Extent1].[ProducNumber] AS [ProducNumber],
[Extent1].[ProducName] AS [ProducName],
[Extent1].[Notes] AS [Notes],
[Extent1].[IsActive] AS [IsActive]
FROM [dbo].[Products] AS [Extent1]
WHERE 1 = [Extent1].[IsActive]
ORDER BY [Extent1].[ProducNumber] ASC
This concludes sample repository pattern implementation for Entity Framework Code First. On a side note, I have not been blogging quite as much. As I found out, spending 2-3 hours in the car every day going to/from work is not very conducive to blogging
.
Thanks.