《ASP.ENT Core 與 RESTful API 開發實戰》-- (第5章)-- 讀書筆記(中)


第 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 類的派生類,並根據掃描結果生成映射規則

知識共享許可協議

本作品采用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。

歡迎轉載、使用、重新發布,但務必保留文章署名 鄭子銘 (包含鏈接: http://www.cnblogs.com/MingsonZheng/ ),不得用於商業目的,基於本文修改后的作品務必以相同的許可發布。

如有任何疑問,請與我聯系 (MingsonZheng@outlook.com) 。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM