One of the concerns that many developers had with the initial Entity Framework release was the difficulty in building a repository. Having said that, the first thing I did upon opening Visual Studio 2010 was to build one. Soon into my test project, I noticed some interesting things. The first thing that really caught my attention was the IObjectSet interface, which provides the ability to modify data objects. Every bit as important, the ObjectContext class now has a generic CreateObjectSet<TEntity>() method. As soon as I saw these two things, I realized the potential, not only for creating a repository, but for creating a generic repository. That’s right, one generic class that can perform all of the CRUD operations for your entire application.
To start out, I built a generic interface for our repository. Notice that with the exception of the two SaveChanges() methods, every method either takes an object of type T as a parameter, or returns an object (either a collection or a single entity), also of type T.
/// <summary>
/// A generic repository for working with data in the database
/// </summary>
/// <typeparam name="T">A POCO that represents an Entity Framework entity</typeparam>
public class DataRepository<T> : IRepository<T> where T : class
{
/// <summary>
/// The context object for the database
/// </summary>
private ObjectContext _context;
/// <summary>
/// The IObjectSet that represents the current entity.
/// </summary>
private IObjectSet<T> _objectSet;
/// <summary>
/// Initializes a new instance of the DataRepository class
/// </summary>
public DataRepository()
: this(new AdventureWorksEntities())
{
}
/// <summary>
/// Initializes a new instance of the DataRepository class
/// </summary>
/// <param name="context">The Entity Framework ObjectContext</param>
public DataRepository(ObjectContext context)
{
_context = context;
_objectSet = _context.CreateObjectSet<T>();
}
/// <summary>
/// Gets all records as an IQueryable
/// </summary>
/// <returns>An IQueryable object containing the results of the query</returns>
public IQueryable<T> Fetch()
{
return _objectSet;
}
/// <summary>
/// Gets all records as an IEnumberable
/// </summary>
/// <returns>An IEnumberable object containing the results of the query</returns>
public IEnumerable<T> GetAll()
{
return GetQuery().AsEnumerable();
}
/// <summary>
/// Finds a record with the specified criteria
/// </summary>
/// <param name="predicate">Criteria to match on</param>
/// <returns>A collection containing the results of the query</returns>
public IEnumerable<T> Find(Func<T, bool> predicate)
{
return _objectSet.Where<T>(predicate);
}
/// <summary>
/// Gets a single record by the specified criteria (usually the unique identifier)
/// </summary>
/// <param name="predicate">Criteria to match on</param>
/// <returns>A single record that matches the specified criteria</returns>
public T Single(Func<T, bool> predicate)
{
return _objectSet.Single<T>(predicate);
}
/// <summary>
/// The first record matching the specified criteria
/// </summary>
/// <param name="predicate">Criteria to match on</param>
/// <returns>A single record containing the first record matching the specified criteria</returns>
public T First(Func<T, bool> predicate)
{
return _objectSet.First<T>(predicate);
}
/// <summary>
/// Deletes the specified entitiy
/// </summary>
/// <param name="entity">Entity to delete</param>
/// <exception cref="ArgumentNullException"> if <paramref name="entity"/> is null</exception>
public void Delete(T entity)
{
if (entity == null)
{
throw new ArgumentNullException("entity");
}
_objectSet.DeleteObject(entity);
}
/// <summary>
/// Deletes records matching the specified criteria
/// </summary>
/// <param name="predicate">Criteria to match on</param>
public void Delete(Func<T, bool> predicate)
{
IEnumerable<T> records = from x in _objectSet.Where<T>(predicate) select x;
foreach (T record in records)
{
_objectSet.DeleteObject(record);
}
}
/// <summary>
/// Adds the specified entity
/// </summary>
/// <param name="entity">Entity to add</param>
/// <exception cref="ArgumentNullException"> if <paramref name="entity"/> is null</exception>
public void Add(T entity)
{
if (entity == null)
{
throw new ArgumentNullException("entity");
}
_objectSet.AddObject(entity);
}
/// <summary>
/// Attaches the specified entity
/// </summary>
/// <param name="entity">Entity to attach</param>
public void Attach(T entity)
{
_objectSet.Attach(entity);
}
/// <summary>
/// Saves all context changes
/// </summary>
public void SaveChanges()
{
_context.SaveChanges();
}
/// <summary>
/// Saves all context changes with the specified SaveOptions
/// </summary>
/// <param name="options">Options for saving the context</param>
public void SaveChanges(SaveOptions options)
{
_context.SaveChanges(options);
}
/// <summary>
/// Releases all resources used by the WarrantManagement.DataExtract.Dal.ReportDataBase
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases all resources used by the WarrantManagement.DataExtract.Dal.ReportDataBase
/// </summary>
/// <param name="disposing">A boolean value indicating whether or not to dispose managed resources</param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (_context != null)
{
_context.Dispose();
_context = null;
}
}
}
}
To use it, you need only instantiate the generic repository of whichever entity type you wish to query. For example, using Adventure Works, you could bind to a GridView on an ASP.NET web form, with the following:
Hopefully, you have gotten a little insight into the power that the new Entity Framework has to offer. I really believe that Microsoft has made great progress in this release. For those that were turned off by eariler versions, I urge you to give Entity Framework another go with the 4.0 release.