How to use AsNoTracking in a service layer - c#

I usually use AsNoTracking when I'm not intending to write anything. How should I handle this in my service layer where dbContext is hidden behind it? (I treat EF core as repository because it is repository)
public class SomeService
{
//...
public SomeEntity GetById(int id)
{
return _dbContext.Find(id);
}
public SomeEntity GetReadonlyById(int id)
{
return _dbContext.SomeEntitities.AsNoTracking().SingleOrDefault(e => e.Id == id);
}
public SomeEntity Update(SomeEntity someEntity)
{
_dbContext.Update(someEntity);
_dbContext.SaveChanges();
}
}
public class SomeController
{
private readonly SomeService _someService;
//....
[HttpGet("{id}")]
public IActionResult Get(int id)
{
var someEntity = _someService.GetReadonlyById(id);
if (someEntity == null)
{
return NotFound();
}
return someEntity;
}
[HttpPut("{id}")]
public IActionResult Modify(int id, SomeEntity modified)
{
var someEntity = _someService.GetById(id);
if (someEntity == null)
{
return NotFound();
}
someEntity.Someproperty = modified.Someproperty;
_someService.Update(someEntity);
return Ok(someEntity);
}
}
Is there any better way to do this?
I can also define my service as follows:
public class SomeService
{
//...
public SomeEntity GetById(int id)
{
return _dbContext.AsNoTracking.SingleOrDefault(e => e.Id == id);
}
public SomeEntity Update(int id, SomeEntity someEntity)
{
var entity = _dbContext.SomeEntities.Find(id);
if (entity == null)
{
return null;
}
entity.Someproperty = someEntity.Someproperty;
_dbContext.Update(entity);
_dbContext.SaveChanges();
return entity;
}
}
public class SomeController
{
private readonly SomeService _someService;
//....
[HttpGet("{id}")]
public IActionResult Get(int id)
{
var someEntity = _someService.GetById(id);
if (someEntity == null)
{
return NotFound();
}
return someEntity;
}
[HttpPut("{id}")]
public IActionResult Modify(int id, SomeEntity modified)
{
var someEntity = _someService.Update(id, modified);
if (someEntity == null)
{
return NotFound();
}
return Ok(someEntity);
}
}
What is the better way?

Basically, it is more common problem.
It is often happens that optimized reading methods are not convenient for updating scenarios and convenient reading methods for updating scenarios have unnecessary overhead for reading only scenarios. I see 3 options here:
Ignoring all problems with performance and just use universal GetById from your first approach for reads and updates. Obviously, it is applicable for simple applications and may not be applicable for high-load applications.
Using CQRS. It means you will have completely separate data model for reads and updates. Since reads usually don't require the complex domain logic it allows you to use any optimizations like AsNoTracking method or even use a plain sql in repositories. It is applicable for complex apps and requires more code.
Trying to find some compromise between these two options according to your particular needs.
As noted in comments your SomeService looks like repository. Ideally, domain service should contain only business logic and shouldn't mix it with infrastructure features like AsNoTracking. Whereas repositories can and should contain infrastructure features like AsNoTracking, Include and etc.

No tracking queries are useful when the results are used in a read-only scenario. They are quicker to execute because there is no need to setup change tracking information.
You can swap an individual query to be no-tracking:
using (var context = new BloggingContext())
{
var blogs = context.Blogs
.AsNoTracking()
.ToList();
}
You can also change the default tracking behavior at the context instance level:
using (var context = new BloggingContext())
{
context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
var blogs = context.Blogs.ToList();
}
Ideally, you should manage the infrastructure stuff in the repository level.

Here is my solution. It works.
BASE CLASS OF ALL SERVICES
public class ServicesBase
{
protected AppDbContext dbcontext { get; }
public ServicesBase(AppDbContext dbcontext)
{
this.dbcontext = dbcontext;
}
public void AsNoTracking()
{
dbcontext.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
}
public void SaveChanges()
{
dbcontext.SaveChanges();
}
}
USING IN CONTROLLER
_masterService.AsNoTracking();
var master = _masterService.GetById(1);
master.WagePerSquareMeter = 21;
_masterService.SaveChanges();
//or dbcontext.SaveChanges(), both of them will ot affect the database.

Related

Mocking EF Core Database Context using Moq and xUnit?

I want to know how to use Moq for mocking my EF Core's DbContext when I am using DI to provide my Database context to the controller as shown below:
public class RegisterController : Controller
{
private AppDbContext context;
public RegisterController(AppDbContext appDbContext)
{
context = appDbContext;
}
public IActionResult Create()
{
return View();
}
[HttpPost]
public async Task<IActionResult> Create(Register register)
{
if (ModelState.IsValid)
{
context.Add(register);
await context.SaveChangesAsync();
return RedirectToAction("Read");
}
else
return View();
}
}
Here AppDbContext is my database context for the EF core.
I want to write test case for the Create action. I have tried the below code:
[Fact]
public async Task Test_Create_POST_ValidModelState()
{
// Arrange
var r = new Register()
{
Id = 4,
Name = "Test Four",
Age = 59
};
var mockRepo = new Mock<AppDbContext>();
mockRepo.Setup(repo => repo.CreateAsync(It.IsAny<Register>()))
.Returns(Task.CompletedTask)
.Verifiable();
var controller = new RegisterController(mockRepo.Object);
// Act
var result = await controller.Create(r);
// Assert
var redirectToActionResult = Assert.IsType<RedirectToActionResult>(result);
Assert.Null(redirectToActionResult.ControllerName);
Assert.Equal("Read", redirectToActionResult.ActionName);
mockRepo.Verify();
}
The main problem here is that I cannot do:
var mockRepo = new Mock<AppDbContext>();
I want to follow this approach as I have already added in-memory database.
Note that I know there is a another way to test with repository pattern. Which can be done by changing the create action as:
public class RegisterController : Controller
{
private IRegisterRepository context;
public RegisterController(IRegisterRepository appDbContext)
{
context = appDbContext;
}
public IActionResult Create()
{
return View();
}
[HttpPost]
public async Task<IActionResult> Create(Register register)
{
if (ModelState.IsValid)
{
await context.CreateAsync(register);
return RedirectToAction("Read");
}
else
return View();
}
}
Where the interface IRegisterRepository and RegisterRepository.cs codes are:
public interface IRegisterRepository
{
Task CreateAsync(Register register);
}
public class RegisterRepository : IRegisterRepository
{
private readonly AppDbContext context;
public RegisterRepository(AppDbContext dbContext)
{
context = dbContext;
}
public Task CreateAsync(Register register)
{
context.Register.Add(register);
return context.SaveChangesAsync();
}
}
and the startup.cs code which add it as a service is:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<AppDbContext>(optionsBuilder => optionsBuilder.UseInMemoryDatabase("InMemoryDb"));
services.AddScoped<IRegisterRepository, RegisterRepository>();
services.AddControllersWithViews();
}
I do not want to follow this approach (the repository pattern). I want to fake database context directly because I want to directly do the insertion of the record in the create action as the controller is getting database context object in it's constructor.
So how to write fake database context using moq in this test methods here?
Don't mock DbContext, because tests with mocking dbContext will not provide good quality feedback for developer.
Mocked DbContext will verify only that some method are called, which will convert code maintenance (refactoring) into a nightmare.
Instead use In-Memory provider or Sqlite provider with "in-memory" feature.
EF Core In-Memory Database Provider
Using SQLite to test an EF Core application
Alternative approach is to test against actual database - such tests will provide more confidence that application works correctly in "production" environment.
There are several really useful libraries which does support what you are looking for.
Here are two of my favourite ones:
EntityFrameworkCore3Mock
Github repo
The only prerequisite is that you have to define your DbSet as virtual.
public class AppDbContext: DbContext
{
public virtual DbSet<Entity> Entities { get; set; }
}
Then the mocking would be this easy:
var initialEntities = new[]
{
new Entity { ... },
new Entity { ... },
};
var dbContextMock = new DbContextMock<AppDbContext>(DummyOptions);
var usersDbSetMock = dbContextMock.CreateDbSetMock(x => x.Entities, initialEntities);
EntityFrameworkCore.Testing
Github repo
The only difference here is how you initialize the table with data:
var initialEntities = new[]
{
new Entity { ... },
new Entity { ... },
};
var dbContextMock = Create.MockedDbContextFor<AppDbContext>();
dbContextMock.Set<Entity>().AddRange(initialEntities);
dbContextMock.SaveChanges();

Repository does not initialize entity data

I would like to create a Details view with entity framework data using a repository pattern.
This is my interface repository:
public interface InterfaceRepositroy: IDisposable
{
IEnumerable<SubjectContent> GetAll();
SubjectContent Get(string id);
}
This is the toher repository:
public class SubjectRepository : InterfaceRepositroy,IDisposable
{
private irfwebpage20161013070934_dbEntities2 db;
public IEnumerable<SubjectContent> GetAll()
{
return db.Set<SubjectContent>().ToList();
}
public SubjectContent Get(string id)
{
return db.Set<SubjectContent>().Find(id);
}
public void Dispose()
{
throw new NotImplementedException();
}
}
Here is my controller:
private InterfaceRepositroy subjectreposi;
public ActionResult Details(string id)
{
SubjectContent subject = subjectreposi.Get(id);
return View(subject);
}
My View is a standard details template.
It gets an error at this point in the controller:
SubjectContent subject = subjectreposi.Get(id);
I would really appreciate the help. This is like the fourth version of a repository pattern i am trying to implement but none of them worked so far. I have tried it without interface, with the instance of the subjecrepository or with different linq to sql in the repository. It either gets http error or it doesnt show the data just the names of the data.
Create constructors that initialise your data context:
public SubjectRepository()
{
db = new irfwebpage20161013070934_dbEntities2();
}
public SubjectRepository(irfwebpage20161013070934_dbEntities2 dbContext)
{
db = dbContext;
}
This allows you to either initialise your repository with no parameters which will initialise you data context or specify your own data context.
You can now use this like this:
var repo = new SubjectRepository();
SubjectContent subject = repo.Get(id);

FindAsync and Include LINQ statements

The code I have got so far works fine
public async Task<ActionResult> Details(Guid? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
ItemDetailModel model = new ItemDetailModel();
model.Item = await db.Items.FindAsync(id);
if (model.Item == null)
{
return HttpNotFound();
}
return View(model);
}
But I want to include 1 table more and cannot use FindAsync
public async Task<ActionResult> Details(Guid? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
ItemDetailModel model = new ItemDetailModel();
model.Item = await db.Items.Include(i=>i.ItemVerifications).FindAsync(id);
if (model.Item == null)
{
return HttpNotFound();
}
return View(model);
}
So I am facing this error
Severity Code Description Project File Line Suppression State
Error CS1061 'IQueryable' does not contain a definition for
'FindAsync' and no extension method 'FindAsync' accepting a first
argument of type 'IQueryable' could be found (are you missing a
using directive or an assembly reference?)
Any clue how to fix it?
The simplest is to use FirstOrDefaultAsync or SingleOrDefaultAsync instead:
model.Item = await db.Items.Include(i => i.ItemVerifications)
.FirstOrDefaultAsync(i => i.Id == id.Value);
The reason you are getting the error is because Find / FindAsync methods are defined for DbSet<T>, but the result of Include is IQueryable<T>.
Another way is to combine FindAsync with explicit loading:
model.Item = await db.Items.FindAsync(id);
if (model.Item == null)
{
return HttpNotFound();
}
await db.Entry(model.Item).Collection(i => i.ItemVerifications).LoadAsync();
If you are using a generic repository and you don't know the PK at runtime, this approach can help:
public interface IGenericRepository<TEntity> where TEntity : class
{
Task<TEntity> Get(int id, string[] paths = null);
}
public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
private readonly ApplicationDbContext _context;
private readonly DbSet<TEntity> _dbSet;
public GenericRepository(ApplicationDbContext context)
{
_context = context;
_dbSet = _context.Set<TEntity>();
}
public async Task<TEntity> Get(int id, string[] paths = null)
{
var model = await _dbSet.FindAsync(id);
foreach (var path in paths)
{
_context.Entry(model).Reference(path).Load();
}
return model;
}
}
When you program using solid principles and domain design then use generics. The Repository pattern uses a generic class. I pass a lambda express to the GetObjectsQueryable function. I have setup lazy loading to be on, using code first handle bars. However, I am moving away from lazy loading and implement a microservice architecture. The include table is a string and you can use the nameof(xxclass) function to ensure the correct name. The function returns and IQueryable results. The repository class methods can be used by its derived class enhance the method is protected. This is a dotnet.core demonstration.
public class Repository
where T : class
{
public IQueryable<T> GetObjectsQueryable(Expression<Func<T, bool>> predicate, string includeTable="")
{
IQueryable<T> result = _dbContext.Set<T>().Where(predicate);
if (includeTable != "")
result = result.Include(includeTable);
return result;
}
}

DI and repository pattern

Currently, my code is similar to this (shortened just to make a point):
DAL
Repository Interface
public interface IRepository<TEntity, in TKey>
{
IList<TEntity> GetAll();
TEntity Get(TKey id);
TEntity Add(TEntity item);
TEntity Update(TEntity item);
bool Remove(TKey id);
}
Base EF repository
public class BaseEFRepository<TEntity, TKey> : IRepository<TEntity, TKey> where TEntity: class, IEntity<TKey> where TKey: struct
{
protected readonly DbContext _dbContext;
public BaseRepository()
{
_dbContext = new MyDB();
_dbContext.Configuration.ProxyCreationEnabled = false;
_dbContext.Configuration.LazyLoadingEnabled = false;
}
public virtual TEntity Get(TKey id)
{
return _dbContext.Set<TEntity>().Find(id);
}
public virtual IList<TEntity> GetAll()
{
return _dbContext.Set<TEntity>()
.ToList();
}
public virtual TEntity Add(TEntity item)
{
_dbContext.Set<TEntity>().Add(item);
_dbContext.SaveChanges();
return item;
}
.....
.....
}
A sample implementation of the base repository
public interface IContactsRepository : IRepository<Contact, long>
{
Contact GetByEmployeeId(string empId, ContactType type);
IList<Contact> GetByEmployeeId(string empId);
}
public class ContactsRepository : BaseEFRepository<Contact, long>, IContactsRepository
{
public Contact GetByEmployeeId(string empId, ContactType type)
{
var contact = _dbContext.Set<Contact>()
.FirstOrDefault(d => d.EmployeeId == empId && d.ContactType == type);
return contact;
}
public IList<Contact> GetByEmployeeId(string empId)
{
var contacts = _dbContext.Set<Contact>()
.Where(d => d.EmployeeId == empId)
.ToList();
return contacts;
}
}
BLL
public class Contacts
{
public Contact Get(long id)
{
IContactsRepository repo = ResolveRepository<IContactsRepository>();
var contact = repo.Get(id);
return contact;
}
public Contact GetByEmployeeId(string empId, ContactType type)
{
IContactsRepository repo = ResolveRepository<IContactsRepository>();
return repo.GetByEmployeeId(empId, type);
}
.......
.......
}
Now, everything is fine. I can simply do something like this:
var _contacts = new Contacts();
var contact = _contacts.GetByEmployeeId("C1112", ContactType.Emergency);
The confusion started when I read this blog post, the author says that using code like:
IContactsRepository repo = ResolveRepository<IContactsRepository>();
is a bad technique and it's anti-pattern and one should inject everything at the root of code. I can't see how would I do this with repository pattern. I am consuming this using a WCF. So, how on earth would I inject everything from the first call in WCF? I can't get it. What am I missing here?
One last thing, in this case the WCF is the last layer, and it should be only aware of the layer before it, which is the BLL layer. If I am going to implement anything as the author of that blog suggested, I will make the WCF layer aware of the DAL layer, isn't that bad practice? correct me if I am wrong.
You need to use Constructor Injection and then compose your objects in the Composition Root.
When you use Constructor Injection, you inject the dependencies through the constructors, so your classes would look like something like this:
public class BaseRepository
{
protected readonly DbContext _dbContext;
//...
public BaseRepository(DbContext context)
{
_dbContext = context;
}
//...
}
public class ContactsRepository : BaseEFRepository<Contact, long>, IContactsRepository
{
//...
public ContactsRepository(DbContext context)
:base(context)
{
}
//...
}
public class Contacts
{
private readonly IContactsRepository m_ContactsRepository;
public Contacts(IContactsRepository contacts_repository)
{
m_ContactsRepository = contacts_repository;
}
public Contact Get(long id)
{
var contact = m_ContactsRepository.Get(id);
return contact;
}
//...
}
Then in the Composition Root your would compose all your objects together. Optionally via a DI container.
Here is an example of such composition that uses Pure DI:
var context = new MyDB();
context.Configuration.ProxyCreationEnabled = false;
context.Configuration.LazyLoadingEnabled = false;
var contacts = new Contacts(new ContactsRepository(context));
In a WCF application that is hosted in IIS, the Composition Root is a custom ServiceHostFactory. This answer provides more details about how to do that.
#Matthew's post should answer your question. However, I would like to mention one thing I noticed in your code in relation to the question. You should not try to manually resolve the dependency but it should be injected by the container. I have modified your code to show this behaviour:
public class Contacts
{
private IContactsRepository repo;
public Contacts(IContactsRepository injectedContactsRepository)
{
repo = injectedContactsRepository;
}
public Contact Get(long id)
{
var contact = repo.Get(id);
return contact;
}
//and other methods definitions...
}
You need to identify a seam suitable for use as a composition root.
For WCF, you need to get creative - you must create a custom ServiceHostFactory to intercept the correct place to compose your object root.
See for example this article and this article from Mark Seemann (author of "Dependency Injection in .Net" - a book I think you would find very useful).
Many free DI containers, such as Autofac, provide off-the-shelf support for WCF, which is probably the best approach.
I really can't recommend Seemann's book highly enough - it goes into a lot of detail about this.
You might also find this article on using Autofac DI with ASP.Net and a repository interesting.

ASP.NET universal controller for database dictionaries (using Entity Framework)

I've got some models that only have two fields: Id and Name. All this models inherit from IDbDictionary interface.
My goal is to make "universal" controller for CRUD operations for this models. My problem is how to (using EF) modify database table by name. For example, there is method
[HttpPost]
public ActionResult Create(IDbDictionary newRecord, string collectionName)
{
if (ModelState.IsValid)
{
db.<collectionName>.Add(newRecord);
db.SaveChanges();
return View("Success");
}
return View("Create", newRecord);
}
Is there a way to do it the way I described? I thought about reflection, but I've no idea how to do this.
Regards,
C
Usually you would have a Service that would handle your generic database operations which you can call from all your methods. Example:
public class DataService
{
public readonly ApplicationDbContext dbContext;
public DataService(ApplicationDbContext dbContext)
{
this.dbContext = dbContext;
}
public void Create<TEntity>(TEntity entity) where TEntity : IDbDictionary
{
this.dbContext.Set<TEntity>().Add(entity);
this.dbContext.SaveChanges();
}
}
Then in your method you would use:
var dataService = new DataService(this.dbContext);
dataService.Create<ClassName>(newEntity);

Categories