I am using WPF with Entity Framework 6
(DB first), Caliburn.Micro
, and MEF
.
I am trying to implement IoC
in my project. I am an absolute beginner in IoC
, and unfortunately, I cant find too many examples where MEF
is used with Repository Pattern
. Here is what I have till now:
All the generated entities inherit this interface
:
interface IHasPKey<T>
{
T ID { get; }
}
where ID
is the Primary Key
of the Entity
.
Now I have created a GenericRepository
like:
class GenericRepository<T> : IRepository<T> where T : class, IHasPKey<Type>
{
protected readonly DbContext _ctx;
public GenericRepository(DbContext context)
{
_ctx = context;
}
virtual public void Add(IEnumerable<T> entitylist)
{
_ctx.Set<T>().AddRange(entitylist);
}
virtual public void Add(T entity)
{
_ctx.Set<T>().Add(entity);
}
virtual public void Delete(IEnumerable<T> entitylist)
{
foreach (var entity in entitylist)
{
var entry = _ctx.Entry(entity);
if (entry.State == EntityState.Detached)
{
_ctx.Set<T>().Attach(entity);
}
}
_ctx.Set<T>().RemoveRange(entitylist);
}
virtual public void Delete(T entity)
{
var entry = _ctx.Entry(entity);
if (entry.State == EntityState.Detached)
{
_ctx.Set<T>().Attach(entity);
}
_ctx.Set<T>().Remove(entity);
}
virtual public IEnumerable<T> FetchAll()
{
return _ctx.Set<T>().ToList();
}
virtual public T FetchByPrimaryKey(Type primarykey)
{
return _ctx.Set<T>().AsNoTracking.SingleOrDefault(c => c.ID == primarykey);
}
virtual public IEnumerable<T> FindWhere(Expression<Func<T, Boolean>> predicate)
{
return _ctx.Set<T>().Where(predicate).AsNoTracking().ToList();
}
virtual public T FirstOrDefault(Expression<Func<T, Boolean>> predicate)
{
return _ctx.Set<T>().Where(predicate).AsNoTracking().FirstOrDefault();
}
virtual public void Update(T updatedentity)
{
_ctx.Set<T>().Attach(updatedentity);
_ctx.Entry(updatedentity).State = EntityState.Modified;
}
}
and my actual implementation of Repository
is like this:
class RateRepository : GenericRepository<Rate>, IRateRepository
{
public RateRepository(DbContext _ctx) : base(_ctx)
{
}
public BISDContext BISDContext
{
get
{
return _ctx as BISDContext;
}
}
public IEnumerable<Rate> GetRatesForClient(string clientname)
{
return BISDContext.Rates.Where(c => c.Client.ID.Contains(clientname)).ToList();
}
public IEnumerable<Rate> GetRatesByItem(string itemname)
{
return BISDContext.Rates.Where(c => c.Item.ID.Contains(itemname)).ToList();
}
public IEnumerable<ItemLine> GetInvoiceItemsForClient(Client client)
{
return BISDContext.Rates.Where(c => c.Client.ID == client.ID)
.OrderBy(c => c.Item.priority)
.Select((c) => new ItemLine()
{
ThisItem = c.Item,
ItemName = c.Item.ID,
Batch = c.Item.batch,
MRP = (decimal)c.Item.mrp,
Rate = (decimal)c.rate,
Unit = c.Item.displayunit,
Qty = 0,
IsEnabled = true
});
}
}
So far, so good, at least, I hope so.
Now for Unit of Work
, I have a GenericUoW
:
class GenericUoW : IUoW, IDisposable
{
protected readonly DbContext _ctx;
public GenericUoW(DbContext context)
{
_ctx = context;
}
public void Complete()
{
_ctx.SaveChanges();
}
public void Dispose()
{
_ctx.Dispose();
}
}
and for actual implementations, I have something like this:
class InvoiceUoW : GenericUoW, IInvoiceUoW
{
public InvoiceUoW(DbContext _ctx) : base(_ctx)
{
salesrepo = new SaleRepository(_ctx);
itemsrepo = new ItemRepository(_ctx);
materialrepo = new MaterialRepository(_ctx);
raterepo = new RateRepository(_ctx);
clientrepo = new ClientRepository(_ctx);
taxrepo = new TaxRepository(_ctx);
stockhistoryrepo = new StockHistoryRepository(_ctx);
proformarepo = new ProformaRepository(_ctx);
}
public ISaleRepository salesrepo { get; private set; }
public IItemRepository itemsrepo { get; private set; }
public IMaterialRepository materialrepo { get; private set; }
public IRateRepository raterepo { get; private set; }
public IClientRepository clientrepo { get; private set; }
public ITaxRepository taxrepo { get; private set; }
public IStockHistoryRepository stockhistoryrepo { get; private set; }
public IProformaRepository proformarepo { get; private set; }
}
Now, in my ViewModel
, I am doing something like this:
[Export(typeof(InvoiceViewModel))]
class InvoiceViewModel : PropertyChangedBase
{
#region ctor
[ImportingConstructor]
public InvoiceViewModel(IInvoiceUoW invoiceUoW)
{
_uow = invoiceUoW;
Clients = new BindableCollection<Client>(_uow.clientrepo.FetchAll());
}
#endregion
private readonly IInvoiceUoW _uow;
//other properties and methods
}
Now, the problem is that, in order to keep all the classes testable, I have to keep maintain a huge load of interfaces, one for each repository
and one for each Unit of Work
. Is this a code smell? Am I overdoing something?
And my second question is related to the use of IoC/DI
in InvoiceIoW
. How do I implement constructor injection
in that class? Because each of those repositories in that class has to be instantiated with the same DataContext
.
DbContext
lifetime to be what you need it to be. E.g. once per object graph would probably work. The longer answer is that EF already uses the UoW and Repository patterns which makes reinventing it yourself a little pointless :) \$\endgroup\$IQueryable
in myviewmodels
. \$\endgroup\$public InvoiceUoW(DbContext _ctx, ISaleRepository salerepo, IItemRepository itemsrepo,IMaterialRepository materialrepo, ... ) : base(_ctx)
, it does something equivalent to this:salesrepo = new SaleRepository(new DbContext()); itemsrepo = new ItemRepository(new DbContext()); materialrepo = new MaterialRepository(new DbContext());...
\$\endgroup\$