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.
Related
I am wondering, how we can handle a situation, and if I'm going about this the wrong way. We wrote a web application based on one ERP's database schema model. We are using Entity Framework for the system, with the regular dependency injection method.
Now that we have purchased multiple ERP's from other labs, we are trying to allow their data be used on our first original web portal built for our original ERP. As you can imagine, this is rough as the database models and design will not line up with ours. For example something like GetAllAssets() stored procedure returns the complex type and then is bound to the repo level, the service level and onto the view.
What I was thinking is that we could maybe add multiple EDMX (context for databases), then keep everything from the Views to the controllers to the service layer the same. At the service layer, and type params to our classes, and so we could pass the context the current user logged in as, and then in auto mapper add new entries for the new database context calls from Entity Framework, to map to our original code from the service back down to the view.
Is this possible, and or a good idea or bad idea?
Here is a example of a current basic controller we are using with DI style, and I have removed a lot of code for this question's example:
[AuthorizeWithSession]
public class LocationController : Controller
{
private readonly IMProAssetLocationService _mProAssetLocationService;
private readonly IUIDataService _uiDataService;
public LocationController(IMProAssetLocationService mProAssetLocationService,
IUIDataService uiDataService)
{
_mProAssetLocationService = mProAssetLocationService;
}
public ActionResult List()
{
return View();
}
public ActionResult List2()
{
return View();
}
public ActionResult GetLocationList([DataSourceRequest]DataSourceRequest request)
{
//var result = DepartmentService.GetDepartmentList(SessionHelper.GetCustId());
var result = _mProAssetLocationService.MProAssetLocationGetLocationByCustID(SessionHelper.GetCustId(), null);
if (result != null && result.Any())
{
return Json(result.ToDataSourceResult(request), JsonRequestBehavior.AllowGet);
}
}
}
The interface service layer:
public interface IMProAssetLocationService
{
IEnumerable<LocationVm> MProAssetLocationGetLocationByCustID(string custId,string id);
string MProAssetLocationInsertLocation(LocationVm vm);
void MProAssetLocationDeleteLocationByCustIDAndLocationID(string custId, string locationId);
void MProAssetLocationUpdateLocationByCustIDAndLocationID(LocationVm vm);
}
The service layer:
public class MProAssetLocationService : LogManager, IMProAssetLocationService
{
private readonly IMProAssetLocationRepo _mProAssetLocationRepo;
public MProAssetLocationService(IMProAssetLocationRepo mProAssetLocationRepo)
{
_mProAssetLocationRepo = mProAssetLocationRepo;
}
protected override Type LogPrefix
{
get { return this.GetType(); }
}
public IEnumerable<LocationVm> MProAssetLocationGetLocationByCustID(string custId, string id)
{
List<LocationVm> listlocationVm = new List<LocationVm>();
try
{
var records = _mProAssetLocationRepo.MProAssetLocationGetLocationByCustID(custId,id);
}
}
The Interface repo layer:
public interface IMProAssetLocationRepo : IRepository<MProAssetLocation>
{
IEnumerable<string> GetMProAssetLocatonByCustId(string custId);
IEnumerable<string> GetMProAssetLocatonDescriptionByCustId(string custId);
IEnumerable<LocationView> GetMProAssetLocatonListByCustId(string search, string locationID, string custId);
IEnumerable<LocationView> MProAssetLocationGetLocationByCustID(string custId, string id);
string MProAssetLocationInsertLocation(LocationView lv);
void MProAssetLocationDeleteLocationByCustIDAndLocationID(string custId, string locationId);
void MProAssetLocationUpdateLocationByCustIDAndLoacationID(LocationView lv);
}
The repo layer:
public class CalLocationsRepo : RepositoryBase<CalLocaton>, ICalLocationsRepo
{
public CalLocationsRepo(IDbFactory dbFactory)
: base(dbFactory)
{
}
//WHERE CalCodeActive=1 AND CalCodeGroup='OSS' ORDER BY CalCode
public IEnumerable<string> GetCalLocations(string empID)
{
return DbContext.TAM_GetCalLocationsList(empID).ToList();
}
}
I was thinking of something like using a context type in our system. I know the entity models calls would be named differently based on our different databases using their own stored procedures, but thought at the level prior to the call i check the context to use, then make the call accordingly like so:
public class DBContextRepo<T> : RepositoryBase, IDBContextRepo<T>
{
DBContextRepo<T> _typeParameterClass;
public DBContextRepo(IDbFactory dbFactory, DBContextRepo<T> typeParameterClass)
: base(dbFactory)
{
_typeParameterClass = typeParameterClass;
}
public List<string> GetAllModelsByManufcaturer(string manufacturerName)
{
List<string> results = new List<string>();
if (_typeParameterClass.GetType() == typeof(TAM.DataLayer.EntityModels.QuoteWerks1Entities))
{
using(var dbContext = DbContextQw)
{
var items = dbContext.Products_OurProducts_Products.Where(p => p.Manufacturer == manufacturerName).ToList();
results = items.Select(p => p.ManufacturerPartNumber).ToList();
}
}
else
{
using (var dbContext = DbContext)
{
var items = dbContext.Models.Where(a => a.Manufacturer.MfrName == manufacturerName);
results = items.Select(m => m.ModelNumber).ToList();
}
}
return results;
}
}
This causes errors and is where im not sure how to handle two DBContext:
public class DbFactory : Disposable, IDbFactory
{
private TAMModel _dbContext;
private QuoteWerks1Entities _dbContextQW;
public TAMModel Init()
{
return _dbContext ?? (_dbContext = new TAMModel());
}
public QuoteWerks1Entities InitQW()
{
return _dbContextQW ?? (_dbContextQW = new QuoteWerks1Entities());
}
protected override void DisposeCore()
{
if (_dbContext != null)
{
_dbContext.Dispose();
}
if (_dbContextQW != null)
{
_dbContextQW.Dispose();
}
}
}
Once i added the second context, all of my regular code say they do not have a corresponding type in constructors such as this one:
public class ContractRepo : RepositoryBase<Contract>, IContractRepo
{
public ContractRepo(IDbFactory dbFactory)
: base(dbFactory)
{
}
public string GetContractIdentifyByCustId(string custId)
{
return DbContext.TAM_GetContractIdentifyByCustId(custId).SingleOrDefault();
}
}
Here is the BaseRepo class:
public class RepositoryBase
{
private readonly TAMModel _dataContext;
private readonly QuoteWerks1Entities _dataContextQW;
protected IDbFactory DbFactory { get; private set; }
protected TAMModel DbContext
{
get
{
return _dataContext ?? DbFactory.Init();
}
}
protected QuoteWerks1Entities DbContextQw
{
get
{
return _dataContextQW ?? DbFactory.InitQW();
}
}
protected RepositoryBase(IDbFactory dbFactory)
{
DbFactory = dbFactory;
}
}
public abstract class RepositoryBase<T> where T : class
{
private readonly TAMModel _dataContext;
private readonly IDbSet<T> _dbSet;
private readonly IDbSet<T> _dbSetQW;
private readonly QuoteWerks1Entities _dataContextQW;
protected IDbFactory DbFactory { get; private set; }
protected TAMModel DbContext
{
get
{
return _dataContext ?? DbFactory.Init();
}
}
protected QuoteWerks1Entities DbContextQW
{
get
{
return _dataContextQW ?? DbFactory.InitQW();
}
}
protected RepositoryBase(IDbFactory dbFactory, T type)
{
DbFactory = dbFactory;
_dbSet = DbContext.Set<T>();
_dbSetQW = DbContextQW.Set<T>();
}
public virtual void Add(T entity)
{
_dbSet.Add(entity);
}
public virtual void Update(T entity)
{
_dbSet.Attach(entity);
DbContext.Entry(entity).State = EntityState.Modified;
}
public virtual void Delete(T entity)
{
_dbSet.Remove(entity);
}
public virtual void Delete(Expression<Func<T, bool>> where)
{
IEnumerable<T> objects = _dbSet.Where<T>(where);
foreach (T obj in objects)
{
_dbSet.Remove(obj);
}
}
public virtual T GetById(int id)
{
return _dbSet.Find(id);
}
public virtual T GetById(string id)
{
return _dbSet.Find(id);
}
public virtual IEnumerable<T> GetAll()
{
return _dbSet.ToList();
}
public virtual IEnumerable<T> GetMany(Expression<Func<T, bool>> where)
{
return _dbSet.Where(where).ToList();
}
public T Get(Expression<Func<T, bool>> where)
{
return _dbSet.Where(where).SingleOrDefault();
}
public virtual IQueryable<T> Query()
{
return _dbSet;
}
public virtual IQueryable<T> Query(Expression<Func<T, bool>> where)
{
return _dbSet.Where(where);
}
public virtual ObjectQuery<U> CreateQuery<U>(string query, ObjectParameter[] parameters)
{
return CastAsObjectContext().CreateQuery<U>(query, parameters);
}
public virtual ObjectQuery<U> CreateQuery<U>(string query)
{
return CreateQuery<U>(query, new ObjectParameter[0] { });
}
public virtual ObjectQuery<DbDataRecord> CreateQuery(string query, ObjectParameter[] parameters)
{
return CreateQuery<DbDataRecord>(query, parameters);
}
public virtual ObjectQuery<DbDataRecord> CreateQuery(string query)
{
return CreateQuery<DbDataRecord>(query);
}
private ObjectContext CastAsObjectContext()
{
var oContext = (DbContext as IObjectContextAdapter).ObjectContext;
return oContext;
}
}
The only problem is, how do I set the context on a login when using dependency injection? Will this work, as at the moment I'm still in the process, or am I over complicating it and going about it the wrong way? Thanks in advance.
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 have a generic Repository like this:
public interface IRepository<TEntity> where TEntity :class
{
IEnumerable<TEntity> SearchFor(Expression<Func<TEntity, bool>> filter);
TEntity GetById(int id);
void Insert(TEntity entity);
void Delete(TEntity entity);
void Update(TEntity entity);
}
public class GenericRepository<TEntity> : IRepository<TEntity> where TEntity: class
{
private DbSet<TEntity> _dbSet; // put the entity specified in place of TEntity in d DbSet so i can query the entity e.g School Entity
private NaijaSchoolsContext _naijaSchoolsContext;
public GenericRepository(NaijaSchoolsContext context)
{
_naijaSchoolsContext = context;
_dbSet = _naijaSchoolsContext.Set<TEntity>(); //return the entity specified in the TEntity and put it in DbSet
}
public IEnumerable<TEntity> SearchFor(System.Linq.Expressions.Expression<Func<TEntity, bool>> filter)
{
return _dbSet.Where(filter);
}
public TEntity GetById(int id)
{
return _dbSet.Find(id);
}
public void Insert(TEntity entity)
{
_dbSet.Add(entity);
}
public void Delete(TEntity entity)
{
_dbSet.Remove(entity);
}
public void Update(TEntity entity)
{
_dbSet.AddOrUpdate(entity);
}
}
I also have a UoW like this:
public interface IUnitofWork : IDisposable
{
void Save();
}
public class UnitofWork : IUnitofWork
{
NaijaSchoolsContext naijaSchoolsContext = new NaijaSchoolsContext();
private GenericRepository<School> schoolRepository;
private bool isDisposed = false;
public GenericRepository<School> SchoolRepository
{
get
{
if (schoolRepository == null)
{
schoolRepository = new GenericRepository<School>(naijaSchoolsContext);
}
return schoolRepository;
}
}
public void Save()
{
naijaSchoolsContext.SaveChanges();
}
public void Dispose(bool disposing)
{
if (!isDisposed)
{
if (disposing)
{
naijaSchoolsContext.Dispose();
}
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
My test class looks like this:
[TestFixture]
public class when_working_with_school_repository
{
}
public class and_saving_a_school : when_working_with_school_repository
{
private School _returnedSchool;
private School _school;
private Mock<IRepository<School>> _repository;
private Exception _result;
[SetUp]
private void SetUp()
{
_repository = new Mock<IRepository<School>>();
_school = new School();
}
[Test]
public void then_a_valid_school_should_be_saved()
{
_repository.Setup(s => s.Insert(_school));
//_returnedSchool = _schoolRepository.Save(_school);
}
[Test]
public void should_throw_an_exception_when_no_school_is_saved()
{
try
{
_repository.Setup(s => s.Insert(null));
}
catch (Exception exception)
{
_result = exception;
}
}
[Test]
public void should_notify_user_if_school_name_already_exists()
{
//bool exists = _schoolRepository.IsExist(_school.Name);
}
}
My tests passes but my concern is that
Am I not supposed to mock the UnitOfWork class. When I tried mocking it, i couldn't get to the StudentRepository class. In using the code without tests, I would have to instantiate UoW to perform my actions, that is why i asked if am supposed to Mock my UoW. if am to Mock it how can i do that?
Please help me if my test is correct or I need to take another course of action.
No, you are not. You are testing mock:
_repository = new Mock<IRepository<School>>();
You want to test your code, not others. Your generic repository simply delegates calls to _dbSet. And that's what you want to test - that calls are delegated (this is sort of wrapper-functionality).
How to do that? You need abstraction over DbSet<T> and this is the object you mock in test. All your tests will look similar:
var dbSetMock = new Mock<DbSet<School>>();
var context = new Mock<Context>();
var repository = new GenericRepository<School>(dbSetMock, context);
repository.FindBy(arg);
dbSetMock.Verify(d => d.FindBy(arg));
This requires abstraction over both DbSet and your custom context to make it work.
I think you're better of mocking the IRepository. But, your test will pass because your not asserting for anything.
I think you need to setup your your mock inorder to get the desired result. Here is a modified example:
[Test]
public void then_a_valid_school_should_be_saved()
{
var _school = new School { .... };
var expected = _school.Id;
_repository.Setup(s => s.Insert(_school));
_repository.Setup(s => s.GetById(_school.Id)).Returns(_school);;
_repository.Insert(_school);
var actual = _repository.GetById(_school.Id);
Assert.Equal(expected, actual);
}
That should do it. To make the test fail try putting a different Id for the expected result and verify that it works. You can use that to improve upon your other test.
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.