"在領域層和數據映射層之間進行中介,使用類似集合的接口來操作領域對象." (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
方法使用標準LINQWhere
和ToList
方法在數據源中過濾並獲取People集合。
通用倉儲提供了一些開箱即用的標準 CRUD 功能:
- 提供
Insert
方法用於保存新實體。 - 提供
Update
和Delete
方法通過實體或實體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
而不是依賴 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 集成文檔。