I m working with an application in which Base inteface has been created as below
public interface IBaseRepository : IDisposable
{
bool IsUnitOfWork { get; set; }
void SaveChanges();
}
Then other interfaces extend this interface as
public interface ICourseRepository : IBaseRepository
{
Course GetCourseById(int id);
List<Course> GetCourses();
CourseModule GetCourseModuleById(int id);
}
Just wondering what would be the advantage of this approach
The base repository allows you to specify behavior you want all repository contracts to have without repeating it in every IMyEntityRepository you create.
Even more fun though is then implementing a Base Repository like this and specifying generic code for generic operations:
public abstract class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class {
private DbContext _context;
public Repository(DbContext context) {
if (context == null) {
throw new ArgumentNullException("context");
}
_context = context;
}
protected DbContext DbContext { get { return _context; } }
public void Create(TEntity entity) {
if (entity == null) {
throw new ArgumentNullException("entity");
}
DbContext.Set<TEntity>().Add(entity);
DbContext.SaveChanges();
}
public TEntity GetById(int id) {
return DbContext.Set<TEntity>().Find(id);
}
public void Delete(TEntity entity) {
if (entity == null) {
throw new ArgumentNullException("entity");
}
DbContext.Set<TEntity>().Attach(entity);
DbContext.Set<TEntity>().Remove(entity);
DbContext.SaveChanges();
}
public void Update(TEntity entity) {
if (entity == null) {
throw new ArgumentNullException("entity");
}
DbContext.Set<TEntity>().Attach(entity);
DbContext.Entry(entity).State = EntityState.Modified;
DbContext.SaveChanges();
}
etc., Then have your MyEntityReporitory extend your BaseRepository
You can reuse the base interface for multiple classes/interfaces without requiring implementation of other interfaces also - so you can have IsUnitOfWork available on a bunch of classes, without needing to also implement GetCourseById on all of those classes. However you can also ensure that all CourseRepositories are treated as units of work.
Related
I am trying to create a Repository & UnitOfWork for Data Access Layer. In my current implementation I have to modify my UnitOfWork everytime I create a new repository. I would like to avoid that and also keep the functionality to extend my repository abstract class.
Following is my generic Repository & UnitOfWork interface & classes
public interface IRepositoryBase<T> where T : class
{
IList<T> FindAll();
T FindByCondition(Expression<Func<T, bool>> expression);
void Create(T entity);
void Update(T entity);
void Delete(T entity);
}
public abstract class RepositoryBase<T> : IRepositoryBase<T> where T : class
{
protected DBContext _dbContext { get; set; }
public RepositoryBase(DBContext dbContext)
{
_dbContext = dbContext;
}
//other methods removed
public void Create(T entity)
{
_dbContext.Set<T>().Add(entity);
}
}
public interface IUnitOfWork
{
IReminderRepository Reminder { get; }
void Save();
}
public class UnitOfWork : IUnitOfWork, IDisposable
{
protected DBContext _dbContext { get; set; }
private IReminderRepository _reminderRepository;
public UnitOfWork(DBContext dbContext)
{
_dbContext = dbContext;
}
public IReminderRepository Reminder
{
get
{
return _reminderRepository = _reminderRepository ?? new ReminderRepository(_dbContext);
}
}
public void Save()
{
_dbContext.SaveChanges();
}
public void Dispose()
{
_dbContext.Dispose();
}
}
Here I can extend my Repository as per my specific needs by implementing the specific Repository as
public interface IReminderRepository : IRepositoryBase<Reminder>
{
IList<Reminder> GetAllReminders();
Reminder GetReminderById(Guid id);
Reminder GetReminderByName(string name);
void CreateReminder(Reminder reminder);
void UpdateReminder(Reminder reminder);
void DeleteReminder(Reminder reminder);
}
public class ReminderRepository : RepositoryBase<Reminder>, IReminderRepository
{
public ReminderRepository(DBContext dbContext)
: base(dbContext)
{
_dbContext = dbContext;
}
//other methods removed
public Reminder GetReminderByName(string name)
{
return FindAll()
.OrderByDescending(r => r.Name)
.FirstOrDefault(r => r.Name == name);
//return FindByCondition(r => r.Name == name);
}
}
This is ok but when ever I will create a new Specific Repository I will have to modify the UnitOfWork class as well by adding a new property for the new Repository.
While searching online I found following but it does not work in my case as my RepositoryBase is an abstract class.
public interface IUnitOfWork
{
void Save();
}
public class UnitOfWork : IUnitOfWork, IDisposable
{
private readonly DBContext _dbContext { get; set; }
private readonly Dictionary<Type, object> _repositories = new Dictionary<Type, object>();
public Dictionary<Type, object> Repositories
{
get { return _repositories; }
set { Repositories = value; }
}
public UnitOfWork(DBContext dbContext)
{
_dbContext = dbContext;
}
public IRepositoryBase<T> Repository<T>() where T : class
{
if (Repositories.Keys.Contains(typeof(T)))
{
return Repositories[typeof(T)] as IRepositoryBase<T>;
}
IRepositoryBase<T> repo = new RepositoryBase<T>(_dbContext);//This does not work
Repositories.Add(typeof(T), repo);
return repo;
}
public void Save()
{
_dbContext.SaveChanges();
}
}
You obviously need to get a reference to a IReminderRepository somewhere in your code to be able to use the remainder specific APIs.
If you don't want to extend your UnitOfWork class to return an IReminderRepository, you may create one yourself in the method that actually uses the specific repository, e.g.:
using (var context = new DBContext())
{
IUnitOfWork uow = new UnitOfWork(context);
ReminderRepository repository = new ReminderRepository(context);
Reminder remainder = repository.GetReminderByName("...");
remainder.SomeProperty = "updated value..";
uow.Save();
}
The only purpose of using the unit of work is to be able to share the same context between several different repositories anyway. Exposing a Dictionary<Type, object> in your UnitOfWork won't solve anything as the purpose of using generics is to provide compile-time type safety.
I am trying to implement UOW with repository pattern in my application.
While the independent repository is in place, but while multiple repositories in one transaction (UOW) is driving me crazy.
EF Relation One Customer - Many CustomerContacts
IUnitOfWork
public interface IUnitOfWork
: IDisposable
{
void InitTransaction();
void Rollback();
void CommitTransaction();
}
BaseUOW
public class UnitOfWork :
IUnitOfWork
{
protected DbContextTransaction _transaction;
#region IUnitOfWork
public void CommitTransaction()
{
_transaction.UnderlyingTransaction.Commit();
}
public void Rollback()
{
_transaction.UnderlyingTransaction.Rollback();
}
#endregion IUnitOfWork
}
CustomerUOW
public class CustomerUOW
: UnitOfWork
{
private IRepository<CustomerRepository> _customerRepository;
private IRepository<CustomerContactRepository> _customerContactRepository;
public BranchUOW(IRepository<CustomerRepository> customerRepository,
IRepository<CustomerContactRepository> customerContactRepository)
{
_customerRepository= customerRepository;
_customerContactRepository= customerContactRepository;
}
public override void InitTransaction()
{
_transaction.Commit();
}
}
How do I implement my CustomerUOW so that Customer &
CustomerContact repository share the same DbContext & goes in one
transaction??
Note: Each repository has an implementation of CRUD in their separate class. like
public class EntityRepository<C, T>
: BaseRepository<FoodieTenantContext, T>
where T : class
where C : CustomerContext
{
private DbSet<T> _dataSet
{
get
{
return _ctx.Set<T>();
}
}
public EntityRepository(FoodieTenantContext ctx)
: base(ctx)
{
}
public override void Add(T entity)
{
_dataSet.Add(entity);
}
public override void Delete(T entity)
{
throw new NotImplementedException();
}
public override IEnumerable<T> Find(Expression<Func<T, bool>> predicate)
{
return _dataSet.Where(predicate).ToList<T>();
}
public override IEnumerable<T> GetAll()
{
return _dataSet.ToList<T>();
}
public override IQueryable<T> GetQuery()
{
return _dataSet;
}
public override int Save()
{
return _ctx.SaveChanges();
}
public override T Single(Expression<Func<T, bool>> predicate)
{
return _dataSet.Where(predicate).SingleOrDefault();
}
public override void Update(T entity)
{
_dataSet.Attach(entity);
_ctx.Entry<T>(entity).State = EntityState.Modified;
}
}
Thanks
On way would be to provide a Func<FoodieTenantContext, IRepository<CustomerContactRepository>> in your CustomerUow
public abstract class UnitOfWork : IUnitOfWork
{
public UnitOfWork(FoodieTenantContext context)
{
this.Context = context;
}
// ... rest of the class
}
// usage could be like the following
public class CustomerUOW : UnitOfWork
{
public CustomerService(Func<FoodieTenantContext, IRepository<CustomerRepository>> customerRepo
, Func<FoodieTenantContext, IRepository<CustomerContactRepository>> contactRepo
, FoodieTenantContext context)
: (context)
{
_customerRepo = customerRepo(context);
_contactRepo = contactRepo(context);
}
}
Another option would be to create a RepositoryFactory, but this would mean you would have to expose a Context property from IRepository<T>
public class RepositoryFactory
{
IServiceProvider _ioc; // This would be your IoC/DI Container
public RepositoryFactory(IServiceProvider ioc)
{
_ioc = ioc;
}
// Resolve T passing in the provided `FoodieTenantContext` into the constructor
public IRepository<T> CreateRepository<T>(FoodieTenantContext context) =>
_ioc.Resolve<T>(context);
}
Another solution could be (my least favourite) would be to expose methods in a RepositoryFactory for each type of IRepository<T>
public class RepositoryFactory
{
public IRepository CreateCustomerContactRepository(FoodieTenantContext context) =>
return new CustomerContactRepository(context);
}
Registering Func in Castle.Windsor
As per comment, to register Func<T> in Castle.Windsor you can try something like the following which is a modified version of Anton's answer to Func injecting with Windsor container question.. (I am not able to test this right now)
Container.Register(
Component.For<Func<FoodieTenantContext, IRepository<CustomerRepository>>>()
.Instance((FoodieTenantContext context) => Container.Resolve<IRepository<CustomerRepository>>(new {context = context}))
)
Otherwise you could try playing around with AsFactory(). For more info read Windsor documentation
And as a last resort, you can always fallback to manually creating a factory or switching IoC/DI containers that support Func<[TIn1, TIn2, ...], T> out of the box, or at least natively.
I'm creating a MVC Web App in C#, it started off with a nice-and-simple DbContext. Then I created repositories so I could write unit tests... Then I implemented dependency injection.. oh no, now I want to create a service layer between my controller and repository.
It's pretty much there apart from I don't know how to call the generic functions from my repository in the service.
Do I have to repeat all of the generic repository functions in the service?
Here's the generic repository:
public interface IRepository<TEntity> : IDisposable where TEntity : class
{
int Count { get; }
IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "");
IQueryable<TEntity> All();
TEntity GetByID(object id);
void Insert(TEntity entity);
void Delete(object id);
void Delete(TEntity entityToDelete);
void Update(TEntity entityToUpdate);
void Save();
}
EF Repository:
public abstract class Repository<CEntity, TEntity> : IRepository<TEntity> where TEntity : class
where CEntity : DbContext, new()
{
private CEntity entities = new CEntity();
protected CEntity context
{
get { return entities; }
set { entities = value; }
}
public virtual int Count
{
get { return entities.Set<TEntity>().Count(); }
}
public virtual IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "")
{
IQueryable<TEntity> query = entities.Set<TEntity>();
if (filter != null)
{
query = query.Where(filter);
}
foreach (var includeProperty in includeProperties.Split
(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
if (orderBy != null)
{
return orderBy(query).ToList();
}
else
{
return query.ToList();
}
}
public virtual IQueryable<TEntity> All()
{
return entities.Set<TEntity>().AsQueryable();
}
public virtual TEntity GetByID(object id)
{
return entities.Set<TEntity>().Find(id);
}
public virtual void Insert(TEntity entity)
{
entities.Set<TEntity>().Add(entity);
}
public virtual void Delete(object id)
{
TEntity entityToDelete = entities.Set<TEntity>().Find(id);
Delete(entityToDelete);
}
public virtual void Delete(TEntity entityToDelete)
{
if (context.Entry(entityToDelete).State == EntityState.Detached)
{
entities.Set<TEntity>().Attach(entityToDelete);
}
entities.Set<TEntity>().Remove(entityToDelete);
}
public virtual void Update(TEntity entityToUpdate)
{
entities.Set<TEntity>().Attach(entityToUpdate);
context.Entry(entityToUpdate).State = EntityState.Modified;
}
public virtual void Save()
{
entities.SaveChanges();
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
context.Dispose();
}
}
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
Service:
public class CampaignService : ICampaignService
{
private readonly IRepository<Campaign> _campaignRepository;
public CampaignService(IRepository<Campaign> campaignRepository)
{
_campaignRepository = campaignRepository;
}
public Campaign GetLatestCampaign()
{
var query = _campaignRepository.Get(x => x.CreatedOn != null, q => q.OrderByDescending(s => s.CreatedOn));
Campaign result = query.First();
return result;
}
}
public interface ICampaignService
{
Campaign GetLatestCampaign();
}
But obviously I can't get the generic properties in the controller:
Do I have to repeat all of the repository functions in the service? But instead of retreiving from DbContext it gets it from the repo..
Seems like a lot of repeat code, don't ya think?
Or should you repeat code but not make the service generic and specifically state what the service is doing- i.e. _campaignService.AddCampaign(c); instead of _campaignService.Add(c);
It's an anti-corruption layer, so yes, you would have to redefine the contract.
Think of it this way:
The generic repository interface has a job: hide any implementation details about how entities are persisted and retrieved.
The service interface has a job: represent use cases.
At first, it may seem like the same methods would be exposed in both cases; however, this rarely holds true except in the simplest of CRUD applications. For example, your service interface could expose multiple ways of adding a user to the system, but the implementation of those methods would simply call the lone Insert method in your repository interface.
Another way of thinking about this: It is incidental if your service interface looks exactly like your repository interface. You should try and shift your thinking to persistence-like language (Insert) or service-like language (AddUser) depending on what code you're working on.
We use base repositories and base services to reduce the amount of redundant code, so that our concrete implementations only have the additional methods required within them.
Every Repository looks like this to begin with, and is extended as needed.
note: ModelBase is a simple base model that we use on all our models. contains things like ID, LastUpdated, isDeleted, etc
public abstract class RepositoryBase<TModel> where TModel : ModelBase, new()
{
protected RepositoryBase(UserModel loggedOnUser,
IDbProvider dbProvider)
{
DbProvider = dbProvider;
LoggedOnUser = loggedOnUser;
}
public virtual Guid Create(TModel model)
{
// Create the record
DbProvider.Create(model);
return model.Id;
}
public virtual TModel GetById(Guid id)
{
var model = DbProvider.Query<TModel>(m => m.Id == id).FirstOrDefault();
if (model == null)
{
throw new NotFoundException(string.Format(NotFoundMessage, id));
}
return model;
}
public virtual IList<TModel> Find()
{
return DbProvider.Query<TModel>(m => m.IsDeleted == false).ToList();
}
public virtual void Update(TModel model)
{
// Set the update/create info
SetCreateInfo(model);
// Update the record
try
{
DbProvider.Update(model);
}
catch (Exception ex)
{
ThrowKnownExceptions(ex);
}
}
public virtual void Delete(TModel model)
{
// Do NOT SetUpdateInfo(model); it's being done in the Update method.
model.IsDeleted = true;
Update(model);
}
public virtual void Delete(Guid id)
{
var model = GetById(id);
Delete(model);
}
}
Then we have a generic service layer
public abstract class ServiceBase<TModel, TViewModel>
where TModel : ModelBase, new()
where TViewModel : ViewModelBase, new()
{
private readonly IRepository<TModel, Guid> _repository;
protected AutoMapper<TModel> ToModel;
protected AutoMapper<TViewModel> ToViewModel;
protected ServiceBase(IRepository<TModel, Guid> repository)
{
_repository = repository;
ToModel = new AutoMapper<TModel>();
ToViewModel = new AutoMapper<TViewModel>();
}
public virtual TViewModel Save(TViewModel viewModel)
{
if (viewModel.Id != Guid.Empty)
{
// The ModelObject Id is not empty, we're either updating an existing ModelObject
// or we're inserting a new ModelObject via sync
var model = _repository.GetById(viewModel.Id);
if (model != null)
{
// Looks like we're updating a ModelObject because it's already in the database.
_repository.Update(ToModel.BuildFrom(viewModel));
return ToViewModel.BuildFrom(_repository.GetById(viewModel.Id));
}
}
// The ModelObject is being created, either via a Sync (Guid Exists), or via an Insert (Guid doesn't Exist)
var id = _repository.Create(ToModel.BuildFrom(viewModel));
return ToViewModel.BuildFrom(_repository.GetById(id));
}
public virtual TViewModel GetById(Guid id)
{
var model = _repository.GetById(id);
return ToViewModel.BuildFrom(model);
}
public virtual IList<TViewModel> Find()
{
return ToViewModel.BuildListFrom(_repository.Find());
}
public virtual void Delete(TViewModel viewModel)
{
var model = ToModel.BuildFrom(viewModel);
_repository.Delete(model);
}
}
That's it for generic stuff...##
every single repository and service will depend on the above.
any repository that is just doing basic CRUD will look like this
public class TenantRepository : RepositoryBase<TenantModel>, ITenantRepository
{
public TenantRepository(UserModel loggedOnUser, IDbProvider dbProvider) : base(loggedOnUser, dbProvider)
{
}
}
And if we ever need an additional method, we just add it to the interface and the concrete implementation. If we don't need anything beyond basic CRUD, the above Repository is "complete".
After all that, we have a similar generic service layer.
and our concrete services are as easy to implement as the concrete repositories.
Every Service looks like this to begin with, and is extended as needed.
public class TenantService : ServiceBase<TenantModel, TenantViewModel>, ITenantService
{
private readonly ITenantRepository _TenantRepository;
public TenantService(ITenantRepository TenantRepository)
: base(TenantRepository)
{
_TenantRepository = TenantRepository;
}
}
and finally, some psuedo code to show how we "find" through the service.
var tenantRepository = new TenantRepository(myself, mydbProvider);
var tenantService = new TenantService(tenantRepository);
var tenants = tenantService.Find();
That's it. Once you wire up your BaseRepository and BaseService, extending the other ones for basic CRUD require next to no redundant code.
I have the following layers in my application.
Repository.Ef (this handle the context of ef)
Entities (here is all entities for ef)
Core (This layer handle all business and works like a wrapper
between Ef <> Gui)
Gui (This is the User interface)
I have interface for most of the classes and use DI, but this version is compressed to only show the classes.
This is the UnitOfWork, that hold the DbContext in my Repository.Ef layer.
public class UnitOfWork : DbContext, IUnitOfWork
{
static UnitOfWork()
{
Database.SetInitializer<UnitOfWork>(null);
}
public UnitOfWork()
: base("Name=SalesDb")
{
}
public IRepository<T> Repository<T>() where T : EntityBase
{
return new Repository<T>(Set<T>());
}
public void ApplyStateChanges()
{
foreach (var dbEntityEntry in ChangeTracker.Entries())
{
var entityState = dbEntityEntry.Entity as EntityBase;
if (entityState == null)
throw new InvalidCastException("All entites must implement the IObjectState interface, " +
"this interface must be implemented so each entites state can explicitely determined when updating graphs.");
dbEntityEntry.State = StateHelper.ConvertState(entityState.State);
}
}
#region DBSET
// HERE IS ALL MY DBSETS
#endregion
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
public override int SaveChanges()
{
ApplyStateChanges();
return base.SaveChanges();
}
}
And this is my Repository (same layer as UnitOfWork)
public class Repository<T> : IRepository<T> where T : class, IEntity
{
private readonly DbSet<T> _dbSet;
public Repository(DbSet<T> dbSet)
{
_dbSet = dbSet;
}
public IQueryable<T> Query()
{
var data = _dbSet.AsQueryable();
return data;
}
public IEnumerable<T> GetAll()
{
return _dbSet;
}
public IEnumerable<T> Find(Expression<Func<T, bool>> predicate)
{
return _dbSet.Where(predicate);
}
public T FindById(int id)
{
return _dbSet.Find(id);
}
public void Add(T entity)
{
entity.State = ObjectState.Added;
_dbSet.Add(entity);
}
public void Remove(T entity)
{
entity.State = ObjectState.Deleted;
_dbSet.Remove(entity);
}
public void Update(T entity)
{
entity.State = ObjectState.Modified;
_dbSet.Attach(entity);
}
}
Here is my Core layer (Business rules and the wrapper between GUI layer)
The following is my ServiceUnit.
public class ServiceUnit
{
internal readonly IUnitOfWork unitOfWork;
public ServiceUnit()
{
unitOfWork = new UnitOfWork();
}
public void Add<T>(T entity, int marketId, string username) where T : EntityBase
{
entity.MarketId = marketId;
entity.ChUser = username;
entity.ChTime = DateTime.Now;
entity.Deleted = false;
unitOfWork.Repository<T>().Add(entity);
unitOfWork.SaveChanges();
}
public void Update<T>(T entity, string username) where T : EntityBase
{
entity.ChUser = username;
entity.ChTime = DateTime.Now;
unitOfWork.Repository<T>().Update(entity);
unitOfWork.SaveChanges();
}
public void Remove<T>(int id) where T : EntityBase
{
var entity = unitOfWork.Repository<T>().FindById(id);
entity.Deleted = true;
entity.ChTime = DateTime.Now;
unitOfWork.Repository<T>().Update(entity);
unitOfWork.SaveChanges();
}
public IEnumerable<T> Find<T>(int? marketId = null, Expression<Func<T, bool>> predicate = null) where T : EntityBase
{
var data = unitOfWork.Repository<T>()
.Find(predicate);
if (marketId != null)
{
data = data
.Where(t => t.MarketId == marketId);
}
return data;
}
public T FindById<T>(int id) where T : EntityBase
{
return unitOfWork.Repository<T>().FindById(id);
}
public void Commit()
{
unitOfWork.SaveChanges();
}
}
And this is a Service class to handle all Contact functions
public class ContactService
{
private readonly ServiceUnit serviceUnit;
private IRepository<Contact> contactRep
{
get { return serviceUnit.unitOfWork.Repository<Contact>(); }
}
private IRepository<ContactUserProfile> contactUserProfileRep
{
get { return serviceUnit.unitOfWork.Repository<ContactUserProfile>(); }
}
public ContactService(ServiceUnit serviceUnit)
{
this.serviceUnit = serviceUnit;
}
public IEnumerable<ContactUserProfile> GetContactsForUser(int marketId, int userId, int status)
{
return contactUserProfileRep
.Query()
.Where(u => u.Contact.MarketId == marketId)
.Where(cup => cup.UserProfileId == userId)
.Where(c => c.Deleted == false)
.Where(c => c.Contact.Status == status)
.ToList();
}
}
Lets explain how i use all this code.
First of all, i dont want to have dependency for entity framework in my gui layer, and with this service wrapper (ServiceUnit) i dont have to reference entity framework.
Every page request create a ServiceUnit, and the ServiceUnit create a new UnitOfWork that hold whole the EntityFramework context.
For example, the contact page create a ServiceUnit and a ServiceContact and inject the Service unit, so i have the same context for the request.
Can this pattern cause any problem? Just want to se if i missed something importent here.
Do you need the "ServiceUnit" class? Why not use the UnitOfWork directly in your services?
What I'd suggest is basically to have have four projects:
Data Access Layer Project: EF DbContext, Repositories, UnitOfWork. References "Entities" project.
Entities Project: EF entities (if you want to share the EF entities
throughout the solution). Doesn't reference any other project.
Service Layer Project: ContactService, etc. Each has the UnitOfWork
injected into them. References "Data Access Layer" and "Entities" project.
GUI Project: with your UI. References "Entities" and "Service Layer" project.
I think ServiceUnit is an unnecessary abstraction, and services can deal with UnitOfWork directly (unless I'm missing something).
By the way, I wouldn't recommend exposing IQueryable from your repositories (as someone suggested to me on another question). Once you expose it, the query is executed outside your repository and so you loose control over its execution (exception handling, etc.). If you search a bit you'll see there's some controversy over this.
First sorry if this was asked already but i cannot find an answer for this 'particular case'.
I've a Interface of Unit of Work:
public interface IUnitOfWork
{
DbContext Context { get; set; }
void Dispose();
void Save();
}
And use a Generic Repository class:
public class GenericRepository<TEntity> where TEntity : class
{
private DbSet<TEntity> dbSet;
private IUnitOfWork UnitOfWork { get; set; }
private DbContext context { get { return UnitOfWork.Context; } }
public GenericRepository(IUnitOfWork unitOfWork)
{
UnitOfWork = unitOfWork;
this.dbSet = context.Set<TEntity>();
}
public virtual IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "")
{
IQueryable<TEntity> query = dbSet;
if (filter != null)
{
query = query.Where(filter);
}
foreach (var includeProperty in includeProperties.Split
(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
if (orderBy != null)
{
return orderBy(query).ToList();
}
else
{
return query.ToList();
}
}
public virtual TEntity GetByID(object id)
{
return dbSet.Find(id);
}
public virtual void Insert(TEntity entity)
{
dbSet.Add(entity);
}
public virtual void Delete(object id)
{
TEntity entityToDelete = dbSet.Find(id);
Delete(entityToDelete);
}
public virtual void Delete(TEntity entityToDelete)
{
if (context.Entry(entityToDelete).State == EntityState.Detached)
{
dbSet.Attach(entityToDelete);
}
dbSet.Remove(entityToDelete);
}
public virtual void Update(TEntity entityToUpdate)
{
dbSet.Attach(entityToUpdate);
context.Entry(entityToUpdate).State = EntityState.Modified;
}
}
I don't want to do my logic in my MVC controler, so I added a businesslayer.
My question is, where should I instantiate (and dispote) my IUnitOfWork, in my controler and pass it to my business layer?
Example:
public static class CircleLogic
{
public static void DeleteCircle(IUnitOfWork uow, int id)
{
try
{
var circleRep = new GenericRepository<Circle>(uow);
var circle = circleRep.GetByID(id);
......
circleRep.Delete(id);
uow.Save();
}
catch (Exception ex)
{
throw;
}
}
}
I've seen this but I don't want to instantiate it in my business layer.
What is the best approach?
Thanks!
I see no harm in passing it into your Business Layer like you have suggested. However, if you want to keep your Business Layer completely persistence ignorant I would suggest introducing an IRepository<T> interface and passing that in instead.
In terms of disposing of the objects, I would make both your IUnitOfWork/Repository classes implement IDisposable so you can make use of the using statement e.g.
public ActionResult DeleteCircle(int id)
{
using (IUnitOfWork uow = new UnitOfWork())
{
using (IRepository<Circle> repo = new GenericRepository<Circle>(uow))
{
CircleLogic.DeleteCircle(repo, id);
}
uow.Save();
}
}
...
public static class CircleLogic
{
public static void DeleteCircle(IRepository<Circle> repo, int id)
{
var circle = repo.GetById(id);
...
repo.Delete(id);
}
}
Because your concrete UnitOfWork implementation would most likely live in your persistence layer, it'd be sensible to instantiate it in either the persistence layer or 1 layer above in the business layer. Your UI should have no knowledge of what technology you're using to persist your entities/data.