1. 前言
在上一篇博文中 http://www.cnblogs.com/xiyin/p/6810350.html 我們講到了ABP領域層的實體,這篇博文繼續講ABP的領域層,這篇博文的主題是ABP領域層—倉儲。我們在上篇博文中介紹的ABP領域層的大緻結構,在這篇文章就不一一贅述了。詳情可以檢視上篇博文。接下來直接進入主題。倉儲的定義是這樣的:
在領域層和資料映射層的中介,使用類似集合的接口來存取領域對象----Martin Fowler。
倉儲被用于領域對象在資料庫上的操作(實體
Entity
和值對象
Value types
),一般來說,我們針對不同的實體(或聚合根
Aggregate Root
) 會建立相對應的倉儲。
疑問:“那是不是對每個實體都建立相應的倉儲呢?”
本文講述的結構如下:

接下來,我将一一講述。
2.ABP領域層—倉儲
2.1 IRepository 接口
在ABP中,倉儲類要實作
IReposiotry
接口。最好的方式是針對不同倉儲對象定義各自不同的接口。
如果你的實體
ID
是
int
類型的,那麼可以使用如下定義:
public interface IPersonREpository : IRepository<Person>
{
//...
}
如果不是
int
類型的,可以使用如下定義:
public interface IPersonRepository : IRepository<Person, long>
{
//...
}
對于倉儲類,
IRepository
定義了許多泛型的方法。比如
Insert
,
Select
Update
Delete
, 詳情可以在源碼的
Abp.Domain.Repositories
程式集中詳細檢視。
接下來對
CRUD
進行一一的介紹。
2.1.1 查詢(Query)
*取得單一實體:
TEntity Get(TPrimaryKey id);
Task<TEntity> GetAsync(TPrimaryKey id);
TEntity Single(Expression<Func<TEntity,bool>> predicate);
TEntity FirstOrDefault(TPrimaryKey id);
Task<TEntity> FirstOrDefaultAsync(TPrimaryKey id); TEntity FirstOrDefault(Expression<Func<TEntity, bool>> predicate); Task<TEntity> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate); TEntity Load(TPrimaryKey id)
Get
方法被用于根據主鍵值(
Id
)取得對應的實體。當資料庫中根據主鍵值找不到相符合的實體時,它會抛出異常。
主要講一下
Single
方法的執行個體,因為參數是一個表達式:
var person = _personRepository.Get(42);
var person = _personRepository.Single(p => p.Name == "Robert");
Tip1:
Single
方法在給出的條件找不到實體或符合的實體超過一個以上時,都會抛出異常。
Tip2:
FirstOrDefault
也一樣,但是當沒有符合
Lambda
表達式或
Id
的實體時,會傳回
null
(取代抛出異常)。當有超過一個以上的實體符合條件,它隻會傳回第一個實體。
Tip3:
Load
并不會從資料庫中檢索實體,但它會建立延遲執行所需的代理對象。如果你隻使用
Id
屬性,實際上并不會檢索實體,它隻有在你存取想要查詢實體的某個屬性時才會從資料庫中查詢實體。
*取得實體清單:
List<TEntity> GetAllList(); Task<List<TEntity>> GetAllListAsync(); List<TEntity> GetAllList(Expression<Func<TEntity, bool>> predicate); Task<List<TEntity>> GetAllListAsync(Expression<Func<TEntity, bool>> predicate); IQueryable<TEntity> GetAll();
GetAllList
被用于從資料中檢索所有實體,重載并且提供過濾實體的功能,
var allPeople = _personRepository.GetAllList();
var somePeople = _personRepository.GetAllList(person => person.IsActive && person.Age > 20 );
GetAll
傳回
IQueryable<T>
類型的對象。因為是
IQueryable
類型的,是以我們可以在調用完成後,進行
Linq
操作。相關的
Linq
操作函數,可以檢視我的這篇博文。示例:
var query = from person in _personRepository.GetAll()
where person.IsActive
orderby person.Name
select person;
var people = query.ToList();
List<Person> personList2 = _personRepository.GetAll().Where(p =>p.Name.Contains("H")).OrderBy(p => p.Name).Skip(40).Take(20).ToList();
*自定義傳回值
ABP 有一個額外的方法來實作
IQueryable<T>
的延遲加載效果,而不需要在調用的方法上添加
UnitOfWork
這個屬性卷标。
T Query<T> (Func<IQueryable<TEnity>,T> queryMethod);
查詢方法接受
Lambda
(或一個方法)來接受
IQueryabel<T>
并且傳回任何對象類型。示例:
var person = _personRepository.Query(q => q.Name.Contains("H").OrderBy(p => p.Name).ToList());
2.1.2 新增
IRepository
接口定義了如下方法來新增一個實體到資料庫。
TEntity Insert(TEntity entity); Task<TEntity> InsertAsync(TEntity entity); TPrimaryKey InsertAndGetId(TEntity entity); Task<TPrimaryKey> InsertAndGetIdAsync(TEntity entity); TEntity InsertOrUpdate(TEntity entity); Task<TEntity> InsertOrUpdateAsync(TEntity entity); TPrimaryKey InsertOrUpdateAndGetId(TEntity entity); Task<TPrimaryKey> InsertOrUpdateAndGetIdAsync(TEntity entity);
Tip: 新增方法會新增實體到資料庫并且傳回相同的已新增實體。
InsertAndGetId
方法會傳回新增實體的辨別符(
Id
)。當我們采用自動遞增辨別符值且需要取得實體的新産生标志符值時非常好用。
2.1.3 更新
TEntity Update(TEntity entity);
Task<TEntity> UpdateAsync(TEntity entity)
2.1.4 删除
void Delete(TEntity entity);
Task DeleteAsync(TEntity entity);
void Delete(TPrimaryKey id);
Task DeleteAsync(TPrimaryKey id);
void Delete(Expression<Func<TEntity, bool>> predicate);
Task DeleteAsync(Expression<Func<TEntity, bool>> predicate);
2.1.5 其他方法
int Count();
Task<int> CountAsync();
int Count(Expression<Func<TEntity, bool>> predicate);
Task<int> CountAsync(Expression<Func<TEntity, bool>> predicate); Long LongCount();
Task<long> LongCountAsync();
Long LongCount(Expression<Func<TEntity, bool>> predicate); Task<long> LongCountAsync(Expression<TEntity, bool>> predicate)
2.2 倉儲的實作
ABP在設計上是采取不指定
ORM
架構或其它存取資料庫技術的方法。隻要實作
IRepository
接口,任何架構都可以使用。
當你使用
NHIbernate
或
EntiyFramework
,如果提供的方法已足夠使用,你就不需要為你的實體建立倉儲對象了。我們可以直接注入
IRepository<TEntiy>
IRepository<TEntity,TPrimaryKey>
下面執行個體為
application service
使用倉儲來新增實體到資料庫:
public class PersonAppService : I PersonAppService
{
private readonly IRepository<Person> _personRepository;
public PersonAppService(IRepository<Person> personRepository)
{
_personRepository = personRepository;
}
public void CreatePerson(CreatePersonInput input)
{
person = new Person {Name = input.Name , EmailAddress = input.EmailAddress};
}
_personRepository.Insert(person);
}
2.3 管理資料庫連接配接
資料庫連接配接的開啟和關閉,在倉儲方法中,ABP會自動化的進行連接配接管理。
當倉儲方法被調用後,資料庫連接配接會自動開啟且啟動事務。當倉儲方法執行結束并且傳回以後,所有的實體變化都會被儲存,事務被送出并且資料庫連接配接被關閉,一切都由ABP自動化的控制。如果倉儲方法抛出任何類型的異常,事務會自動地復原并且資料連接配接會被關閉。
如果倉儲方法調用其他倉儲方法(即便是不同的倉儲的方法),它們共享同一個連接配接和事務。連接配接會由倉儲方法調用鍊最上層的那個倉儲方法所管理。
2.4 倉儲的生命周期
所有的倉儲對象都是暫時性的,這就是說,它們是在由需要的時候才會被建立。ABP大量的使用依賴注入,當倉儲類需要被注入的時候,新的類實體會由注入容器自動地建立。
2.5 倉儲的最佳實踐
- 對于一個T類型的實體,是可以使用
。但别任何情況下都建立定制化的倉儲,除非我們真的很需要。預定于倉儲方法已經足夠應付各種案例。IRepository<T>
- 建立定制的倉儲可以實作
IRepository<TEntity>
- 倉儲類應該是無狀态的。這意味着,你不該定義倉儲等級的狀态對象并且倉儲方法的調用也不應該影響到其他調用。
- 當倉儲可以使用像 依賴注入,盡可能較少,或不根據其他服務。
3. 結語
正好讓我回顧回顧之前閱讀的書。雖說都是基礎内容,但也加深了其了解,下一篇将會講 工作單元。聽說是最看好的。😄。哈哈。
4.參考文獻
- https://www.aspnetboilerplate.com/Pages/Documents