I have a code-first web application made using Entity Framework Core and ASP.NET Core 2.1.
When I save data sometimes it saves properly with only 1 record. However sometimes it saves 2 duplicated records with different Ids.
The Create(company) method is called only once from the controller, but sometimes creates two companies.
Controller implementation:
[ApiController]
public class CompaniesController : ControllerBase
{
private readonly ICompanyManager _companyManager;
public CompaniesController(IEmpresaManager companyManager)
{
_companyManager = companyManager;
}
[HttpPost]
public async Task<IActionResult> Post(CompanyBindingModel companyViewModel)
{
try
{
await _companyManager.Create(_mapper.Map<Company>(companyViewModel));
}
catch (Exception ex)
{
return Conflict(ex.Message);
}
return Ok(new { Mensagem = "Company created successfully" });
}
Manager implementation:
public class CompanyManager : ICompanyManager
{
private readonly RepositoyBase<Company, long> _repository;
public CompanyManager (RepositoyBase<Company, long> repository) {
_repository = repository;
}
public async Task<long> Create(Company company)
{
_repository.Add(company);
await _repository.SaveChangesAsync();
return company.Id;
}
}
This is my repository pattern implementation, which saves all entities.
DbContext is injected via dependency injection and registred as scoped.
In Startup.cs
services.AddScoped<DbContext, ApplicationDbContext>();
Repository implementation
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
namespace MyProject.Repositories
{
public class RepositoryBase<TEntity, TKey> : IRepositoryBase<TEntity, TKey>
where TEntity : class, IEntity<TKey>
where TKey : struct
{
protected readonly DbContext _dbContext;
public RepositoryBase(DbContext dbContext)
{
_dbContext = dbContext;
}
public virtual async Task<IEnumerable<TEntity>> GetAll(Expression<Func<TEntity, bool>> expression)
{
return await _dbContext.Set<TEntity>().Where(expression).ToListAsync();
}
public virtual async Task<TEntity> Get(Expression<Func<TEntity, bool>> expression)
{
return await _dbContext.Set<TEntity>().FindAsync(expression);
}
public virtual async Task<TEntity> GetById(TKey id)
{
return await _dbContext.Set<TEntity>().FindAsync(id);
}
public virtual IQueryable<TEntity> GetAll()
{
return _dbContext.Set<TEntity>().AsNoTracking();
}
public virtual void Add(TEntity entity)
{
_dbContext.Set<TEntity>().Add(entity);
}
public virtual void Update(TEntity entity)
{
_dbContext.Set<TEntity>().Update(entity);
}
public virtual void Remove(TEntity entity)
{
if (_dbContext.Entry(entity).State == EntityState.Detached)
{
_dbContext.Attach(entity);
}
_dbContext.Set<TEntity>().Remove(entity);
}
public virtual void Remove(TKey id)
{
TEntity entityToRemove = _dbContext.Set<TEntity>().Find(id);
_dbContext.Set<TEntity>().Remove(entityToRemove);
}
public virtual async Task SaveChangesAsync()
{
await _dbContext.SaveChangesAsync();
}
}
}
Is this caused by my repository implementation, DbContext registered as scoped or something with thread-safe?
Related
I am writting an API backend application using .NET Core
BaseRepository:
public class BaseRepository<T, TPrimaryKey> : IBaseRepository<T, TPrimaryKey> where T : class where TPrimaryKey : struct
{
private readonly DatabaseContext _dbContext;
public BaseRepository(DatabaseContext dbContext)
{
_dbContext = dbContext;
}
public async Task<IEnumerable<T>> GetAll()
{
return await _dbContext.Set<T>().ToListAsync();
}
public IQueryable<T> GetQueryable()
{
return _dbContext.Set<T>();
}
public async Task<T> Find(TPrimaryKey id)
{
return await _dbContext.Set<T>().FindAsync(id);
}
public async Task<T> Add(T entity, bool saveChanges = true)
{
await _dbContext.Set<T>().AddAsync(entity);
if (saveChanges)
await _dbContext.SaveChangesAsync();
return await Task.FromResult(entity);
}
public async Task Edit(T entity, bool saveChanges = true)
{
_dbContext.Entry(entity).State = EntityState.Modified;
if (saveChanges)
await _dbContext.SaveChangesAsync();
}
public async Task Delete(T entity, bool saveChanges = true)
{
if (entity == null)
throw new NullReferenceException();
_dbContext.Set<T>().Remove(entity);
if (saveChanges)
await _dbContext.SaveChangesAsync();
}
public async Task<IEnumerable<T>> BulkInsert(IEnumerable<T> entities, bool saveChanges = true)
{
foreach (T entity in entities)
{
await _dbContext.Set<T>().AddAsync(entity);
}
if (saveChanges)
await _dbContext.SaveChangesAsync();
return await Task.FromResult(entities);
}
public async Task BulkUpdate(IEnumerable<T> entities, bool saveChanges = true)
{
foreach (T entity in entities)
{
_dbContext.Entry(entity).State = EntityState.Modified;
}
if (saveChanges)
await _dbContext.SaveChangesAsync();
}
public async Task Save()
{
await _dbContext.SaveChangesAsync();
}
}
IBaseRepository:
public interface IBaseRepository<T, E> where T : class where E : struct
{
Task<IEnumerable<T>> GetAll();
IQueryable<T> GetQueryable();
Task<T> Find(E id);
Task<T> Add(T entity, bool saveChanges = true);
Task Edit(T entity, bool saveChanges = true);
Task Delete(T entity, bool saveChanges = true);
Task<IEnumerable<T>> BulkInsert(IEnumerable<T> entities, bool saveC
Task BulkUpdate(IEnumerable<T> entities, bool saveChanges = true);
Task Save();
}
IServiceBase:
public interface IServiceBase<TEntity, TPrimaryKey>
{
Task<TEntity> GetById(TPrimaryKey id);
Task<TEntity> GetSingle(Expression<Func<TEntity, bool>> whereCondition);
Task<IEnumerable<TEntity>> GetAll();
IEnumerable<TEntity> GetAll(Expression<Func<TEntity, bool>> whereCondition);
IQueryable<TEntity> GetAllQueryable();
IQueryable<TEntity> Query(Expression<Func<TEntity, bool>> whereCondition);
Task<TEntity> Create(TEntity entity);
Task Delete(TEntity entity);
Task Update(TEntity entity);
Task<long> Count(Expression<Func<TEntity, bool>> whereCondition);
Task<long> Count();
Task<IEnumerable<TEntity>> BulkInsert(IEnumerable<TEntity> entities);
Task BulkUpdate(IEnumerable<TEntity> entities);
}
for example IAddressServices:
public interface IAddressService : IServiceBase<Address, Guid>
{
Task<Address> VerifyAddress(Address address);
}
ServiceBase:
public abstract class ServiceBase<TEntity, TRepository, TPrimaryKey> : IServiceBase<TEntity, TPrimaryKey>
where TEntity : class
where TPrimaryKey : struct
where TRepository : IBaseRepository<TEntity, TPrimaryKey>
{
public TRepository Repository;
public ServiceBase(IBaseRepository<TEntity, TPrimaryKey> rep)
{
Repository = (TRepository)rep;
}
public virtual async Task<TEntity> GetById(TPrimaryKey id)
{
return await Repository.Find(id);
}
public async Task<TEntity> GetSingle(Expression<Func<TEntity, bool>> whereCondition)
{
return await Repository.GetQueryable().Where(whereCondition).FirstOrDefaultAsync();
}
public async Task<IEnumerable<TEntity>> GetAll()
{
return await Repository.GetAll();
}
public IEnumerable<TEntity> GetAll(Expression<Func<TEntity, bool>> whereCondition)
{
return Repository.GetQueryable().Where(whereCondition);
}
public IQueryable<TEntity> GetAllQueryable()
{
return Repository.GetQueryable();
}
public IQueryable<TEntity> Query(Expression<Func<TEntity, bool>> whereCondition)
{
return Repository.GetQueryable().Where(whereCondition);
}
public virtual async Task<TEntity> Create(TEntity entity)
{
return await Repository.Add(entity);
}
public virtual async Task Delete(TEntity entity)
{
await Repository.Delete(entity);
}
public virtual async Task Update(TEntity entity)
{
await Repository.Edit(entity);
}
public async Task<long> Count(Expression<Func<TEntity, bool>> whereCondition)
{
return await Repository.GetQueryable().Where(whereCondition).CountAsync();
}
public async Task<long> Count()
{
return await Repository.GetQueryable().CountAsync();
}
public async Task<IEnumerable<TEntity>> BulkInsert(IEnumerable<TEntity> entities)
{
return await Repository.BulkInsert(entities);
}
public async Task BulkUpdate(IEnumerable<TEntity> entities)
{
await Repository.BulkUpdate(entities);
}
}
and concrete implementations of services:
AddressService:
public class AddressService : ServiceBase<Address, IBaseRepository<Address, Guid>, Guid>, IAddressService
{
public AddressService(IBaseRepository<Address, Guid> rep) : base(rep)
{
}
public async Task<Address> VerifyAddress(Address address)
{
//logic
}
}
ProductController:
public class ProductController : ControllerBase
{
private readonly IProductService _productService;
private readonly IAddressService _addressService;
private readonly ILogger _logger;
private readonly IMapper _mapper;
public ProductController (IProductService productService,
IAddressService addressService,
ILogger<ProductController> logger,
IMapper mapper)
{
_packageService = packageService;
_addressService = addressService;
_logger = logger;
_mapper = mapper;
}
[HttpGet]
public async Task<IActionResult> GetAllProductsWithAddresses()
{
try
{
var products = await _productService.GetAllQueryable().Include(x => x.Address).ToListAsync();
return Ok(_mapper.Map<List<ProductResponse>>(products));
}
catch (Exception e)
{
_logger.LogError($"An unexpected error occured: ${e}");
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
}
Lets say for example if I had an POST endpoint in ProductController where I need to insert data in 3 different database tables: Address, ProductSize and ProductImage. I would have 3 services and I would call _addressService.Add(address), _productSize.Add(productSize) and _productImageService(image) in my controller. How can I support transactions here if DatabaseContext is located in BaseRepository, what is the best practice?
How can I support transactions here if DatabaseContext is located in BaseRepository, what is the best practice?
Best practice is to throw out all that junk and just have your controller talk to the DbContext, or have the controller talk to a business service that talks to the DbContext.
Barring that DI should inject the same DbContext instance in each service or repository, so you can expose Unit-of-work methods on any one of them. Or you could introduce an additional service for managing cross-repo operations. EG
public class UnitOfWork
{
DbContext db;
public UnitOfWork(DbContext db)
{
this.db = db;
}
IDbContextTransaction BeginTransaction() => db.Database.BeginTransaction();
void CommitTransaction() => db.Database.CommitTransaction();
int SaveChanges() => db.SaveChanges();
}
I have an application in .net core 3.1 with angular front end. I want to use the decorator to the base controller in order to log CUD operations in the entire application. I am using Scrutor nuget package in the project.
The Base Controller is the following
using System.Collections.Generic;
using System.Threading.Tasks;
using AutoMapper;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Xenagos.Data;
using Xenagos.Data.EFCore;
using Xenagos.ViewModels;
namespace Xenagos.Controllers
{
[Route("api/[controller]")]
[ApiController]
public abstract class BaseController<TEntity, TViewEntity, TRepository> : ControllerBase, IBaseController<TEntity, TViewEntity>
where TEntity : class
where TViewEntity : class, IViewEntity
where TRepository : IRepository<TEntity>
{
private readonly IRepository<TEntity> repository;
private readonly IMapper mapper;
public BaseController(TRepository repository, IMapper mapper)
{
this.repository = repository;
this.mapper = mapper;
}
// GET: api/[controller]
[HttpGet]
public virtual async Task<ActionResult<ComplexData<TViewEntity>>> Get()
{
var results = await repository.GetAll();
List<TViewEntity> resultsView =
this.mapper.Map<List<TEntity>, List<TViewEntity>>(results);
return Ok(new ComplexData<TViewEntity>(resultsView));
}
// GET: api/[controller]/5
[HttpGet("{id}")]
public async Task<ActionResult<TEntity>> Get(int id)
{
var entity = await repository.Get(id);
if (entity == null)
{
return NotFound();
}
return entity;
}
// PUT: api/[controller]/5
[HttpPut("{id}")]
public virtual async Task<IActionResult> Put(string id, TViewEntity entity)
{
if (!id.Equals(entity.Id))
{
return BadRequest();
}
await repository.Update(this.mapper.Map<TEntity>(entity));
return NoContent();
}
// POST: api/[controller]
[HttpPost]
public virtual async Task<ActionResult<TEntity>> Post(TViewEntity entity)
{
await repository.Add(this.mapper.Map<TEntity>(entity));
return CreatedAtAction("Get", new { id = entity.Id }, entity);
}
// DELETE: api/[controller]/5
[HttpDelete("{id}")]
public async Task<ActionResult<TViewEntity>> Delete(int id)
{
var entity = await repository.Delete(id);
if (entity == null)
{
return NotFound();
}
return this.mapper.Map<TViewEntity>(entity);
}
}
}
The decorator I made is the following
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xenagos.Controllers;
using Xenagos.ViewModels;
namespace Xenagos.Data
{
public class LoggingDecorator<T, TViewEntity, TRepository> : IBaseController<T, TViewEntity>
where T : class
where TViewEntity : class, IViewEntity
where TRepository : IRepository<T>
{
private IBaseController<T, TViewEntity> _baseController;
private readonly ILogger<LoggingDecorator<T, TViewEntity, TRepository>> _logger;
public LoggingDecorator(IBaseController<T, TViewEntity> baseController, ILogger<LoggingDecorator<T, TViewEntity, TRepository>> logger)
{
_baseController = baseController;
_logger = logger;
}
Task<ActionResult<TViewEntity>> IBaseController<T, TViewEntity>.Delete(int id)
{
_logger.LogWarning($"Deleting record from ... with ID:{id}");
Task<ActionResult<TViewEntity>> result = _baseController.Delete(id);
return result;
}
public Task<ActionResult<ComplexData<TViewEntity>>> Get()
{
return _baseController.Get();
}
Task<ActionResult<T>> IBaseController<T, TViewEntity>.Get(int id)
{
return _baseController.Get(id);
}
public Task<ActionResult<T>> Post(TViewEntity entity)
{
_logger.LogWarning($"Adding new record from ... with object data :{JsonConvert.SerializeObject(entity)}");
return _baseController.Post(entity);
}
public Task<IActionResult> Put(string id, TViewEntity entity)
{
_logger.LogWarning($"updating record from ... with object data :{JsonConvert.SerializeObject(entity)}");
Task<IActionResult> result = _baseController.Put(id, entity);
return result;
}
}
}
In the startup class in the public void ConfigureServices(IServiceCollection) I use the following lines
services.AddScoped<IBaseController<Models.Property, PropertyViewModel>, BaseController<Models.Property, PropertyViewModel, PropertyRepository>>();
services.Decorate<IBaseController<Models.Property, PropertyViewModel>, LoggingDecorator<Models.Property, PropertyViewModel, PropertyRepository>>();
I have extracted an Interface from the base controller, on top of all the previous actions.
While the application runs, it does not call/pass-through the decorator. What I'm missing here ?
I haven't used the decorator pattern with .net core and dependency injection before. All the added code is in the backend only, I haven't altered the front end at all.
Thank you in advance.
Logging enter/exit on controller actions using ActionFilter:
public class LoggingActionFilter : IActionFilter
{
ILogger _logger;
public LoggingActionFilter(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<LoggingActionFilter>();
}
public void OnActionExecuting(ActionExecutingContext context)
{
// do something before the action executes
_logger.LogInformation($"Action '{context.ActionDescriptor.DisplayName}' executing");
}
public void OnActionExecuted(ActionExecutedContext context)
{
// do something after the action executes
_logger.LogInformation($"Action '{context.ActionDescriptor.DisplayName}' executed");
}
}
Startup
services.AddMvc()
.AddMvcOptions(options =>
{
options.Filters.Add<LoggingActionFilter>();
});
You might also want to implement IAsyncActionFilter for the async actions.
To read more about action filters, look here.
You can also add exception filters to log all exceptions.
I am working on implementing the generic repository pattern with db Context and Autofac DI, and I am using the constructor based on dependency injections.
I have created the three layers repository pattern which are:
Infrastructure layer
Model layer
Services layer
Now, I am using this architecture in MVC web application when I try to pass the IContactService interface in ContactController and run the application.
I am getting the circular dependency detected error. Followings are my project code.
Repository code:
public class Repository<T> : IRepository<T> where T : class
{
private InvoiceBillingDbContext db;
private DbSet<T> dbSet;
public Repository()
{
db = new InvoiceBillingDbContext();
dbSet = db.Set<T>();
}
public IEnumerable<T> GetAll()
{
return dbSet.ToList();
}
public T GetById(object Id)
{
return dbSet.Find(Id);
}
public void Insert(T obj)
{
dbSet.Add(obj);
Save();
}
public void Update(T obj)
{
db.Entry(obj).State = EntityState.Modified;
Save();
}
public void Delete(object Id)
{
T getObjById = dbSet.Find(Id);
dbSet.Remove(getObjById);
}
public void Save()
{
db.SaveChanges();
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (this.db != null)
{
this.db.Dispose();
this.db = null;
}
}
}
public void AddRange(List<T> obj)
{
dbSet.AddRange(obj);
Save();
}
public IEnumerable<T> Get(Expression<Func<T, bool>> predicate)
{
return dbSet.Where(predicate);
}
}
Interface IRepository
public interface IRepository<T> where T : class
{
IEnumerable<T> GetAll();
T GetById(object Id);
IEnumerable<T> Get(Expression<Func<T, bool>> predicate);
void Insert(T obj);
void AddRange(List<T> obj);
void Update(T obj);
void Delete(Object Id);
void Save();
}
My ContactRepository
public class ContactRepository:Repository<Contact>, IContactRepository
{
private IContactRepository _contractRepo;
public ContactRepository(IContactRepository contractRepo)
{
this._contractRepo = contractRepo;
}
}
My ContactRepository Interface
public interface IContactRepository:IRepository<Contact>
{
}
My Contact Service
public interface IContactService
{
void AddContact(Contact contact);
IEnumerable<Contact> GetContactsByTypeId(int typeId);
}
public class ContactService : IContactService
{
private readonly IContactRepository _contactRepository;
public ContactService(IContactRepository contactRepository)
{
this._contactRepository = contactRepository;
}
public void AddContact(Contact contact)
{
_contactRepository.Insert(contact);
}
public IEnumerable<Contact> GetContactsByTypeId(int typeId)
{
return _contactRepository.Get(i => i.TypeID == typeId);
}
}
My Controller
public class ContactController : Controller
{
// GET: Contact
private IContactService _contactService;
public ContactController(IContactService contactService)
{
this._contactService = contactService;
}
public ActionResult Index()
{
return View(new ContactViewModel());
}
public ActionResult AddContact(ContactViewModel contact)
{
if (ModelState.IsValid)
{
var _contactMapper = Mapper.Map<ContactViewModel, Contact>(contact);
_contactService.AddContact(_contactMapper);
ViewBag.Message = "Successfully Saved";
return View("Index",new ContactViewModel());
}
ViewBag.Message = "Error Occur Please try Again!";
return View("Index", new ContactViewModel());
}
}
And Autofac Dependency Injection
public static void ConfigureContainer()
{
var builder = new ContainerBuilder();
// Register all controllers in the Mvc Application assembly
builder.RegisterControllers(typeof(MvcApplication).Assembly);
// Registered Warehouse Reservoir Service
builder.RegisterType<InvoiceRepository>().As<IInvoiceRepository>();
// Registration Service Layer Service
builder.RegisterType<InvoiceService>().As<IInvoiceService>();
builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>));
// Registered Warehouse Reservoir Service
builder.RegisterType<JournalVoucherRepository>().As<IJournalVoucherRepository>();
// Registration Service Layer Service
builder.RegisterType<JournalVoucherService>().As<IJournalVoucherService>();
// Registered Warehouse Reservoir Service
builder.RegisterType<ContactRepository>().As<IContactRepository>();
// Registration Service Layer Service
builder.RegisterType<ContactService>().As<IContactService>();
// Registration filter
builder.RegisterFilterProvider();
var container = builder.Build();
// Setting Dependency Injection Parser
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
You've only got one implementation of IContactRepository. You've also got a generic IRepository<T>.
Your implementation of IContactRepository gets another IContactRepository injected into it. So you're recursively resolving the same class and injecting it into itself.
public class ContactRepository:Repository<Contact>, IContactRepository
{
private IContactRepository _contractRepo;
// In order to resolve IContractRepository, the container has to
// resolve another IContractRepository to inject into it.
// And another one to inject into that. It's recursive.
public ContactRepository(IContactRepository contractRepo)
{
this._contractRepo = contractRepo;
}
}
Your example doesn't show how ContactRepository uses the IContactRepository that gets inject into it. But this
public interface IContactRepository:IRepository<Contact>
{
}
reveals that it's using the exact same methods found in IRepository<Contact>, because IContactRepository doesn't have any methods other than those.
So you most likely just need to modify ContactRepository to inject IRepository<Contact>, not IContactRepository.
public class ContactRepository:Repository<Contact>, IContactRepository
{
private IRepository<Contact> _contractRepo;
public ContactRepository(IRepository<Contact> contractRepo)
{
this._contractRepo = contractRepo;
}
}
That could be easy to overlook since both interfaces have exactly the same methods.
1.) I am a building new MVC application with 3 tier project architecture having:
Common Project with entities
Business/Service holding interfaces and logic classes and
Data holding repositories, interfaces, DbContext and UnitOfWorkclasses. I am using Unity Config to register dependencies, DbContext and UnitOfWork.
2.) I created a repository for each table and one generic repository that does basic CRUD operations.
Example Entity residing in Common Project:
public class MenuSecd
{
[Key, Column(Order = 0)]
public string prg_module { get; set; }
[Key, Column(Order = 1)]
public int prg_numb { get; set; }
[Key, Column(Order = 2)]
public string menu_level { get; set; }
}
My generic Entity Logic Interface residing in Business Project:
public interface IEntityLogic<T> : ILogic where T : class
{
void Create(T entity);
void Delete(T entity);
IEnumerable<T> GetAll();
void Update(T entity);
}
Entity Logic Class:
public abstract class EntityLogic<T> : IEntityLogic<T> where T : class
{
IUnitOfWork _unitOfWork;
IGenericRepository<T> _repository;
public EntityLogic(IUnitOfWork unitOfWork, IGenericRepository<T> repository)
{
_unitOfWork = unitOfWork;
_repository = repository;
}
public virtual void Create(T entity)
{
if(entity == null)
{
throw new ArgumentNullException(nameof(entity));
}
_repository.Add(entity);
_unitOfWork.Commit();
}
}
Example Business Logic class for the entity defined in Common Project:
public class MenuSecdLogic : EntityLogic<MenuSecd>, IMenuSecdLogic
{
IUnitOfWork _unitOfWork;
IMenuSecdRepository _repository;
public MenuSecdLogic(IUnitOfWork unitOfWork, IMenuSecdRepository repository) : base(unitOfWork, repository)
{
_unitOfWork = unitOfWork;
_repository = repository;
}
public List<MenuSecd> GetItems(string usrgrp_id)
{
return _repository.GetItems(usrgrp_id);
}
}
My Generic Repository in Data Project looks like:
public abstract class GenericRepository<T> : IGenericRepository<T> where T : class
{
protected DbContext _entities;
protected readonly IDbSet<T> _dbset;
public GenericRepository(DbContext context)
{
_entities = context;
_dbset = context.Set<T>();
}
public virtual T Add(T entity)
{
return _dbset.Add(entity);
}
public virtual T Delete(T entity)
{
return _dbset.Remove(entity);
}
public virtual void Edit(T entity)
{
_entities.Entry(entity).State = EntityState.Modified;
}
}
Repository Interface for the same Entity is defined as:
public interface IMenuSecdRepository : IGenericRepository<MenuSecd>
{
List<MenuSecd> GetItems(string usrgrp_id);
}
Repository class for above mentioned interface is:
public class MenuSecdRepository : GenericRepository<MenuSecd>, IMenuSecdRepository
{
public MenuSecdRepository(DbContext context) : base(context)
{
}
public List<MenuSecd> GetItems(string usrgrp_id)
{
return _dbset.Where(m => m.usrgrp_id == usrgrp_id).ToList();
}
}
My DbContext looks like:
public class DashboardContext : DbContext
{
public DashboardContext() : base("Name=DBEntities")
{
}
public DbSet<MenuSecd> menusecd { get; set; }
public override int SaveChanges()
{
var modifiedEntries = ChangeTracker.Entries().Where(x => x.State == EntityState.Added || x.State == EntityState.Modified);
//future custom implementation like auditing
return base.SaveChanges();
}
}
My UnitOfWork looks like:
public sealed class UnitOfWork : IUnitOfWork
{
private DbContext _dbContext;
public UnitOfWork(DbContext context)
{
_dbContext = context;
}
public int Commit()
{
return _dbContext.SaveChanges();
}
//disposes current object
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
//disposes all external resources
private void Dispose(bool disposing)
{
if (disposing)
{
if (_dbContext != null)
{
_dbContext.Dispose();
_dbContext = null;
}
}
}
}
My controller:
public class DashController : Controller
{
private readonly IMenuSecdLogic _menuSecdLogic;
public DashController(IMenuSecdLogic menuSecdLogic)
{
_menuSecdLogic = menuSecdLogic;
}
public void Save()
{
var menuSecd = new menuSecd();
//populate all fields for entity MenuSecd
_menuSecdLogic.Create(menuSecd);
}
}
My Unity Config in App_Start looks like :
public static void RegisterTypes(IUnityContainer container)
{
container.RegisterType<DbContext, DashboardContext>();
container.RegisterType<IUnitOfWork, UnitOfWork>();
container.RegisterType(typeof(IGenericRepository<>), typeof(GenericRepository<>));
container.RegisterType<IMenuSecdLogic, MenuSecdLogic>();
container.RegisterType<IMenuSecdRepository, MenuSecdRepository>();
}
So when run above project everything builds fine. But when controller calls:
_menuSecdLogic.Create(menuSecd);
It reaches Entity Logic and adds a new entity to _repository at :
_repository.Add(entity);
_unitOfWork.Commit();
But when it hits next line to actually save it to database which is :
return _dbContext.SaveChanges();
in UnitOfWork.cs file.
It comes to dashboardContext where it finally have to save it to database. But it does execute :
var modifiedEntries = ChangeTracker.Entries().Where(x => x.State == EntityState.Added || x.State == EntityState.Modified);
return base.SaveChanges();
But nothing changes in database. There will be no record in database. To test I have added modifiedEntries to see if it is in context or not. By the time control reaches this point I see no modified entries at all. But in EntityLogic.cs it does add a new entity to local entities in repository.
I am not sure what is happening with UnitOfWork here. I ran SQL Profiler to see if it is hitting database or not. Interestingly it is not hitting database at all. But if my make following changes to EntityLogic like this:
public virtual void Create(T entity)
{
if(entity == null)
{
throw new ArgumentNullException(nameof(entity));
}
_repository.Add(entity);
_repository.Save();
//_unitOfWork.Commit();
}
It hits Database and records gets saved fine. But I am not getting why it is neither tracking changes nor hitting database if I use _unitOfWork.Commit() which I want to do. Please help.
It looks like your issue is the scope of your DbContext. Your UnitOfWork and GenericRepository<T> classes are getting different instances.
Not super familiar with Unity, but it looks like you want to use something like this for your DbContext registration:
container.RegisterType<DbContext, DashboadContext>(new PerRequestLifetimeManager());
This will create a single DashboardContext for each request, and your UnitOfWork and GenericRepository<T> classes will be working within the same context.
My generic repository Interface is:
public interface IGenericRepository<T , TEntityKey> where T : EntityBase<TEntityKey>
{
IEnumerable<T> GetAll();
IEnumerable<T> FindBy(Expression<Func<T, bool>> predicate);
T FindBy(TEntityKey entityKey);
Task<T> FindByAsync(TEntityKey entityKey);
T Add(T entity);
T Delete(T entity);
void Edit(T entity);
void Save();
void Dispose();
}
I used await for all async methods in implementing my repositories.
with using Castle Windsor as IOC container, I installed my context with PerWebRequestLifeStyle.
The client sends two requests and each request maps to different repositories implemented by IGenericRepository and this exception rises:
A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe.
I changed life style of the context to transient but still the exception rises.
Can anyone help where I'm wrong?
GenericRepository Implementation:
public abstract class GenericRepository<T, TEntityKey> : IGenericRepository<T, TEntityKey>
where T : EntityBase<TEntityKey>
{
protected IDbContext _entities;
protected readonly IDbSet<T> _dbset;
protected GenericRepository(IDbContext context)
{
_entities = context;
_dbset = _entities.Set<T>();
}
~GenericRepository()
{
_entities.Dispose();
}
public virtual IEnumerable<T> GetAll()
{
return _dbset.AsEnumerable<T>();
}
public IEnumerable<T> FindBy(System.Linq.Expressions.Expression<Func<T, bool>> predicate)
{
IEnumerable<T> query = _dbset.Where(predicate).AsEnumerable();
return query;
}
public T FindBy(TEntityKey entityKey)
{
var result = _dbset.SingleOrDefault(e => e.Id.ToString() == entityKey.ToString());
return result;
}
public Task<T> FindByAsync(TEntityKey entityKey)
{
var result = _dbset.SingleOrDefaultAsync(e => e.Id.ToString() == entityKey.ToString());
return result;
}
public virtual T Add(T entity)
{
return _dbset.Add(entity);
}
public virtual T Delete(T entity)
{
return _dbset.Remove(entity);
}
public virtual void Edit(T entity)
{
_entities.Entry(entity).State = EntityState.Modified;
}
public virtual void Save()
{
_entities.SaveChanges();
}
public async Task SaveAsync()
{
await _entities.SaveChangesAsync();
}
public void Dispose()
{
if (_entities != null)
{
_entities.Dispose();
_entities = null;
GC.SuppressFinalize(this);
}
}
}
I think you have some inherited repositories like these:
Public Repository1<Class1, KeyClass1> : GenericRepository<T, TEntityKey>
{
Public Repository1(IDbContext c) : base(c) { }
}
Public Repository2<Class2, KeyClass2> : GenericRepository<T, TEntityKey>
{
Public Repository2(IDbContext c) : base(c) { }
}
This means your Castle Windsor will create one instance for IDbContext and injects it into both repositories that causes your error.
I can suggest you to change your base class to this:
public abstract class GenericRepository<T, TEntityKey> : IGenericRepository<T, TEntityKey>
where T : EntityBase<TEntityKey>
{
protected IDbContext _entities;
protected readonly IDbSet<T> _dbset;
protected GenericRepository()
{
_entities = new MyDbContext();
_dbset = _entities.Set<T>();
}
...
}