i need to implement a fully async architecture for a windows 8 App, i use SQLite For winRT & SQLite.Net in the data layer.
What i have done so far :
DAL
Context :
public interface IContext
{
Task InitializeDatabase();
SQLiteAsyncConnection GetAsyncConnection();
}
public class SQLiteAsyncContext : IContext
{
private SQLiteAsyncConnection _context;
private String _dBPath;
public SQLiteAsyncContext()
{
this._dBPath = Path.Combine(
Windows.Storage.ApplicationData.Current.LocalFolder.Path, "DBName");
this._context = new SQLiteAsyncConnection(_dBPath);
}
public async Task InitializeDatabase()
{
await _context.CreateTableAsync<User>();
}
public SQLiteAsyncConnection GetAsyncConnection()
{
return _context;
}
}
Generic Repository :
public interface IAsyncRepository<T> where T : class
{
Task<int> AddAsync(T entity);
Task<int> UpdateAsync(T entity);
Task<int> RemoveAsync(T entity);
Task<IList<T>> GetAllAsync();
Task<IList<T>> GetBy(System.Linq.Expressions.Expression<Func<T, bool>> predicate);
Task SaveChanges();
}
public class AsyncRepository<T> : IAsyncRepository<T> where T : class, new()
{
private SQLiteAsyncConnection _context;
public AsyncRepository(IContext _context)
{
this._context = _context.GetAsyncConnection();
}
public async Task<int> AddAsync(T entity)
{
return await _context.InsertAsync(entity);
}
public async Task<int> UpdateAsync(T entity)
{
return await _context.UpdateAsync(entity);
}
public async Task<int> RemoveAsync(T entity)
{
return await _context.DeleteAsync(entity);
}
public async Task<IList<T>> GetAllAsync()
{
return await _context.Table<T>().ToListAsync();
}
public async Task<IList<T>> GetBy(System.Linq.Expressions.Expression<Func<T, bool>> predicate)
{
return await _context.Table<T>().Where(predicate).ToListAsync();
}
public Task SaveChanges()
{
throw new NotImplementedException();
}
}
BL
public interface IAsyncServices<T> where T : class
{
Task<int> AddAsync(T entity);
Task<int> UpdateAsync(T entity);
Task<int> RemoveAsync(T entity);
Task<IList<T>> GetAllAsync();
Task<IList<T>> GetBy(System.Linq.Expressions.Expression<Func<T, bool>> predicate);
}
public class AsyncUserService : IAsyncServices<User>
{
private readonly IAsyncRepository<User> _asyncUserRepository;
public AsyncUserService(IAsyncRepository<User> _asyncUserRepository)
{
this._asyncUserRepository = _asyncUserRepository;
}
public async Task<int> AddAsync(User entity)
{
return await _asyncUserRepository.AddAsync(entity);
}
public async Task<int> UpdateAsync(User entity)
{
return await _asyncUserRepository.UpdateAsync(entity);
}
public async Task<int> RemoveAsync(User entity)
{
return await _asyncUserRepository.RemoveAsync(entity);
}
public async Task<IList<User>> GetAllAsync()
{
return await _asyncUserRepository.GetAllAsync();
}
public async Task<IList<User>> GetBy(Expression<Func<User, bool>> predicate)
{
return await _asyncUserRepository.GetBy(predicate);
}
}
i have some questions :
Is it possible to implement UnitOfWork pattern With SQliteAsyncConnection.
How To perform Commit & RollBack within Repository.
Anything i should Improve ?
Thanks In Advance .
Is it possible to implement UnitOfWork pattern With
SQliteAsyncConnection.
I guess it heavily depends on your business tasks. You cannot invent just abstract architecture magically covering all possible cases. Asynchronous data processing is not a toy in itself. Combined with database it can quickly become an unmanageable mess of code.
How To perform Commit & RollBack within Repository.
Well, first you need to stop access to Repository with some locking mechanism. Then you need to wait until all your asynchronous operations complete. And then you commit / rollback and unlock. This is generic of course and may not suite your workflow.
Anything i should Improve ?
You tell ;)
Actually, I would heavily recommend not to use generic async database access. Use async database operations ONLY if you really need them to be async. Fetching one record by primary key does not need to be async. It's blazing fast enough.
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();
}
We are using Net Core 2 with Entity Framework. Our Sql database consists of many tables, Address, Account, Customer, Sales, etc.
Some tables have corresponding history tables: AddressHistory, CustomerHistory,
Anytime someone changes something in original tables, the corresponding History table should be updated. (cannot use sql temporal tables, since we have custom history logic)
We are trying to apply interfaces, I'm little stuck on the code, can someone provide a quick code example of how to implement this with interfaces? Especially in SaveHistory portion, how can dependency injection be applied? Feel free to rewrite code as needed
public partial class TestDbContext
{
public void AddEntityHistory<IEntityHistory>(IEntityHistory entity) where IEntityHistory: Address
{
// do something 1 etc
}
public void AddEntityHistory<IEntityHistory>(IEntityHistory entity) where IEntityHistory: Customer
{
// do something 2 etc
}
public override int SaveChanges()
{
SaveEntityHistory();
return base.SaveChanges();
}
protected void SaveEntityHistory()
{
var modifiedEntities = ChangeTracker.Entries<IEntityHistory>().Where(e => e.State == EntityState.Added || e.State == EntityState.Modified);
foreach(var entity in modifiedEntities)
{
AddEntityHistory(entity); // what is the code base here? giving error below, it should call appropriate AddEntityHistory Method for corresponding Entity
}
}
}
Error Above:
Error CS0311 The type 'Interfaces.IEntityHistory' cannot be used as type parameter 'IEntityHistory' in the generic type or method 'PropertyContext.AddEntityHistory(IEntityHistory)'. There is no implicit reference conversion from 'Interfaces.IEntityHistory' to 'Data.Entities.Address'.
Resources :
Trying to utilize similar codebase: for CreateDate, UserId, etc
https://dotnetcore.gaprogman.com/2017/01/26/entity-framework-core-shadow-properties/
Generic form for methods won't work here at all. Reflection is more suitable for your requirement:
public partial class TestDbContext
{
public void AddEntityHistory(Address entity)
{
// do something 1 etc
}
public void AddEntityHistory(Customer entity)
{
// do something 2 etc
}
public override int SaveChanges()
{
SaveEntityHistory();
return base.SaveChanges();
}
protected void SaveEntityHistory()
{
var modifiedEntities = ChangeTracker.Entries<IEntityHistory>()
.Where(e => e.State == EntityState.Added || e.State == EntityState.Modified);
foreach (var entity in modifiedEntities)
{
var methodInfo = this.GetType().GetMethod(nameof(AddEntityHistory), new[] { entity.Entity.GetType() });
methodInfo.Invoke(this, new[] { entity.Entity });
}
}
}
You can use a Generic Repository and then for each Entity's repository, you can then save its respective history table. Below is the example code.
IGenericRepository
public interface IGenericRepository
{
Task GetByIdAsync(object id, CancellationToken cancellationToken = default);
Task InsertAsync(TEntity entity, CancellationToken cancellationToken = default);
void Delete(object id);
void Delete(TEntity entityToDelete);
void Update(TEntity entityToUpdate);
void UpdateStateAlone(TEntity entityToUpdate);
}
GenericRepository
public class GenericRepository : IGenericRepository
where TEntity : class, new()
{
private readonly YourDbContext context;
internal DbSet dbSet;
public GenericRepository(YourDbContext context)
{
this.context = context;
dbSet = context.Set();
}
public virtual Task GetByIdAsync(object id, CancellationToken cancellationToken = default)
{
return dbSet.FindAsync(id);
}
public virtual Task InsertAsync(TEntity entity, CancellationToken cancellationToken = default)
{
return dbSet.AddAsync(entity, cancellationToken);
}
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;
}
public virtual void UpdateStateAlone(TEntity entityToUpdate)
{
context.Entry(entityToUpdate).State = EntityState.Modified;
}
}
Now, to use the above Generic Repository for your Entities, use the below sample code.
using System.Threading.Tasks;
public interface IAddressRepository: IGenericRepository
{
Task CommitAsync();
public virtual Task InsertAsync(TEntity entity, CancellationToken cancellationToken = default)
}
using System.Threading.Tasks;
public class AddressRepository: GenericRepository, IAddressRepository
{
private readonly YourDbContext _context;
public AddressRepository(YourDbContext context) : base(context)
{
_context = context;
}
public override Task InsertAsync(Address entity, CancellationToken cancellationToken = default)
{
base.InsertAsync(entity,cancellationToken );
//call your history insert here and then the below save. This will save both the record in the main Address table and then your Address's history table.
return _context.SaveChangesAsync();
}
public Task CommitAsync()
{
return _context.SaveChangesAsync();
}
}
For detailed implementation, refer this Implement create, read, update, and delete functionalities using Entity Framework Core
I am trying to implement async Repository pattern and would like to update asynchronously:
My controller looks like this:
// PUT api/category
[HttpPut]
public void Put([FromBody] CategoryDto categoryDto)
{
var category = _mapper.Map<Categories>(categoryDto);
_categoryService.UpdateCategory(category);
}
My RepositotyBase<T> class:
public abstract class RepositoryBase<T> where T : class
{
public virtual async Task Update(T entity)
{
// This code throws error: Cannot access a disposed object. A common
// cause of this error is disposing ...
await Task.Run(() => { // I have **await** here
dbSet.Attach(entity);
shopContext.Entry(entity).State = EntityState.Modified;
});
}
}
My CategoryService:
public class CategoryService : ICategoryService
{
public async Task UpdateCategory(Categories category)
{
await _categoryRepository.Update(category); // I have **await** here
_unitOfWork.Commit();
return;
}
}
However, the async implementation of method public virtual async Task Update(T entity) of RepositoryBase<T> throws an error:
public abstract class RepositoryBase<T> where T : class
{
public virtual async Task Update(T entity)
{
// This code throws error: Cannot access a disposed object. A common
// cause of this error is disposing ...
await Task.Run(() => {
dbSet.Attach(entity); // I have **await** here
shopContext.Entry(entity).State = EntityState.Modified;
});
}
}
Cannot access a disposed object. A common cause of this error is
disposing a context that was resolved from dependency injection and
then later trying to use the same context instance elsewhere in your
application. This may occur if you are calling Dispose() on the
context, or wrapping the context in a using statement. If you are
using dependency injection, you should let the dependency injection
container take care of disposing context instances.
UPDATE:
Is it correct async implemetation?
My controller:
// PUT api/category
[HttpPut]
public void Put([FromBody] CategoryDto categoryDto)
{
var category = _mapper.Map<Categories>(categoryDto);
_categoryService.UpdateCategory(category);
}
My generic repository:
public abstract class RepositoryBase<T> where T : class
{
public virtual async Task Update(T entity)
{
dbSet.Attach(entity);
shopContext.Entry(entity).State = EntityState.Modified;
}
}
My unit of work:
public class UnitOfWork : IUnitOfWork
{
private readonly IDbFactory dbFactory;
private StoreEntities dbContext;
public UnitOfWork(IDbFactory dbFactory)
{
this.dbFactory = dbFactory;
}
public StoreEntities DbContext
{
get { return dbContext ?? (dbContext = dbFactory.Init()); }
}
public async Task CommitAsync()
{
//DbContext.Commit();
await DbContext.SaveChangesAsync();
}
}
My service:
public class CategoryService : ICategoryService
{
public async Task UpdateCategory(Categories category)
{
_categoryRepository.Update(category); // I have **await** here
await Task.Run(()=> {_unitOfWork.Commit()}); //
return;
}
}
You need to setup your controller action PUT api/category to be async/await aware.
#mjwills has alluded to this in the comments.
// PUT api/category
[HttpPut]
public async Task Put([FromBody] CategoryDto categoryDto)
{
var category = _mapper.Map<Categories>(categoryDto);
await _categoryService.UpdateCategory(category);
// you probably want to set a response code. e.g return OK()
}
This is a little more offtopic but I am addressing some of the other comments.
You also need to save the changes to the database in EF. You shouldn't need to manually create your own tasks. To my knowledge EF provides a SaveChangesAsync method for you: https://learn.microsoft.com/en-us/ef/core/saving/async.
public abstract class RepositoryBase<T> where T : class
{
public virtual async Task Update(T entity)
{
dbSet.Attach(entity);
shopContext.Entry(entity).State = EntityState.Modified;
await shopContext.SaveChangesAsync();
}
}
public class CategoryService : ICategoryService
{
public async Task UpdateCategory(Categories category)
{
return await _categoryRepository.Update(category);
}
}
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?
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>();
}
...
}