I've seen many, many UoW+Repository implementations. Whenever one was built on top of Entity Framework, I'd cringe at the added complexity.
Sure the complexity buys you (sometimes) full decoupling from Entity Framework, which in theory would possibly allow swapping EF for, say, NHibernate, or even "downgrade" to a lower-level ADO.NET solution.
But most people implement an IUnitOfWork
interface for the sole purpose of enabling easier testability - I'm one of those. I'm not buying the "dude I can swap my ORM without touching the rest of my app" argument, simply because it's more often than not, a theoretical advantage, if not a plain lie.
So I like to keep it stupid simple (KISS), and only do the minimum needed to enable mocking of my DbContext
class - here's a real-world IUnitOfWork
interface I've written in a recent project:
IUnitOfWork interface
public interface IUnitOfWork
{
int SaveChanges();
IDbSet<TEntity> Repository<TEntity>() where TEntity : class;
}
Entity Framework's DbContext
is a unit of work, and IDbSet<TEntity>
is a repository. So instead of going all out and writing a generic repository and adding a considerable amount of useless complexity (useless in the sense that I don't need that to enable mocking my DbContext), I'm making my DbContext
-derived class implement my IUnitOfWork
interface:
CallAssistantContext class
[DbConfigurationType(typeof(MySqlEFConfiguration))]
public class CallAssistantContext : DbContext, IUnitOfWork
{
public CallAssistantContext(string connectionString)
: base(connectionString)
{ }
public IDbSet<TEntity> Repository<TEntity>() where TEntity : class
{
return base.Set<TEntity>();
}
public IDbSet<CustomerServiceCall> CustomerServiceCalls { get; set; }
public IDbSet<CallType> CallTypes { get; set; }
public IDbSet<ContactType> ContactTypes { get; set; }
public IDbSet<IssueType> IssueTypes { get; set; }
public IDbSet<Customer> Customers { get; set; }
public IDbSet<CustomerStore> CustomerStores { get; set; }
public IDbSet<SalesRep> SalesReps { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
The "client code" depends on an abstraction - the IUnitOfWork
interface, and can very easily be constructor-injected with a mock implementation in unit tests:
public class MainWindowViewModel : ViewModelBase { private readonly IUnitOfWork _context; public MainWindowViewModel(IUnitOfWork context) { _context = context; }
Querying the context doesn't look too complicated either:
var cutoffDate = DateTime.Now.Subtract(TimeSpan.FromDays(7)); ServiceCalls = _context.Repository<CustomerServiceCall>() .Where(e => e.CallStart >= cutoffDate) .ToList();
So, is anything blatantly wrong with this approach? Here I've injected the UoW/context directly where it's needed - in a bigger project I would have created a "service" and constructor-injected the UoW there instead.