zoukankan      html  css  js  c++  java
  • 深入浅出-可定制仓储设计

    "在领域层和数据映射层之间进行中介,使用类似集合的接口来操作领域对象." (Martin Fowler)。

    实际上,仓储用于领域对象在数据库(参阅实体)中的操作,通常每个 聚合根 或不同的实体创建对应的仓储。

    通用(泛型)仓储

    ABP为每个聚合根或实体提供了 默认的通用(泛型)仓储 . 你可以在服务中注入 IRepository<TEntity, TKey> 使用标准的CRUD操作. 用法示例:

    public class PersonAppService : ApplicationService
    {
        private readonly IRepository<Person, Guid> _personRepository;
    
        public PersonAppService(IRepository<Person, Guid> personRepository)
        {
            _personRepository = personRepository;
        }
    
        public async Task Create(CreatePersonDto input)
        {
            var person = new Person { Name = input.Name, Age = input.Age };
    
            await _personRepository.InsertAsync(person);
        }
    
        public List<PersonDto> GetList(string nameFilter)
        {
            var people = _personRepository
                .Where(p => p.Name.Contains(nameFilter))
                .ToList();
    
            return people
                .Select(p => new PersonDto {Id = p.Id, Name = p.Name, Age = p.Age})
                .ToList();
        }
    }
    

    在这个例子中:

    • PersonAppService 在它的构造函数中注入了 IRepository<Person, Guid>。
    • Create 方法使用了 InsertAsync 创建并保存新的实体。
    • GetList 方法使用标准LINQ Where 和 ToList 方法在数据源中过滤并获取People集合。

    上面的示例在实体DTO之间使用了手动映射. 参阅 对象映射 了解自动映射的使用方式.

    通用仓储提供了一些开箱即用的标准 CRUD 功能:

    • 提供 Insert 方法用于保存新实体。
    • 提供 Update 和 Delete 方法通过实体或实体id更新或删除实体。
    • 提供 Delete 方法使用条件表达式过滤删除多个实体。
    • 实现了 IQueryable<TEntity>, 所以你可以使用LINQ和扩展方法 FirstOrDefaultWhereOrderByToList 等...
    • 所有方法都具有 sync(同步) 和 async(异步) 版本。

    基础仓储

    IRepository<TEntity, TKey> 接口扩展了标准 IQueryable<TEntity> 你可以使用标准LINQ方法自由查询.但是,某些ORM提供程序或数据库系统可能不支持IQueryable接口。

    ABP提供了 IBasicRepository<TEntity, TPrimaryKey> 和 IBasicRepository<TEntity> 接口来支持这样的场景. 你可以扩展这些接口(并可选择性地从BasicRepositoryBase派生)为你的实体创建自定义存储库。

    依赖于 IBasicRepository 而不是依赖 IRepository 有一个优点, 即使它们不支持 IQueryable 也可以使用所有的数据源, 但主要的供应商, 像 Entity Framework, NHibernate 或 MongoDb 已经支持了 IQueryable。

    因此, 使用 IRepository 是典型应用程序的 建议方法. 但是可重用的模块开发人员可能会考虑使用 IBasicRepository 来支持广泛的数据源。

    只读仓储

    对于想要使用只读仓储的开发者,我们提供了IReadOnlyRepository<TEntity, TKey> 与 IReadOnlyBasicRepository<Tentity, TKey>接口。

    无主键的通用(泛型)仓储

    如果你的实体没有id主键 (例如, 它可能具有复合主键) 那么你不能使用上面定义的 IRepository<TEntity, TKey>, 在这种情况下你可以仅使用实体(类型)注入 IRepository<TEntity>。

    IRepository<TEntity> 有一些缺失的方法, 通常与实体的 Id 属性一起使用。由于实体在这种情况下没有 Id 属性, 因此这些方法不可用. 比如 Get 方法通过id获取具有指定id的实体. 不过, 你仍然可以使用IQueryable<TEntity>的功能通过标准LINQ方法查询实体。

    自定义仓储

    对于大多数情况, 默认通用仓储就足够了. 但是, 你可能会需要为实体创建自定义仓储类.

    自定义仓储示例

    ABP不会强制你实现任何接口或从存储库的任何基类继承. 它可以只是一个简单的POCO类。但是建议继承现有的仓储接口和类,获得开箱即用的标准方法使你的工作更轻松。

    自定义仓储接口

    首先在领域层定义一个仓储接口:

    public interface IPersonRepository : IRepository<Person, Guid>
    {
        Task<Person> FindByNameAsync(string name);
    }

    此接口扩展了 IRepository<Person, Guid> 以使用已有的通用仓储功能。

    自定义仓储实现

    自定义存储库依赖于你使用的数据访问工具。在此示例中,我们将使用Entity Framework Core:

    public class PersonRepository : EfCoreRepository<MyDbContext, Person, Guid>, IPersonRepository
    {
        public PersonRepository(IDbContextProvider<TestAppDbContext> dbContextProvider) 
            : base(dbContextProvider)
        {
    
        }
    
        public async Task<Person> FindByNameAsync(string name)
        {
            return await DbContext.Set<Person>()
                .Where(p => p.Name == name)
                .FirstOrDefaultAsync();
        }
    }
    

    你可以直接使用数据库访问提供程序 (本例中是 DbContext ) 来执行操作。有关基于EF Core的自定义仓储的更多信息,请参阅EF Core 集成文档

  • 相关阅读:
    块设备驱动、bio理解
    configfs_sample.c 理解
    configfs-用户空间控制的内核对象配置
    infiniswap安装
    virtualBox环境下安装centos7,设置虚拟主机和本地主机网络互通的几个关键步骤
    知识点-web
    SpringSecutiry源码探究(DAO密码认证)
    知识点-线程
    知识点-基础
    keypoint
  • 原文地址:https://www.cnblogs.com/fxck/p/13076358.html
Copyright © 2011-2022 走看看