仓储

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

    ABP为每个聚合根或实体提供了 默认的通用(泛型)仓储 . 你可以在服务中 使用标准的CRUD操作.

    默认通用仓储用法示例:

    参阅 “IQueryable & 异步操作“ 部分了解如何使用 异步扩展方法, 如 ToListAsync() (建议始终使用异步) 而不是 ToList().

    在这个例子中;

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

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

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

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

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

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

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

    只读仓储

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

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

    自定义仓储

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

    自定义仓储示例

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

    自定义仓储接口

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

    1. public interface IPersonRepository : IRepository<Person, Guid>
    2. {
    3. }

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

    自定义仓储实现

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

    你可以直接使用数据库访问提供程序 (本例中是 DbContext ) 来执行操作.

    IQueryable & 异步操作

    IRepository 继承自 IQueryable,这意味着你可以直接使用LINQ扩展方法. 如上面的泛型仓储示例.

    示例: 使用 Where(...)ToList() 扩展方法

    1. var people = _personRepository
    2. .Where(p => p.Name.Contains(nameFilter))

    .ToList, Count()… 是在 System.Linq 命名空间下定义的扩展方法. (参阅所有方法).

    你通常想要使用 .ToListAsync(), .CountAsync()…. 来编写真正的异步代码.

    但在你使用标准的时会发现无法在应用层或领域层使用这些异步扩展方法,因为:

    • 这里异步方法不是标准LINQ方法,它们定义在Microsoft.EntityFrameworkCoreNuget包中.
    • 标准模板应用层与领域层不引用EF Core 包以实现数据库提供程序独立.

    根据你的需求和开发模式,你可以根据以下选项使用异步方法.

    强烈建议使用异步方法! 在执行数据库查询时不要使用同步LINQ方法,以便能够开发可伸缩的应用程序.

    当你添加NuGet包后,你可以使用全功能的EF Core扩展方法.

    示例: 直接使用 ToListAsync()

    此方法建议;

    • 如果你正在开发一个应用程序并且不打算在将来 更新FE Core,或者如果以后需要更改,你可以容忍它. 如果你正在开发最终的应用程序,这是合理的.

    MongoDB

    如果使用的是MongoDB,则需要将[Volo.Abp.MongoDB] NuGet包添加到项目中. 但在这种情况下你也不能直接使用异步LINQ扩展(例如ToListAsync),因为MongoDB不提供 IQueryable<T>的异步扩展方法,而是提供 IMongoQueryable<T>. 你需要先将查询强制转换为 IMongoQueryable<T> 才能使用异步扩展方法.

    示例: 转换Cast IQueryable<T>IMongoQueryable<T> 并且使用 ToListAsync()

    1. var people = ((IMongoQueryable<Person>)_personRepository
    2. .Where(p => p.Name.Contains(nameFilter)))
    3. .ToListAsync();

    选项-2: 自定义仓储方法

    你始终可以创建自定义仓储方法并使用特定数据库提供程序的API,比如这里的异步扩展方法. 有关自定义存储库的更多信息,请参阅或MongoDb文档.

    此方法建议;

    • 如果你想完全隔离你的领域和应用层和数据库提供程序.

    IAsyncQueryableExecuter 是一个用于异步执行 IQueryable<T> 对象的服务,不依赖于实际的数据库提供程序.

    示例: 注入并使用 IAsyncQueryableExecuter.ToListAsync() 方法

    ApplicationServiceDomainService 基类已经预属性注入了 AsyncExecuter 属性,所以你可直接使用.

    ABP框架使用实际数据库提供程序的API异步执行查询.虽然这不是执行查询的常见方式,但它是使用异步API而不依赖于数据库提供者的最佳方式.

    此方法建议;

    • 如果你正在构建一个没有数据库提供程序集成包的可重用库,但是在某些情况下需要执行 IQueryable<T>对象.

    例如,ABP框架在 CrudAppService 基类中(参阅文档)使用 IAsyncQueryableExecuter.