Hi I am attempting to create a generic repository above EF4. Rather than going different Repository & UnitOfWork implementation, I wish to create a reposit开发者_Go百科ory class which will maintain the state of ObjectContext within itself. My interface looks like:
/// <summary>
/// Contract for base generic repository
/// </summary>
public interface IRepository
{
ActionResult SaveData<TEntity>(TEntity entityObj, bool commitTransaction) where TEntity : IEntity, new();
ActionResult SaveData<TEntity>(ICollection<TEntity> entityObjects, bool commitTransaction) where TEntity : IEntity, new();
ActionResult DeleteData<TEntity>(TEntity entityObj, bool commitTransaction) where TEntity : IEntity, new();
ActionResult DeleteData<TEntity>(ICollection<TEntity> entityObjects, bool commitTransaction) where TEntity : IEntity, new();
ICollection<TEntity> SelectAll<TEntity>() where TEntity : IEntity, new();
ICollection<TEntity> SelectByCondition<TEntity>(Func<TEntity, bool> condition) where TEntity : IEntity, new();
}
ActionResult is class which tells me whether a particular transaction was executed successfully. Now I didn't wanted to maintain the transaction state outside repository. So with every save/delete we can pass the bool value. For the first time, the transaction object can be checked internally and then when I call my last transaction I can send committransaction true, which will call SaveChanges() function.
My question is: Is this approach good by design? What problems can I face?
I see a lot of problems with this implementation.
- Explicitly telling each save and delete operation to commit or not is cumbersome and error prone. Just accidentally set one operation to false and your break atomicy. Controlling transactions should not be done at that level.
- The
SelectByCondition
takes aFunc<T, bool>
predicate, which means the complete database table must be loaded and filtered in memory. A better design would be to use Expression trees. - You say you defined a "repository class which will maintain the state of
ObjectContext
within itself" but in fact you defined a unit of work :-), because repository is used for one single type of object. - Every method takes the
TEntity
type argument, while this would be better suited at the interface level, because then you would be following the repository design pattern. Example:IRepository<TEntity>
. Such design would make the usage type safe.
Take a look at this article. It describes a (fairly abstract) way of implementing unit of works and repositories while allowing you to LINQ query over them and allowing them to be unit testable.
You've essentially created a hybrid unit of work and repository interface thereby breaking the SRP. I'd recommend you stick with Unit of Work typically injected into your repositories through an IoC container. You can still go with a generic Repository. It's a known pattern with a lot of community samples specifically around EF (check my answer on Unit Testing EF for a link).
Using a common pattern where possible rather than baking your own will always better serve any developer to the team now or in the future.
精彩评论