В последнее время я реализовал шаблон репозитория и единицу работы (используя общий репозиторий), потому что это помогает модульному тестированию. Есть много учебных пособий, которые конфликтуют друг с другом.
У меня есть несколько вопросов, связанных с моим кодом ниже:
- В настоящее время я добавляю
IUnitOfWork/UnitOfWork
в качестве службы области вStartup.cs. Я не совсем уверен, должен ли это быть один экземпляр (синглтон) или новый экземпляр (в области видимости), или даже временный. - Я пропустил что-то еще, кроме реализации
IDisposable
в UnitOfWork? - Что-то еще не так с моим шаблоном репозитория / реализацией UnitOfWork?
IUnitOfWork.cs
using System.Threading.Tasks;
namespace SaveBees.Repositories
{
public interface IUnitOfWork
{
IUserRepository Users { get; }
IRoleRepository Roles { get; }
Task<int> SaveAsync();
}
}
UnitOfWork.cs
using System;
using System.Threading.Tasks;
namespace SaveBees.Repositories
{
public class UnitOfWork : IUnitOfWork, IDisposable
{
private readonly SaveBeesContext _context;
public UnitOfWork(SaveBeesContext context)
{
_context = context;
}
private IRoleRepository _roleRepository;
public IRoleRepository Roles
{
get
{
if (_roleRepository == null)
{
_roleRepository = new RoleRepository(_context);
}
return _roleRepository;
}
}
private IUserRepository _userRepository;
public IUserRepository Users
{
get
{
if (_userRepository == null)
{
_userRepository = new UserRepository(_context);
}
return _userRepository;
}
}
public async Task<int> SaveAsync()
{
return await _context.SaveChangesAsync();
}
private bool _disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_context.Dispose();
}
}
_disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}
IGeneralRepository.cs
using SaveBees.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
namespace SaveBees.Repositories
{
public interface IGenericRepository<TEntity> where TEntity : class, IEntity
{
Task<List<TEntity>> GetAllAsync(Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, Func<IQueryable<TEntity>, IQueryable<TEntity>> includes = null);
Task<List<TEntity>> QueryAsync(Expression<Func<TEntity, bool>> filter, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, Func<IQueryable<TEntity>, IQueryable<TEntity>> includes = null);
Task<TEntity> GetByIdAsync(int id, Func<IQueryable<TEntity>, IQueryable<TEntity>> includes = null);
Task<TEntity> SingleOrDefaultAsync(Expression<Func<TEntity, bool>> filter, Func<IQueryable<TEntity>, IQueryable<TEntity>> includes = null);
Task AddAsync(TEntity entity);
void Update(TEntity entity);
void Remove(TEntity entity);
}
}
GeneralRepository.cs
using Microsoft.EntityFrameworkCore;
using SaveBees.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
namespace SaveBees.Repositories
{
public abstract class GenericRepository<TEntity, TContext> : IGenericRepository<TEntity>
where TEntity : class, IEntity
where TContext : DbContext
{
private readonly TContext _context;
public GenericRepository(TContext context)
{
_context = context;
}
public async Task<List<TEntity>> GetAllAsync(Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, Func<IQueryable<TEntity>, IQueryable<TEntity>> includes = null)
{
IQueryable<TEntity> query = _context.Set<TEntity>();
if (includes != null)
{
query = includes(query);
}
if (orderBy != null)
{
query = orderBy(query);
}
return await query.ToListAsync();
}
public async Task<List<TEntity>> QueryAsync(Expression<Func<TEntity, bool>> filter, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, Func<IQueryable<TEntity>, IQueryable<TEntity>> includes = null)
{
IQueryable<TEntity> query = _context.Set<TEntity>();
if (filter != null)
{
query = query.Where(filter);
}
if (includes != null)
{
query = includes(query);
}
if (orderBy != null)
{
query = orderBy(query);
}
return await query.ToListAsync();
}
public async Task<TEntity> GetByIdAsync(int id, Func<IQueryable<TEntity>, IQueryable<TEntity>> includes = null)
{
IQueryable<TEntity> query = _context.Set<TEntity>();
if (includes != null)
{
query = includes(query);
}
return await query.SingleOrDefaultAsync(x => x.Id == id);
}
public async Task<TEntity> SingleOrDefaultAsync(Expression<Func<TEntity, bool>> filter, Func<IQueryable<TEntity>, IQueryable<TEntity>> includes = null)
{
IQueryable<TEntity> query = _context.Set<TEntity>();
if (includes != null)
{
query = includes(query);
}
return await query.SingleOrDefaultAsync(filter);
}
public async Task AddAsync(TEntity entity)
{
await _context.Set<TEntity>().AddAsync(entity);
}
public void Update(TEntity entity)
{
_context.Set<TEntity>().Update(entity);
}
public void Remove(TEntity entity)
{
_context.Remove(entity);
}
}
}
IUserRepository.cs
using SaveBees.Models;
using System.Threading.Tasks;
namespace SaveBees.Repositories
{
public interface IUserRepository : IGenericRepository<User>
{
Task<User> GetByEmailAsync(string email);
}
}
UserRepository.cs
using Microsoft.EntityFrameworkCore;
using SaveBees.Models;
using System.Threading.Tasks;
namespace SaveBees.Repositories
{
public class UserRepository : GenericRepository<User, SaveBeesContext>, IUserRepository
{
public UserRepository(SaveBeesContext context) : base(context)
{
}
public async Task<User> GetByEmailAsync(string email)
=> await SingleOrDefaultAsync(e => e.Email == email,
e => e.Include(u => u.UserRoles)
.ThenInclude(ur => ur.Role));
}
}
IEntity.cs
namespace SaveBees.Data
{
public interface IEntity
{
public int Id { get; set; }
}
}