第 5 章 使用 Entity Framework Core
5.3 重构仓储类
创建一个通用仓储接口
namespace Library.API.Services
{
public interface IRepositoryBase<T>
{
Task<IEnumerable<T>> GetAllAsync();
Task<IEnumerable<T>> GetByConditionAsync(Expression<Func<T, bool>> expression);
void Create(T entity);
void Update(T entity);
void Delete(T entity);
Task<bool> SaveAsync();
}
}
继续创建一个接口
namespace Library.API.Services
{
public interface IRepositoryBase2<T,TId>
{
Task<T> GetByIdAsync(TId id);
Task<bool> IsExistAsync(TId id);
}
}
添加 RepositoryBase 类并实现上面两个接口
namespace Library.API.Services
{
public class RepositoryBase<T, TId> : IRepositoryBase<T>, IRepositoryBase2<T, TId> where T : class
{
public DbContext DbContext { get; set; }
public RepositoryBase(DbContext dbContext)
{
DbContext = dbContext;
}
public Task<IEnumerable<T>> GetAllAsync()
{
return Task.FromResult(DbContext.Set<T>().AsEnumerable());
}
public Task<IEnumerable<T>> GetByConditionAsync(Expression<Func<T, bool>> expression)
{
return Task.FromResult(DbContext.Set<T>().Where(expression).AsEnumerable());
}
public void Create(T entity)
{
DbContext.Set<T>().Add(entity);
}
public void Update(T entity)
{
DbContext.Set<T>().Update(entity);
}
public void Delete(T entity)
{
DbContext.Set<T>().Remove(entity);
}
public async Task<bool> SaveAsync()
{
return await DbContext.SaveChangesAsync() > 0;
}
public async Task<T> GetByIdAsync(TId id)
{
return await DbContext.Set<T>().FindAsync(id);
}
public async Task<bool> IsExistAsync(TId id)
{
return await DbContext.Set<T>().FindAsync(id) != null;
}
}
}
这里需要注意的是,EF Core 对于查询的执行采用延迟执行的方法,只有遇到了实际需要结果的操作,查询才会执行,这些操作包括以下几种类型:
- 对结果使用 for 或 foreach 循环
- 使用了 ToList()、ToArray() 和 ToDictionary() 等方法
- 使用了 Single()、Count()、Average、First() 和 Max() 等方法
创建其他仓储接口
public interface IAuthorRepository : IRepositoryBase<Author>, IRepositoryBase2<Author, Guid>
创建实现类
namespace Library.API.Services
{
public class AuthorRepository : RepositoryBase<Author, Guid>, IAuthorRepository
{
public AuthorRepository(DbContext dbContext) : base(dbContext)
{
}
}
}
以同样的方式创建 IBookRepository 与 BookRepository
接着创建仓储包装器 IRepositoryWrapper 及其实现
namespace Library.API.Services
{
public interface IRepositoryWrapper
{
IBookRepository Book { get; }
IAuthorRepository Author { get; }
}
}
namespace Library.API.Services
{
public class RepositoryWrapper : IRepositoryWrapper
{
public LibraryDbContext LibraryDbContext { get; }
private IAuthorRepository _authorRepository = null;
private IBookRepository _bookRepository = null;
public RepositoryWrapper(LibraryDbContext libraryDbContext)
{
LibraryDbContext = libraryDbContext;
}
public IAuthorRepository Author => _authorRepository ?? new AuthorRepository(LibraryDbContext);
public IBookRepository Book => _bookRepository ?? new BookRepository(LibraryDbContext);
}
}
包装器提供了所有仓储接口的统一访问方式,从而避免了单独访问每个仓储接口
接下来要将包装器放到容器中,在 ConfigureServices 注入
services.AddScoped<IRepositoryWrapper, RepositoryWrapper>();
5.4 重构 Controller 和 Action
在重构之前,引入对象映射库 AutoMapper
Install-Package AutoMapper.Extensions.Microsoft.DependencyInjection
在 ConfigureServices 注入
services.AddAutoMapper(typeof(Startup));
为了 AutoMapper 正确执行对象映射,需要创建一个 Profile 类的派生类,用以说明映射的对象以及映射规则
namespace Library.API.Helpers
{
public class LibraryMappingProfile : Profile
{
public LibraryMappingProfile()
{
CreateMap<Author, AuthorDto>()
.ForMember(dest => dest.Age, config =>
config.MapFrom(src => src.BirthData.GetCurrentAge()));
CreateMap<Book, BookDto>();
CreateMap<AuthorForCreationDto, Author>();
CreateMap<BookForCreationDto, Book>();
CreateMap<BookForUpdateDto, Book>();
}
}
}
CreateMap 方法的两个泛型参数分别指明对象映射中的源和目标,当从数据库中获取数据时,实体类为源,而 DTO 为目标;当处理请求时相反
当程序运行时,执行 AddAutoMapper 方法时会扫描指定程序集中 Profile 类的派生类,并根据扫描结果生成映射规则