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.
Related
I have an MVC 5 application that uses EF 6 and implements Repository pattern with dependency injection using the DI container Ninject. The connection string for the dbcontext is stored in the Web.config file which the EF Context properly finds. Everything works fine. Lately, I have a requirement that the connection to my DBContext need to be determined at runtime and connect to different databases (but with exactly the same structure). So, I need to change the sql connectionstring part from the entity connectionstring at run-time before the repository is instantiated. I would really appreciate some help in doing it. I am not a DI guru; know just enough Ninject to get my things going.
Here is my Repository Base Interface:
public interface IRepositoryBase<T> where T : class
{
void Add(T entity, string userGuid = "");
void Delete(T entity);
// ... removed other method signatures for brevity
}
My Repository base implementation:
public abstract class RepositoryBase<D, T> : IRepositoryBase<T>, IDisposable
where T : class
where D : DbContext, new()
{
private Guid? currUserGuid = null;
private D dataContext;
protected D DataContext
{
get
{
if (dataContext == null)
dataContext = new D();
return dataContext;
}
set { dataContext = value; }
}
public IQueryable<T> FindBy(Expression<Func<T, bool>> predicate)
{
return DataContext.Set<T>().Where(predicate);
}
public virtual IQueryable<T> GetAll()
{
IQueryable<T> query = DataContext.Set<T>();
return query;
}
public virtual void Delete(T entity)
{
OperationStatus stat = TryDelete(entity);
}
// .... removed rest for brevity
}
Interface and implementation for concrete class:
public interface ICustomerRepository : IRepositoryBase<Customer>
{
Customer GetCustomerAndStatus( Guid custGuid );
}
public class CustomerRepository : RepositoryBase<PCDataEFContext, Customer>, ICustomerRepository
{
public Customer GetCustomerAndStatus( Guid custGuid )
{
return DataContext.Customers.Include( x => x.CustStatusType )
.SingleOrDefault( x => x.PKGuid == custGuid );
}
}
My Ninject dependency resolver:
public class NinjectDependencyResolver : IDependencyResolver
{
private IKernel kernel;
public NinjectDependencyResolver()
{
kernel = new StandardKernel();
AddBindings();
}
public IKernel Kernel { get { return kernel; } }
private void AddBindings()
{
kernel.Bind<ICustomerRepository>().To<CustomerRepository>();
// ... other bindings are omitted for brevity
}
}
and finally, here is my Entity Framework generated DBContext:
public partial class PCDataEFContext : DbContext
{
public PCDataEFContext()
: base("name=PCDataEFContext")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<Customer> Customers { get; set; }
}
All the above code works great! But as I said in the beginning, I don't know how to inject the connection string into my Repositorybase class at runtime so that I don't have to modify any of my inherited repositories (I have plenty of them). Someone please help.
Babu.
Could you do it like this?
public partial class PCDataEFContext : DbContext
{
public PCDataEFContext()
: base(Util.GetTheConnectionString())
{ }
}
public class MyDerivedContext : PCDataEFContext
{
public MyDerivedContext()
: base()
{ }
}
class Util
{
public static string GetTheConnectionString()
{
// return the correct name based on some logic...
return "name=PCDataEFContext";
}
}
Another way of doing it, could be in the RepositorBase class you defined, by altering the connectionstring after the creation of the dbcontext:
protected D DataContext
{
get
{
if (dataContext == null)
{
dataContext = new D();
dataContext.Database.Connection.ConnectionString = "the new connectionstring";
}
return dataContext;
}
set { dataContext = value; }
}
I am working on crud operations in mvc 4.0 with unitofwork and generic repository with Ninject for DI.
I am able to get a particular record from a table, I am even able to get all the records from the table.
but I am not able to insert a new record in the database table. I am not getting any error/exception and it is running each statement
cleanly but there is no effect in database below is my controller where I am using the repository and unitof work.
Can somebody tell me where I am wron or what code/statements I have left in this code. I ahve checked it lot of time and I am stucked now.
Not getting the problem
Controller:
private IUnitOfWork _unitOfWork;
private IRepository<tbl_Employee> _Repo;
private IRepository<tbl_Department> _Department;
public HomeController( IUnitOfWork UOW, IRepository<tbl_Employee> Repository, IRepository<tbl_Department> Depart)
{
this._unitOfWork = UOW;
this._Repo = Repository;
this._Department = Depart;
}
//This runs successfully and gets all the records in the view page and I am displaying all records using foreach in div structure
public ActionResult Index()
{
EmployeeModel ObjModel = new EmployeeModel();
ObjModel.Employees = this._Repo.GetALL();
//ObjModel.Employees = this._Employee.GetEmployees();
return View(ObjModel);
}
//This also runs successfully and it brought me a single record on selection of particular record from employee listing.
public ActionResult EmployeeDetail(string id)
{
EmployeeDetailModel ObjModel = new EmployeeDetailModel();
if (!string.IsNullOrEmpty(id))
{
var Employee = this._Repo.Find(Convert.ToInt32(id));
if (Employee != null)
{
ObjModel.InjectFrom(Employee);
}
}
return View(ObjModel);
}
// Here is the problem . Not able to insert the record. The model object is not empty . I have checked it and there is no error.It brought me a message
"Employee Created Successfully but in database there is no record.
public ActionResult SaveEmployee(EmployeeDetailModel Model)
{
string Msg = string.Empty;
try
{
tbl_Employee ObjEmployee = new tbl_Employee();
ObjEmployee.InjectFrom(Model);
if (Model.Male)
{
ObjEmployee.Sex = "m";
}
else
{
ObjEmployee.Sex = "f";
}
ObjEmployee.Department_Id = Model.Dept_id;
ObjEmployee.Salary = Convert.ToInt32(Model.Salary);
this._Repo.Insert(ObjEmployee);
this._unitOfWork.Commit();
Msg = "Employee Created Successfully";
}
catch
{
Msg = "Error occurred while creating the employee, Please try again.";
}
return Json(new { Message = Msg });
}
/// Repository interface
public interface IRepository<T> where T : class
{
void Insert(T entity);
void Delete(T entity);
void Update(T entity);
T Find(int key);
IEnumerable<T> GetALL();
}
Repository class
public class Repository<T> : Connection, IRepository<T> where T : class
{
private readonly DbSet<T> _dbSet;
public Repository()
{
_dbSet = _dbContext.Set<T>();
}
public void Insert(T entity)
{
_dbSet.Add(entity);
}
public void Delete(T entity)
{
_dbSet.Remove(entity);
}
public void Update(T entity)
{
var updated = _dbSet.Attach(entity);
_dbContext.Entry(entity).State = EntityState.Modified;
//_dataContext.Entry(item).State = EntityState.Modified;
}
public T Find(int Key)
{
var dbResult = _dbSet.Find(Key);
return dbResult;
}
public IEnumerable<T> GetALL()
{
return _dbSet;
}
}
UnitofWork Interface
public interface IUnitOfWork : IDisposable
{
void Commit();
}
Unit of work class
public class UnitOfWork : Connection, IUnitOfWork
{
private bool _disposed;
public void Commit()
{
_dbContext.SaveChanges();
}
public void Dispose()
{
Dispose(true);
// Take yourself off the Finalization queue to prevent finalization code for object from executing a second time.
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
if (!_disposed)
{
// If disposing equals true, dispose all managed and unmanaged resources.
if (disposing)
{
// Dispose managed resources.
if (_dbContext != null)
{
_dbContext.Dispose();
}
}
}
_disposed = true;
}
}
My UnitofWork and Repository class derives from connection class where dbcontext is defined.
public abstract class Connection
{
protected db_TestEntities _dbContext;
public Connection()
{
this._dbContext = new db_TestEntities();
}
}
Is it that my dbContext is creating a new instance everytime like explained Here
and if yes then how can I resolve it.
tbl_Employee ObjEmployee = new tbl_Employee();
ObjEmployee.InjectFrom(Model);
if (Model.Male)
{
ObjEmployee.Sex = "m";
}
else
{
ObjEmployee.Sex = "f";
}
ObjEmployee.Department_Id = Model.Dept_id;
ObjEmployee.Salary = Convert.ToInt32(Model.Salary);
this._Repo.Insert(ObjEmployee);
After this, you should see your object mapped by EF in local memory.
this._unitOfWork.Commit();
Here your object should be pushed to database. dbContext.SaveChanges() return number of changed records which should be in your case 1.
Msg = "Employee Created Successfully";
Update:
So the problem is in your Connection class as you suggested.
I would create your DbContext in one place and then pass it to repository and unit of work. You could also create DbContext in unit of work constructor and then pass UOW to repository. This is one of my older implementation of this:
public class EntityFrameworkUnitOfWork : IUnitOfWork
{
private ForexDbContext dbContext;
internal ForexDbContext DbContext
{
get { return dbContext ?? (dbContext = new ForexDbContext()); }
}
internal DbSet<T> Set<T>()
where T : class
{
return DbContext.Set<T>();
}
public void Dispose()
{
if(dbContext == null) return;
dbContext.Dispose();
dbContext = null;
}
public void SaveChanges()
{
int result = DbContext.SaveChanges();
}
public ITransaction BeginTransaction()
{
return new EntityFrameworkTransaction(DbContext.BeginTransaction());
}
}
public class ContactsRepositoryWithUow : IRepository<Contact>
{
private SampleDbEntities entities = null;
public ContactsRepositoryWithUow(SampleDbEntities _entities)
{
entities = _entities;
}
public IEnumerable<Contact> GetAll(Func<Contact, bool> predicate = null)
{
if (predicate != null)
{
if (predicate != null)
{
return entities.Contacts.Where(predicate);
}
}
return entities.Contacts;
}
public Contact Get(Func<Contact, bool> predicate)
{
return entities.Contacts.FirstOrDefault(predicate);
}
public void Add(Contact entity)
{
entities.Contacts.AddObject(entity);
}
public void Attach(Contact entity)
{
entities.Contacts.Attach(entity);
entities.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);
}
public void Delete(Contact entity)
{
entities.Contacts.DeleteObject(entity);
}
}
Please find answer in below link for more details
Crud Operation with UnitOfWork
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 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.
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.