How to use database transactions in current approach? - c#

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();
}

Related

How can I use unit of work pattern with generic repository pattern in my ASP.NET Core Web API project?

There are many examples on the internet about the use of unit of work and generic repository together, but I couldn't find exactly how to apply the unit of work repository pattern to my own project. Because everyone did it differently.
Actually, I set up the unit of work structure, but I don't quite understand how I can use it in my own project? How can I apply the unit of work repository I made to my own project? Can you help me with this? Can you tell me if I have a mistake? This is how I saw the unit of work repository on the internet and implemented it.
First of all, if I just talk about my project, my project is an ASP.NET Core Web API project and basically a 3-layer structure:
API layer. I have controllers in the API layer.
Second is the business layer. The business layer only serves to communicate with the data access layer.
The third layer is the data access layer, in this layer I do the database operations, such as adding, deleting, updating.
I'm doing these with the generic repository. As an example, I am sharing some of my code below.
I just shared category as an example, but I have more than one class like category.
API layer - CategoriesController:
[Route("api/[controller]")]
[ApiController]
public class categoriesController : ControllerBase
{
private ICategoryService category_service;
DatabaseContext c = new DatabaseContext();
public categoriesController(ICategoryService category_service)
{
this.category_service = category_service;
}
[HttpGet("getall")]
public async Task<IActionResult> Get()
{
return Ok(await category_service.TGetAll());
}
[HttpGet("getbyid/{id}")]
public async Task<IActionResult> GetByIdCategory(int id)
{
var category = await category_service.TGetById(id);
if (category != null)
{
return Ok(category); // 200 ok
}
else
{
return NotFound(); //404 not found
}
}
[HttpPost("add")]
public async Task<IActionResult> Add(Category category)
{
var result = category_service.TAdd(category);
if (result != null)
{
return Ok(result);
}
return BadRequest(result);
}
}
Business layer - CategoryManager:
public class CategoryManager:ICategoryService
{
ICategoryDal _categoryDal;
public CategoryManager(ICategoryDal _cateogoryDal)
{
this._categoryDal = _cateogoryDal;
}
public async Task<List<Category>> TGetAll()
{
return await _categoryDal.GetListAll();
}
public async Task<Category> TGetById(int id)
{
return await _categoryDal.GetByID(id);
}
public async Task TAdd(Category entity)
{
await _categoryDal.Insert(entity);
}
public async Task TDelete(Category entity)
{
await _categoryDal.Delete(entity);
}
public async Task TUpdate(Category entity)
{
await _categoryDal.Update(entity);
}
}
Data Access layer - CategoryRepository:
public class CategoryRepository : GenericRepository<Category>, ICategoryDal
{
}
GenericRepository:
public class GenericRepository<T> : IGenericDal<T> where T : class
{
protected DatabaseContext dbContext;
public GenericRepository(DatabaseContext context)
{
dbContext = context;
}
public async Task Delete(T t)
{
dbContext.Remove(t);
await dbContext.SaveChangesAsync();
}
public IQueryable<T> FindByCondition(Expression<Func<T, bool>> expression)
{
return dbContext.Set<T>()
.Where(expression)
.AsNoTracking();
}
public async Task<T> GetByID(int id)
{
return await dbContext.Set<T>().FindAsync(id);
}
public async Task<List<T>> GetListAll()
{
return await dbContext.Set<T>().ToListAsync();
}
public async Task<List<T>> GetListAll(Expression<Func<T, bool>> filter)
{
return await dbContext.Set<T>()
.Where(filter)
.ToListAsync();
}
public async Task Insert(T t)
{
await dbContext.AddAsync(t);
await dbContext.SaveChangesAsync();
}
public async Task Update(T t)
{
var updatedEntity = dbContext.Entry(t);
updatedEntity.State = EntityState.Modified;
dbContext.SaveChanges();
}
}
UnitOfWork:
public class UnitOfWorkRepository : IUnitOfWork
{
private readonly DatabaseContext _dbContext;
private IDbContextTransaction _transaction;
private bool _disposed;
public UnitOfWorkRepository(DatabaseContext dbContext)
{
_dbContext = dbContext;
}
public bool BeginNewTransaction()
{
try
{
_transaction = _dbContext.Database.BeginTransaction();
return true;
}
catch (Exception ex)
{
return false;
}
}
protected virtual void Dispose(bool disposing)
{
if (!this._disposed)
{
if (disposing)
{
_dbContext.Dispose();
}
}
this._disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public IGenericDal<T> GetRepository<T>() where T : class
{
return new GenericRepository<T>(_dbContext);
}
public bool RollBackTransaction()
{
try
{
_transaction.Rollback();
_transaction = null;
return true;
}
catch (Exception ex)
{
return false;
}
}
public int SaveChanges()
{
var transaction = _transaction != null ? _transaction : _dbContext.Database.BeginTransaction();
using (transaction)
{
try
{
// Context boş ise hata fırlatıyoruz
if (_dbContext == null)
{
throw new ArgumentException("Context is null");
}
// SaveChanges metodundan dönen int result ı yakalayarak geri dönüyoruz.
int result = _dbContext.SaveChanges();
// Sorun yok ise kuyruktaki tüm işlemleri commit ederek bitiriyoruz.
transaction.Commit();
return result;
}
catch (Exception ex)
{
// Hata ile karşılaşılır ise işlemler geri alınıyor
transaction.Rollback();
throw new Exception("Error on SaveChanges ", ex);
}
}
}
}
IUnitOfWork:
public interface IUnitOfWork : IDisposable
{
bool BeginNewTransaction();
bool RollBackTransaction();
IGenericDal<T> GetRepository<T>() where T : class;
int SaveChanges();
}
You can check the source in the link.
https://www.linkedin.com/pulse/repository-unit-work-patterns-net-core-dimitar-iliev/

.NET Core, randomly creating duplicate records

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?

Why i have migration problem in unit test but not in controller c# Web API

Hello i have a project web api in c# and i want to write unit test to check my controller. But i find an error that i really don't understand. When i run my method in controller
public class TherapistsController : ApiController
{
TherapistService _therapistService = new TherapistService();
GeneralService _generalService = new GeneralService();
//GET: api/Therapists/GetAllTherapists
[HttpGet]
[Route("api/Therapists/GetAllTherapists")]
public IHttpActionResult GetTherapist()
{
var therapists = _therapistService.GetAllTherapist();
if (therapists.Count() > 0)
return Ok(therapists);
return NotFound();
}
}
it give me the result and it is fine
But if i run this method in a unit test
[TestClass]
public class UnitTest1
{
[TestMethod]
public void GetAllTherapistByOutletTest()
{
var therapists = new WebAPI.Controllers.TherapistsController();
IHttpActionResult result = therapists.GetTherapist();
Assert.IsInstanceOfType(result, typeof(OkResult));
}
}
it give me the error
Like u see the error says that i need to update database by migration but it still give me same error after i migrate and update database. But when i run the method by calling API ,it still give me the result like the first picture and no error. I debug both ways and they have same steps until method GetAll() in repository like u see in the above picture. I don't really know what wrong ?
Repository
public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
protected readonly SpaDbContext db;
public GenericRepository(SpaDbContext _db)
{
this.db = _db;
}
public void Add(TEntity entity)
{
db.Set<TEntity>().Add(entity);
}
public void AddRange(IEnumerable<TEntity> entities)
{
db.Set<TEntity>().AddRange(entities);
}
public void Detached(TEntity entity)
{
db.Entry<TEntity>(entity).State = EntityState.Detached;
}
public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
{
return db.Set<TEntity>().Where(predicate);
}
public TEntity Get(Expression<Func<TEntity, bool>> predicate)
{
return db.Set<TEntity>().FirstOrDefault(predicate);
}
public TEntity Get(object Id)
{
return db.Set<TEntity>().Find(Id);
}
public IEnumerable<TEntity> GetAll()
{
return db.Set<TEntity>().ToList();
}
public void Remove(TEntity entity)
{
db.Set<TEntity>().Remove(entity);
}
IRepository
namespace Repository
{
public interface IGenericRepository<TEntity> where TEntity : class
{
IEnumerable<TEntity> GetAll();
IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate);
TEntity Get(Expression<Func<TEntity, bool>> predicate);
TEntity Get(object Id);
void Add(TEntity entity);
void AddRange(IEnumerable<TEntity> entities);
void Update(TEntity entity);
//void Remove(object Id);
void Remove(TEntity entity);
void RemoveRange(IEnumerable<TEntity> entities);
void Detached(TEntity entity);
IEnumerable<TEntity> GetByQuery(string query);
}
}
Make sure you have set a valid connection string in test project as web api project
If you want do a real unit test with the controller methods you need use Mock services with this you will not have this kind of problems
https://learn.microsoft.com/en-us/aspnet/web-api/overview/testing-and-debugging/unit-testing-controllers-in-web-api

how wait second operation on my context before first completes with lifestyle PerWebRequest for the context and asynchronous calls to it

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>();
}
...
}

Using SQliteAsyncConnection With UnitOfWork & Repository Pattern

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.

Categories