Messing around with Ruby and Rails in general makes you think a little bit about DRY (don’t repeat yourself) principles. This mentality is scattered around the whole rails framework in general, and I started adopting it into my daily programming. One of the sections that wasn’t very “DRY” was my entity framework and the CRUD operations. This part of the code is not only tedious but the part of the code that is the most prone to errors.
Let me show you how this works. First off, this post is assuming your using some sort of dependency injection (I prefer ninject).
I start off with an Repository class that inherits from IRepository.
public class Repository : IRepository
{
public IFaqRepository Faqs
{ get; private set;
}
public Repository(IFaqRepository faqRepository)
{
Faqs = faqRepository;
}
}
So what we have is a FAQ Repository. This repository is abstract of the data access layer. So essentially we want to build in any custom queries. We want to hide the queries against the entity framework. Let’s now look at the FAQRepository class.
public class FaqRepository : Repository, IFaqRepository
{
public FaqRepository
(DataBaseEntities context
)
: base
(context
)
{
}
public IQueryable AllPublished()
{
return All().Where(faq => faq.IsPublished);
}
}
Nothing too fancy going on here. You inherit from a base Repository class which takes as a generic parameter an Entity Framework Entities, and secondly an Entity Framework Entity. The other part is the IQueryable method. This is a custom method I wrote that filters out the published faq’s.
So what’s going on behind the scenes?
public abstract class Repository : Repository
where TEntity : EntityObject, new
()
where TObjectContext : ObjectContext, new
()
{
protected Repository
(TObjectContext context
)
: base
(context
)
{ }
public IQueryable All()
{
return DataContext.CreateObjectSet();
}
public TEntity Save(TEntity entity)
{
using (var transaction = new TransactionScope())
{
if (entity.EntityKey == null || entity.EntityState == EntityState.Added)
DataContext.AddObject(EntitySetName, entity);
if (entity.EntityState == EntityState.Detached)
DataContext.Attach(entity);
DataContext.SaveChanges();
transaction.Complete();
return entity;
}
}
public TEntity ById(Expression> predicate)
{
return All().FirstOrDefault(predicate);
}
public IQueryable Find(Expression> predicate)
{
return All().Where(predicate);
}
public void Delete(TEntity entity)
{
DataContext.DeleteObject(entity);
DataContext.SaveChanges();
}
protected string EntitySetName
{
get { return DataContext.CreateObjectSet().EntitySet.Name; }
}
}
This is where most of the magic happens–Using generics I was able to abstract out all the crud (Create, Read, Update, Delete) operations. The only method that might be a little confusing is a helper method called EntitySetName. The method helps get me the name of the entityset. It’s marked as protected not private because I need it for a lower abstraction.
There are also other Repository’s in the source that allows you to make business rules engines and attaches them to the repository here. These will allow you to add custom validation per entity, but I hear the new CTP6 of the Entity Framework code first will have an IValidate interface for putting business logic on the entities themselves. One advantage my business rule engine has is it allows you to have the ObjectContext to allow you to query the database.
The last piece of code you need is the IRepository code. This interface will load up all the repositories, and allow you to moq the interfaces for testing.
public interface IFaqRepository : IRepository
{
IQueryable AllPublished();
}
The IRepository interface is more of a helper than anything. To me it simlifies what custom methods I have on a given repository. The IRepository exposes all the methods that the other repository base has (All(), ById(), Save(), Delete()).
I will do a screen cast on this in the near future. For now, you can download the full version at bitbucket. https://bitbucket.org/tylergarlick/nexbusiness-core
Kickit on DotNetKicks.com
Recent Comments