I'm using EF6. The generated code is something like:
public partial class MyDataContext : DbContext
{
public MyDataContext() : base("name=mydata")
{
}
public virtual DbSet<Book> Books { get; set; }
}
Then I have a generic repository like:
public class GenericRepository<TObject> where TObject : class
{
protected readonly MyDataContext Context;
protected GenericRepository(MyDataContext context)
{
Context = context;
}
public virtual TObject Update(TObject data, int id)
{
if (data == null)
return null;
TObject obj = Context.Set<TObject>().Find(id);
if (obj != null)
{
Context.Entry(obj).CurrentValues.SetValues(data);
Context.SaveChanges();
}
return obj;
}
}
Then I have a service that uses the GenericRepository to update data:
public class MyDataService<TObject> where TObject : class
{
private readonly MyDataContext context;
public MyDataService(MyDataContext ct)
{
context = ct;
}
public TObject Update(TObject obj, int id)
{
var r = new GenericRepository<TObject>(context);
return r.Update(obj, id);
}
}
So I can update a books with something like this:
var ds = new MyDataService<Book>(new MyDataContext());
var data = ds.Update(new Book { Name = "New Name" }, 1);
This is working fine. Next I try to use Moq to unit test the above code with something like:
var updatedBook = new Book { Name = "Update Book Name" };
var mockSet = new Mock<DbSet<Book>>();
var mockContext = new Mock<MyDataContext>();
mockContext.Setup(c => c.Books).Returns(mockSet.Object);
mockContext.Setup(c => c.Set<Book>().Find(It.IsAny<object[]>()))
.Returns<object[]>(ids => chips.FirstOrDefault(d => d.Id == (int)ids[0]));
var service = new MyDataService<Book>(mockContext.Object);
var data = service.Update(updatedBook, 1);
However, I get an exception on the Context.Entry(obj).CurrentValues.SetValues(data) line.
How do I mock the Update method properly?
You could implement an interface for MyDataService to be able to mock it
public Interface IMyDataService<TObject> where TObject : class
{
TObject Update(TObject obj, int id);
}
public class MyDataService<TObject>:IMyDataService<TObject>
where TObject : class
{
private readonly MyDataContext context;
public MyDataService(MyDataContext ct)
{
context = ct;
}
public TObject Update(TObject obj, int id)
{
var r = new GenericRepository<TObject>(context);
return r.Update(obj, id);
}
}
Moq:
var mockDataService = new Mock<IMyDataService<Book>>();
mockDataService.Setup(c=> c.Update(It.Any<Book>(),It.Any<int>()).Returns(updatedbook);
The service should be dependent on the repository. Passing the context directly to the service is misleading as what the service really needs and uses is the repository.
Your classes should depend on abstractions and not on concretions. That said, all the above classes could be abstracted behind interfaces. but for now I'll focus on the service class and it's dependence on the repository. You are coupling different layers too closely. Service layer doesn't need to know about data context
Abstract the repository to allow for easier testability
interface IGenericRepository<TObject> where TObject : class {
TObject Update(TObject data, int id);
}
public class GenericRepository<TObject> : IGenericRepository<TObject> where TObject : class {
protected readonly MyDataContext Context;
public GenericRepository(MyDataContext context) {
Context = context;
}
public virtual TObject Update(TObject data, int id) {
if (data == null)
return null;
TObject obj = Context.Set<TObject>().Find(id);
if (obj != null) {
Context.Entry(obj).CurrentValues.SetValues(data);
Context.SaveChanges();
}
return obj;
}
}
The service would only now need to know about the repository abstraction, not its implementation details.
public class MyDataService<TObject> where TObject : class {
private readonly IGenericRepository<TObject> repository;
public MyDataService(IGenericRepository<TObject> repository) {
this.repository = repository;
}
public TObject Update(TObject obj, int id) {
return repository.Update(obj, id);
}
}
So now the service can be tested in isolation without any need to worry about any data context
//Arrange
var updatedBook = new Book { Name = "Update Book Name" };
var id = 1;
var mockRepository = new Mock<IGenericRepository<Book>>();
mockRepository
.Setup(m => m.Update(updatedBook, id))
.Returns(updatedBook);
var service = new MyDataService<Book>(mockRepository.Object);
//Act
var data = service.Update(updatedBook, id);
//Assert
//...
When it's time to unit test the repository implementation in isolation, then you can follow the same structure and abstract the context for the repository implementation.
I would suggest the small refactoring in order to make the testing easier and even possible. With this implementation you are relying on the implementation of the DbContext and DbEntityEntry.
At first extract interface for your context:
public inteface IMyDataContext<TObject> where TObject is class
{
TObject FindById(int id); //call FindId
void Update(TObject); //call DbEntityEntry SetValues
void SaveChanges();
}
In the GenericRepository then inject the interface. This will make your life easier, you can then easily mock all method. Unit tests of the repository should verify that right methods of the context are called.
Related
I have a service that is setup in this way.
public Interface IDataService : IDisposable
{
IQueryable<T> Set<T>() where T : class;
IDbSet<T> WritableSet<T>() where T : class;
}
IDataService is inherited by DataService.
public abstract class DataService : IDataService
{
public IDataContext DataContext { get; private set; }
public IQueryable<T> Set<T>() where T : class
{
return DataContext.Set<T>().AsNoTracking();
}
public IDbSet<T> WritableSet<T>() where T : class
{
return DataContext.Set<T>();
}
public AddResult<T> Add<T>(T obj) where T : class, IPersistentEntity
{
if (obj == null)
return new AddResult<T>() { IsValid = false };
else
{
if (obj.Id == Guid.Empty)
WritableSet<T>().Add(obj);
bool success = DataContext.SaveChanges() > 0;
return new AddResult<T>() { Entity = obj, IsValid = success };
}
}
}
And The DataService is inherited by EntityService.
public class EntityService : DataService
{
public EntityService(IDataContext DataContext) : base(DataContext)
{
}
public void EntityStarted(Guid Id)
{
var a = GetWriteableById<Entity>(Id);
a.Status = 1;
DataContext.SaveChanges();
}
}
This EntityService is used in one of my components. EntityService's object is created and passed to the component's constructor.
I'm using Moq to perform some tests on the component and for that, the plan was to mock the EntityService such that the EntityService uses a fake db container with dummy data for database like operations. But, I'm not having the best idea to mock this with minimum amount of new code.
The least appealing idea that I have is to create a fake EntityService class using the interface and have it's own implementation suitable for tests.
Help is appreciated! :)
As per #JLe and #Chetan's comment on the question, I had to mock the DbContext.
I followed this article to mock the DbContext.
Mocking DbContext with Moq
Here is how the code looks like.
private void Setup()
{
List<Entity> entityData = new List<Entity>();
entityData.Add(new Entity
{
Id = Guid.NewGuid()
});
DbSet<Entity> MockEntitySet = GetSet(entityData);
MockContext = new Mock<IDbContext>();
MockContext.Setup(m => m.Set<Entity>()).Returns(MockEntitySet);
}
public static DbSet<T> GetSet<T>(List<T> sourceList) where T : class
{
return GetSet(sourceList.ToArray());
}
public static DbSet<T> GetSet<T>(T[] sourceList) where T : class
{
var name = typeof(T).Name;
var queryable = sourceList.AsQueryable();
Mock<DbSet<T>> dbSet = new Mock<DbSet<T>>();
dbSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(queryable.Provider);
dbSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression);
dbSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
dbSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(queryable.GetEnumerator());
dbSet.Setup(m => m.AsNoTracking()).Returns(dbSet.Object);
return dbSet.Object;
}
[TestMethod]
public void Test()
{
EntityService service = new EntityService(MockContext.Object);
ComponentToTest compObj = new ComponentToTest(service);
compObj.MethodToTest(...);
// Assertions
}
Thank you guys! Appreciate your help and suggestions.
To mock you need an Interface, if not, you need to mark the methods that you want to mock as virtual.
Under the hood the mocking framework is going to create a new implementation for you that behaves as you configured the mock.
Hope it helps.
I have been trying to implement the repository pattern in .Net Core using a generic class. This is what i have come up with (ive taken out all but one method to keep it simple). The class / method works, but im trying to write a unit(integration) test for it, in this case the Add Method.
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
protected readonly DbContext Context;
public Repository(DbContext context)
{
Context = context;
}
public void Add(TEntity entity)
{
Context.Set<TEntity>().Add(entity);
}
}
So far my integration test is as follows,
Set Up makes a SqlLite database to save to
Sets up the DbContext
Sets up a Unit of Work - The unit of work is needed to SaveChanges the the DbContext and can be ignored for the most part
Then im creating a "StorageSystem" Domain Object and testing the Generic class. But it feels like I should be able to test it with out passing a specific domain model. Or at the very least, enter the different domain models as parameterised tests.
[TestFixture]
public class RepositoryTests
{
SqliteConnection _connection;
DbContextOptions<ApplicationDbContext> _options;
ApplicationDbContext _context;
UnitOfWork _uow;
[SetUp]
public void SetUp()
{
_connection = new SqliteConnection("DataSource=:memory:");
_connection.Open();
_options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseSqlite(_connection)
.Options;
using (var context = new ApplicationDbContext(_options))
{
context.Database.EnsureCreated();
}
_context = new ApplicationDbContext(_options);
_uow = new UnitOfWork(_context);
}
[TearDown]
public void TearDown()
{
_connection.Close();
}
[Test]
public void Add_AddsEntityToRepository()
{
//arrange
var storageSystem = new StorageSystem {Id = 1, Name = "Storage1"};
var repo = new Repository<StorageSystem>(_context);
//act
repo.Add(storageSystem);
_uow.Complete();
//assert
Assert.AreEqual(1, _context.StorageSystems.Count());
}
I am fairly new to working with Generics, The closest solution I could find was using an abstract class. However I couldnt make it working with me code with it not detecting the tests as an abstract class and not being able to make a repository of type TEntity.
Example taken from here
[TestFixture]
public abstract class RepositoryTests1<TEntity>
{
SqliteConnection _connection;
DbContextOptions<ApplicationDbContext> _options;
ApplicationDbContext _context;
UnitOfWork _uow;
[SetUp]
public void SetUp()
{
_connection = new SqliteConnection("DataSource=:memory:");
_connection.Open();
_options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseSqlite(_connection)
.Options;
using (var context = new ApplicationDbContext(_options))
{
context.Database.EnsureCreated();
}
_context = new ApplicationDbContext(_options);
_uow = new UnitOfWork(_context);
}
[TearDown]
public void TearDown()
{
_connection.Close();
}
[Test]
public void Add_AddsEntityToRepository_GenericAttempt()
{
//arrange
TEntity entityToAdd = this.CreateEntity();
var repo = new Repository<TEntity>(_context); //ERROR HERE - TEntity must be a reference type
//act
repo.Add(entityToAdd);
_uow.Complete();
//assert
//NO IDEA WHAT THE ASSERTION WOULD BE
}
protected abstract TEntity CreateEntity();
}
So in short, how do i unit test this Generic Repository?
You could Restrict your Repository to a certain base class created by you like EntityBase (this should be abstract)
public class EntityBase
{
public int Id { get; set; }
}
public class Repository<TEntity> : IRepository<TEntity> where TEntity : EntityBase
{
...
}
Then you are able to pass your base type to this test methods.
[Test]
public void Add_AddsEntityToRepository()
{
//arrange
var storageSystem = new StorageSystem {Id = 1, Name = "Storage1"};
var repo = new Repository<EntityBase>(_context);
//act
repo.Add(storageSystem);
_uow.Complete();
//assert
Assert.AreEqual(1, _context.StorageSystems.Count());
}
Since you are ensuring through inherintance, that every type has at least the members of EntityBase, you do not have to test further more down the inheritance tree of your entity types. (unless you have a certain use-case for that)
If you certain methods, that you need to know on the base-type, but the implementation depends on the child class, simply put the method as abstract member in the base class and override it in the child class.
public class EntityBase
{
public int Id { get; set; }
// sample method that copies member values from other object to current instance
public abstract void CopyProperties(EntityBase other);
}
public class Student : EntityBase
{
public int Id { get; set; }
public override void CopyProperties(EntityBase other)
{
...
}
}
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 am trying to Mock Entity Framework 6.0.2 Async methods using JustMock. I am following testing with async queries but it is write using Moq I am trying to convert this into JustMock with help of Mock Multiple Interfaces but getting an exception :
The provider for the source IQueryable doesn't implement
IDbAsyncQueryProvider. Only providers that implement
IDbAsyncQueryProvider can be used for Entity Framework asynchronous
operations. For more details see
http://go.microsoft.com/fwlink/?LinkId=287068.
Here is my code :
var dummyData = GetEmployeeSkills();
var mockSet = Mock.Create<DbSet<EmployeeSkill>>();
(mockSet as IDbAsyncEnumerable<EmployeeSkill>).Arrange(x => x.GetAsyncEnumerator())
.Returns(new TestDbAsyncEnumerator<EmployeeSkill>(dummyData.GetEnumerator()));
(mockSet as IQueryable<EmployeeSkill>).Arrange(x => x.Provider).Returns(new TestDbAsyncQueryProvider<EmployeeSkill>(dummyData.Provider));
(mockSet as IQueryable<EmployeeSkill>).Arrange(x => x.Expression).Returns(dummyData.Expression);
(mockSet as IQueryable<EmployeeSkill>).Arrange(x => x.ElementType).Returns(dummyData.ElementType);
(mockSet as IQueryable<EmployeeSkill>).Arrange(x => x.GetEnumerator()).Returns(dummyData.GetEnumerator());
var mockContext = Mock.Create<TimeSketchContext>();
mockContext.Arrange(x => x.Set<EmployeeSkill>()).Returns(mockSet);
baseRepository = new BaseRepository<EmployeeSkill>(mockContext);
private EmployeeSkill GetEmployeeSkill()
{
return new EmployeeSkill
{
SkillDescription = "SkillDescription",
SkillName = "SkillName",
Id = 1
};
}
private IQueryable<EmployeeSkill> GetEmployeeSkills()
{
return new List<EmployeeSkill>
{
GetEmployeeSkill(),
GetEmployeeSkill(),
GetEmployeeSkill(),
}.AsQueryable();
}
Test :
[Fact]
public async Task DbTest()
{
var data = await baseRepository.FindAsync(1);
Assert.NotEqual(null, data);
}
Repository :
public class BaseRepository<T> : IRepositoryBase<T> where T : class, IEntity, new()
{
protected readonly DbContext InnerDbContext;
protected DbSet<T> InnerDbSet;
public BaseRepository(IDbContext innerDbContext)
{
InnerDbContext = innerDbContext as DbContext;
InnerDbSet = innerDbContext.Set<T>();
}
public virtual Task<T> FindAsync(long id)
{
return InnerDbSet.FirstOrDefaultAsync(x=>x.Id == id);
}
}
Interface :
public interface IDbContext
{
DbSet<T> Set<T>() where T : class;
}
Context :
public class TimeSketchContext : DbContext, IDbContext
{
public virtual DbSet<EmployeeSkill> EmployeeSkill { get; set; }
}
Because JustMock can mock non virtual methods when you are writing
var mockContext = Mock.Create<TimeSketchContext>();
mockContext.Arrange(x => x.Set<EmployeeSkill>()).Returns(mockSet);
it will mock the DbContext.Set<> and not your IDbContext.Set<> so you get the exception.
There are at least 2 solution to this:
Mock your IDbContext interface
var mockContext = Mock.Create<IDbContext>();
Or change back your BaseRepository to use a DbContext instead of your interface:
public class BaseRepository<T> : IRepositoryBase<T> where T : class, IEntity, new()
{
protected readonly DbContext InnerDbContext;
protected DbSet<T> InnerDbSet;
public BaseRepository(DbContext innerDbContext)
{
InnerDbContext = innerDbContext;
InnerDbSet = InnerDbContext.Set<T>();
}
public virtual Task<T> FindAsync(long id)
{
return InnerDbSet.FirstOrDefaultAsync(x => x.Id == id);
}
}
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.