Я хочу использовать Shared Service, который включает в себя некоторые общие методы, такие как создание, обновление, удаление на моем бизнес-уровне. Я уже реализовал репозиторий и рабочие классы, но столкнулся с некоторыми проблемами при попытке создать общий сервис. Давайте предположим, что у нас есть неразделяемый метод создания, подобный этому:
public class ProductService : IProductService
{
private readonly IUnitOfWork _unitOfWork;
public ProductService(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public async Task<Product> CreateProduct(Product newProduct)
{
await _unitOfWork.Products.AddAsync(newProduct);
await _unitOfWork.CommitAsync();
return newProduct;
}
}
Часть, которая меня смущает в приведенном выше коде, заключается в том, что я вызываю свой UnitOfWork с командой _unitOfWork.Product, как нам преобразовать его в unitOfWork.TEntity, чтобы сделать его универсальным? Является ли это возможным? В этом случае я попытался сделать общий, но я думаю, что нет такой вещи, как _unitOfWork.TEntity. Мне просто нужно отредактировать класс Service, я добавляю другие связанные классы, чтобы предоставить дополнительную информацию.
Услуга:
public class Service<TEntity>: IService<TEntity> where TEntity : class
{
private readonly IUnitOfWork _unitOfWork;
public Service(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public async Task<TEntity> AddAsync(TEntity entity)
{
await _unitOfWork.TEntity.AddAsync(entity);
await _unitOfWork.CommitAsync();
return entity;
}
}
IService:
public interface IService<TEntity> where TEntity : class
{
Task<TEntity> AddAsync(TEntity entity);
}
Репозиторий:
public abstract class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
private readonly DbContext _context;
private readonly DbSet<TEntity> _dbSet;
public Repository(ECommerceDbContext context)
{
_context = context;
_dbSet = context.Set<TEntity>();
}
public async Task<TEntity> AddAsync(TEntity entity)
{
entity.CreateDate = DateTime.Now;
await _dbSet.AddAsync(entity);
await SaveAsync();
return entity;
}
public async Task AddAsync(IEnumerable<TEntity> entities)
{
foreach (var item in entities)
{
await AddAsync(item);
}
}
public async Task<bool> AnyAsync(Expression<Func<TEntity, bool>> expression)
{
return await _dbSet.AnyAsync(expression);
}
public async Task<bool> AnyAsync()
{
return await AnyAsync(x => true);
}
public async Task<long> CountAsync()
{
return await CountAsync(x => true);
}
public async Task<long> CountAsync(Expression<Func<TEntity, bool>> expression)
{
return await _dbSet.LongCountAsync(expression);
}
public void Delete(TEntity model)
{
_dbSet.Remove(model);
}
public async Task DeleteAsync(int id)
{
var entity = await GetAsync(id);
Delete(entity);
}
public async Task<TEntity> FirstOrDefault(Expression<Func<TEntity, bool>> expression)
{
return await _dbSet.FirstOrDefaultAsync(expression);
}
public async Task<TEntity> GetAsync(int id)
{
return await FirstOrDefault(x => x.Id == id);
}
public async Task<IEnumerable<TEntity>> GetAll()
{
return await _dbSet.ToListAsync();
}
public async Task SaveAsync()
{
await _context.SaveChangesAsync();
}
public async Task<TEntity> Update(TEntity entity)
{
var temp = GetAsync(entity.Id);
entity.UpdateDate = DateTime.Now;
_context.Entry(temp).CurrentValues.SetValues(entity);
await SaveAsync();
return await temp;
}
}
IРепозиторий:
public interface IRepository<TEntity> where TEntity :class
{
Task<TEntity> AddAsync(TEntity entity);
Task AddAsync(IEnumerable<TEntity> entities);
Task<bool> AnyAsync(Expression<Func<TEntity, bool>> expression);
Task<bool> AnyAsync();
Task<long> CountAsync();
Task<long> CountAsync(Expression<Func<TEntity, bool>> expression);
void Delete(TEntity model);
Task DeleteAsync(int id);
Task<TEntity> FirstOrDefault(Expression<Func<TEntity, bool>> expression);
Task<TEntity> GetAsync(int id);
Task<IEnumerable<TEntity>> GetAll();
Task SaveAsync();
Task<TEntity> Update(TEntity entity);
}
Единица Работы:
public class UnitOfWork:IUnitOfWork
{
private readonly ECommerceDbContext _context;
private ProductRepository _productRepository;
public UnitOfWork(ECommerceDbContext context)
{
_context = context;
}
public IProductRepository Products => _productRepository = _productRepository ?? new ProductRepository(_context);
public async Task<int> CommitAsync()
{
return await _context.SaveChangesAsync();
}
public void Dispose()
{
_context.Dispose();
}
}
IUnitOfWork
public interface IUnitOfWork : IDisposable
{
IProductRepository Products { get; }
Task<int> CommitAsync();
}
Могу ли я реализовать универсальный метод AddAsync, который может быть полезен для всех сущностей? Как я могу использовать шаблон обратного вызова action/func? Можете ли вы объяснить больше?
Я добавил ответ с примером, объясняющим подход.
Имейте в виду, что общий репозиторий считается анти-шаблоном.
Служба TEntity : class .. Я думаю, здесь класс TEntity : Entity.
Будет полезно, если вы поделитесь своими объектами IProductRepository и ProductRepository.
public interface IService<TEntity> where TEntity : Entity
{
Task<TEntity> AddAsync(TEntity entity);
}
public class Service<TEntity> : IService<TEntity> where TEntity : Entity
{
private readonly IUnitOfWork _unitOfWork;
public Service(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public async Task<TEntity> AddAsync(TEntity entity)
{
//await _unitOfWork.TEntity.AddAsync(entity);
await _unitOfWork.Products.AddAsync(entity);
await _unitOfWork.CommitAsync();
return entity;
}
}
public class ProductService : Service<Product>,IProductService
{
public ProductService(IUnitOfWork unitOfWork):base(unitOfWork)
{
}
}
public class UnitOfWork : IUnitOfWork
{
private readonly ECommerceDbContext _context;
private IProductRepository _productRepository;
public UnitOfWork(ECommerceDbContext context)
{
_context = context;
}
public IProductRepository Products => _productRepository = _productRepository ?? new ProductRepository(_context);
public async Task<int> CommitAsync()
{
return await _context.SaveChangesAsync();
}
public void Dispose()
{
_context.Dispose();
}
}
Братан, это не универсально, представьте, что это много сущностей, таких как Продукт. Другие организации также будут использовать этот метод. Таким образом, вы не можете добавить сюда _unitOfWork.Products. ProductRepository, унаследованный от Repository, прямо сейчас использует одни и те же методы.
Ой! Я пропустил это. Нужна общая единица работы. Попробуйте на stackoverflow.com/questions/39343750/generic-unit-of-work
Кажется, Generic UnitOfWork решит проблему, но я не могу реализовать Generic UnitOfWork. Любая помощь будет приветствоваться.
Поскольку каждый тип сущности является строго типизированным объектом, вы не можете обобщить свою реализацию без отражения. Распространенным обходным путем является предоставление универсального обратного вызова, в то же время позволяя инициатору предоставлять конкретное сопоставление для каждого типа объекта.
public class Service<TEntity> : IService<TEntity> where TEntity : class
{
private readonly IUnitOfWork _unitOfWork;
public Service(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public async Task<TEntity> AddAsync(TEntity entity,
Func<TEntity, IUnitOfWork, Task> addEntityCallback)
{
await addEntityCallback(entity, _unitOfWork);
await _unitOfWork.CommitAsync();
return entity;
}
}
public interface IService<TEntity> where TEntity : class
{
Task<TEntity> AddAsync(TEntity entity, Func<TEntity, IUnitOfWork, Task> addEntityCallback);
}
Затем вы можете вызвать IService.AddAsync
с определенным сопоставлением:
public class ProductService : IProductService
{
private readonly IService<Product> _service;
public ProductService(IService<Product> service)
{
_service = service;
}
public async Task<Product> CreateProduct(Product newProduct)
{
await _service.AddAsync(newProduct,
(entity, unitOfWork) => unitOfWork.Products.AddAsync(entity));
return newProduct;
}
}
Обновление: в случае, если вы хотите всегда наследовать Service<TEntity>
(согласно вашему комментарию), вы можете использовать абстрактный метод, который работает аналогично параметру обратного вызова. Это позволяет вам по-прежнему инкапсулировать логику в ProductService
, но теперь вам больше не требуется предоставлять метод создания.
public class ProductService : Service<Product>, IProductService
{
public ProductService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
}
protected override async Task<Product> AddEntityCallback(Product entity,
IUnitOfWork unitOfWork)
{
await unitOfWork.Products.AddAsync(entity);
return entity;
}
}
public abstract class Service<TEntity> : IService<TEntity> where TEntity : class
{
private readonly IUnitOfWork _unitOfWork;
public Service(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
protected abstract Task<TEntity> AddEntityCallback(TEntity entity,
IUnitOfWork unitOfWork);
public async Task<TEntity> AddAsync(TEntity entity)
{
await AddEntityCallback(entity, _unitOfWork);
await _unitOfWork.CommitAsync();
return entity;
}
}
Здесь есть небольшое недоразумение. Product Service должен наследоваться от базового класса Service, это моя вина, я забыл его указать. Таким образом, нам не нужно будет снова и снова определять методы вставки, обновления, удаления в каждом сервисе. Другими словами, в ProductService не должно быть CreateProduct, я планировал вызывать его из класса Service в Controller.
@alihangir ааа, из твоего первоначального вопроса этого не было ясно. Я обновил свой ответ. Вы все еще можете справиться с этим, используя абстрактный метод. Вы должны где-то инкапсулировать логику, и выполнение ее в конкретном, знающем классе облегчит поддержку в будущем.
У меня есть действие ProductController и async Task<IActionResult> NewProduct(SaveProductDto saveProductDto) в контроллере. Мне просто нужно вызвать AddAsync в этом действии из ProductService, унаследовав его от базовой службы. Например, _productService.AddAsync(сделать что-нибудь)
Верно, и обновленный подход позволит это сделать.
Работает очень хорошо, спасибо. Я должен упомянуть, что ProductService также хотел реализовать это: async Task<Product> IProductService.AddEntityCallback(Product entity, IUnitOfWork unitOfWork). Я не знаю, почему это заставило меня сделать это. У нас есть как 1) защищенное асинхронное переопределение Task<Product> AddEntityCallback (сущность продукта, IUnitOfWork unitOfWork), так и 2) async Task<Product> IProductService.AddEntityCallback (сущность продукта, IUnitOfWork unitOfWork) одновременно.
Если универсальной реализации, отвечающей вашим потребностям, нет, вы всегда можете использовать шаблон обратного вызова action/func, чтобы применить обновление к экземпляру с заданной областью действия.