I'm trying to practise loose coupling and seperating everything in an n-tier architecture, but I'm stuck on - what I believe - basic stuff. My number one issue is references. I'm constantly moving classes between assemblies because Class A needs Class B, but can't reach it, so lets move Class B -- and then I break Class C.
This is the best I could come up with.
Step 1: Architecture
Project.Data
Project.DataAccess
- Context
- Migrations
- Repositories
- Unit of work
- ViewModels
Project.Web
Step 2: Use Project.DataAccess as glue between Presentation and Data
I am using the Unit of Work pattern, but that requires access to the POCOs, so I can't use the UoW in my Controller. Therefore I thought it would be a good plan to create wrapper/service called ViewModelService
. This service instantiates the UoW and returns AutoMapped Viewmodels to my controller.
However ...
My UoW/Repository patterns is generic, so I'm trying to make my service generic too.
IRepository
public interface IRepository<TObject>
{
IQueryable<TObject> All();
IQueryable<TObject> Filter(Expression<Func<TObject, bool>> predicate);
IQueryable<TObject> Filter<TKey>(Expression<Func<TObject, bool>> filter,
out int total, int index = 0, int size = 50);
bool Contains(Expression<Func<TObject, bool>> predicate);
TObject Find(params object[] keys);
TObject Find(Expression<Func<TObject, bool>> predicate);
TObject Create(TObject t);
int Delete(TObject t);
int Delete(Expression<Func<TObject, bool>> predicate);
int Update(TObject t);
void Ignore(TObject t);
int Count { get; }
}
Generic BaseRepository
public class BaseRepository<TObject> : IRepository<TObject>
where TObject : class
{
protected AppDbContext Context = null;
public BaseRepository(AppDbContext context)
{
Context = context;
}
protected DbSet<TObject> DbSet
{
get { return Context.Set<TObject>(); }
}
public virtual int Count
{
get { return Queryable.Count<TObject>(DbSet); }
}
// ... (You get the picture)
}
UnitOfWork
public class UnitOfWork : IDisposable
{
private readonly AppDbContext _context = new AppDbContext();
private BaseRepository<Order> _orderRepository;
private BaseRepository<Product> _productRepository;
private BaseRepository<ApplicationUser> _userRepository;
private bool _disposed;
public BaseRepository<Order> OrderRepository
{
get
{
if (_orderRepository == null)
{
_orderRepository = new BaseRepository<Order>(_context);
}
return _orderRepository;
}
}
public BaseRepository<Product> ProductRepository
{
get
{
if (_productRepository == null)
{
_productRepository = new BaseRepository<Product>(_context);
}
return _productRepository;
}
}
public BaseRepository<ApplicationUser> UserRepository
{
get
{
if (_userRepository == null)
{
_userRepository = new BaseRepository<ApplicationUser>(_context);
}
return _userRepository;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public void Save()
{
_context.SaveChanges();
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_context.Dispose();
}
}
_disposed = true;
}
}
So right now - without the "service" and the new n-tier layers - I use this in my Controllers.
public class ProductController : Controller
{
private UnitOfWork _unitOfWork = new UnitOfWork();
// GET: /Product/
[Route]
public ActionResult Index()
{
var model = _unitOfWork.ProductRepository.All();
return View(model);
}
// Etc...
But now that I am dividing everything up into seperate layers, I can't do that. And I don't want to have my Unit of Work class to map the ViewModels.
Here is my attempt to create this "service" (not sure it's even the correct name for it):
ViewModelService
public class ViewModelService : IViewModelService
{
private readonly UnitOfWork _unitOfWork = new UnitOfWork();
public T GetSingle<T>(int key)
{
// Get appropriate repository based on T1?
throw new System.NotImplementedException();
}
public IQueryable<T> GetAll<T>()
{
throw new System.NotImplementedException();
}
}
Now I am faced with the issue -- how do I make sure that when I call:
_viewModelService.GetSingle<ProductVM>(id);
it figures out - by itself (via reflection?) - that it should call:
_unitOfWork.ProductRepository.Find(id)
internally inside the repository?
Wow, I feel I did a terrible job explaining that! :)
TL;DR
I have a UnitOfWork class:
public class UnitOfWork : IDisposable
{
private readonly DbContext _context = new DbContext();
private BaseRepository<Order> _orderRepository;
private BaseRepository<Product> _productRepository;
private BaseRepository<ApplicationUser> _userRepository;
private bool _disposed;
public BaseRepository<Order> OrderRepository
{
get
{
if (_orderRepository == null)
{
_orderRepository = new BaseRepository<Order>(_context);
}
return _orderRepository;
}
}
public BaseRepository<Product> ProductRepository
{
get
{
if (_productRepository == null)
{
_productRepository = new BaseRepository<Product>(_context);
}
return _productRepository;
}
}
public BaseRepository<ApplicationUser> UserRepository
{
get
{
if (_userRepository == null)
{
_userRepository = new BaseRepository<ApplicationUser>(_context);
}
return _userRepository;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public void Save()
{
_context.SaveChanges();
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_context.Dispose();
}
}
_disposed = true;
}
}
Now I want to create a generic wrapper:
public class ViewModelService : IViewModelService
{
private readonly UnitOfWork _unitOfWork = new UnitOfWork();
public T GetSingle<T>(int key)
{
// Get appropriate repository based on T1?
throw new System.NotImplementedException();
}
}
How do I use reflection so that when I ask for GetSingle<ProductVM>(id)
the wrapper will translate that into a call to _unitOfWork.ProductRepository.Find(id);
-- so the wrapper knows to call the correct repository inside the UoW.
Whew.