程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> <<ABP框架>> 倉儲,abp框架倉儲

<<ABP框架>> 倉儲,abp框架倉儲

編輯:關於.NET

<<ABP框架>> 倉儲,abp框架倉儲


文檔目錄

 

本節內容:

  • 默認倉儲
  • 自定義倉儲
    • 自定義倉儲接口
    • 自定義倉儲實現
  • 基倉儲方法
    • 查詢
      • 獲取單個實體
      • 獲取實體列表
    • 關於 IQueryable
      • 自定義返回值
    • 插入
    • 更新
    • 刪除
    • 其它
    • 關於異步方法
  • 管理數據庫連接
  • 一個倉儲的生命周期
  • 倉儲最佳實踐

 

領域和映射層之間的媒介使用一種類似集合的接口來訪問實體。通常地,每個實體(或聚合根)使用一個分離的倉儲。

 

默認倉儲

在ABP裡,一個倉儲類實現IRepository<TEntity,TPrimaryKey>接口。ABP默認地為每個實體類型自動創建一個默認倉儲。你可以直接注入IRepository<TEntity>(或IRepository<TEntity,TPrimaryKey>)。一個應用服務使用倉儲把一個實體插入數據庫的例子:

public class PersonAppService : IPersonAppService
{
    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);
    }
}

PersonAppService構造器注入IRepository<Person>並使用Insert方法。

 

自定義倉儲

只有當實體需要創建一個自定義的倉儲方法時,才需要你創建一個倉儲類。

 

自定義倉儲接口

如下示例,為一個Person實體定義一個倉儲:

public interface IPersonRepository : IRepository<Person>
{

}

IPersonRepository擴展了IRepository<TEntity>,它用來定義具有int(Int32)類型Id屬性的實體。如果你的實體鍵不是int,你可以擴展IRepository<TEntity,TPrimaryKey>接口,如下所示:

public interface IPersonRepository : IRepository<Person, long>
{

}

 

自定義倉儲實現

ABP設計成與ORM(對象/關系映射)框架分離、或其它訪問數據庫技術分離。倉儲開箱即用地實現了NHibernate和EntityFramework。查看ABP對這些框架的實現的相關文檔:

  • NHibernate integration
  • EntityFramework integration

 

基倉儲方法

每個倉儲包含一些通用的來自IRepository<TEntity>接口的方法,我們在此把它們的大部分方法,研究一下。

 

查詢

獲取一個單獨實體

TEntity Get(TPrimaryKey id);
Task<TEntity> GetAsync(TPrimaryKey id);
TEntity Single(Expression<Func<TEntity, bool>> predicate);
Task<TEntity> SingleAsync(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)的實體。如果無法從數據庫中找到給定Id的實體,將拋出異常。Single方法類似於Get方法,但接受一個表達式,而不是一個Id,所以你可以寫一個lambda表達式來獲取一個實體,用法示例:

var person = _personRepository.Get(42);
var person = _personRepository.Single(p => p.Name == "Halil İbrahim Kalkan");

注意:Single會在無法獲取符合表達式的實體,或是有多個符合表達式的實體時,拋出異常。

FirstOrDefault類似,但在找不到給定Id的實體時,返回null(代替拋出異常)。如果找到多個實體,則返回第一個。

Load不從數據庫獲取實體,但為延遲加載創建一個代理對象。如果你只是使用Id屬性,那麼實質上,不會從數據庫中獲取實體,只有當你訪問實體的其它屬性時,它才從數據庫中獲取實體。出於性能考慮,這個方法用來代替Get。它已經在NHibernate中實現了。如果ORM供應器沒有實現它,Load方法就跟Get方法是一樣的。

 

獲取一個實體列表

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 > 42);

GetAll返回IQueryable<T>,所以你在這個方法後可以添加Linq方法,例如:

//Example 1
var query = from person in _personRepository.GetAll()
            where person.IsActive
            orderby person.Name
            select person;
var people = query.ToList();

//Example 2:
List<Person> personList2 = _personRepository.GetAll().Where(p => p.Name.Contains("H")).OrderBy(p => p.Name).Skip(40).Take(20).ToList();

通過實例GetAll,幾乎可以把寫所有查詢寫在Linq裡,甚至它可以用在一個json表達式裡。

關於 IQueryable<T>

當你在一個倉儲方法之外調用GetAll(),必須有一個打開的數據庫連接,這是因為IQueryable<T>是延遲執行的。它不會執行數據庫的查詢,除非你調用ToList()方法或在一個foreach循環(或其它方式訪問查詢裡的項)。所以,當你調用ToList()方法時,數據庫連接必須可用。對於一個Web項目,在部分情況你不必關心這個,因為Mvc控制器方法默認都是工作單元,且數據庫連接在整個請求裡都是可用的。為更好地理解它,請查看工作單元文檔。

 

自定義返回值

還有一個另外的方法提供更強大的IQueryable,可以用在工作單元之外。

T Query<T>(Func<IQueryable<TEntity>, T> queryMethod);

Query方法接受一個lambda表達式(或方法),該表達式(或方法)接收IQueryable<T>並返回任何類型的對象。例如:

var people = _personRepository.Query(q => q.Where(p => p.Name.Contains("H")).OrderBy(p => p.Name).ToList());

由於給定的lambda(或方法)在倉儲方法在執行,當數據庫連接可用時,它被執行。你可以在執行查詢後,返回實體列表、單個實體、一個投射或其它。

 

插入

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);

Insert方法簡單地把一個新實體插入到數據庫,並返回此插入的實體。InsertAndGetId方法為新插入的實體返回Id,當Id是自增時非常有用。InsertOrUpdate根據Id值執行插入或更新操作。最後,InsertOrUpdateAndGetId在插入或更新實體後,返回它的Id值。

 

更新

IRepository定義了更新一個已存在於數據庫的實體的方法,它獲取一個需要更新的實體,返回相同的實體。

TEntity Update(TEntity entity);
Task<TEntity> UpdateAsync(TEntity entity);

大部分時間,你不需要顯式地調用Update方法,因為工作單元會在完成時調用Update方法。見工作單元文檔獲取更多信息。

 

刪除

IRepository定義了從數據庫刪除一個已存在的實體的方法。

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);

第一個方法接受一個已存在的實體,第二個接受要刪除實體的Id。最後一個根據給定條件刪除所有符合的實體,注意:所有匹配謂詞的實體可能會從數據庫中先獲取到內存,然後再刪除(看倉儲如何實現了),所以使用它要小心了,這在有大量符合條件的實體時,可能引起性能問題。

 

其它

IRepository同時也提供了獲取一個表的實體數量的方法

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<Func<TEntity, bool>> predicate);

 

關於異步方法

ABP支持異常編程模式,所以倉儲方法有異步版本。如下例子為一個應用服務方法使用異常模式:

public class PersonAppService : AbpWpfDemoAppServiceBase, IPersonAppService
{
    private readonly IRepository<Person> _personRepository;

    public PersonAppService(IRepository<Person> personRepository)
    {
        _personRepository = personRepository;
    }

    public async Task<GetPeopleOutput> GetAllPeople()
    {
        var people = await _personRepository.GetAllListAsync();

        return new GetPeopleOutput
        {
            People = Mapper.Map<List<PersonDto>>(people)
        };
    }
}

GetAllPeople方法是一個異步方式,並使用關鍵字await調用GetAllListAsync。

可能不是所有的ORM框架都支持異步。EntityFramework支持。如果不支持,異步方法以同步的方式工作。同樣的,例如,在EntityFramework中InsertAsync和Insert工作方式相同,因為EF直到工作單元完成前(也就是DbContext.SaveChanges),代碼不寫入新的實體。

 

管理數據庫連接

在一個倉儲方法裡,它不打開或關閉數據庫連接,ABP自動管理數據庫連接。

當進入一個倉儲方法,ABP自動打開一個數據庫連接並開始一個事務,當這個方法結束並返回時,所有的變化被保存,事務提交後關閉數據庫連接。如果你的倉儲方法拋出任何類型的異常,自動回滾事務並關閉數據庫連接。這適用於所有實現IRepository接口的類的公開方法。

如果一個倉儲方法調用另一個倉儲方法(即使是一個不同倉儲的方法),它們共享相同的連接和事務,第一個方法管理數據庫的連接(打開/關閉)。獲取更多的數據庫連接管理信息,請查閱工作單元文檔。

 

一個倉儲的生命周期

所有倉儲實例都是短暫的,它的意思是:它們為每次的使用都進行實例化。查閱依賴注入文檔獲取更多信息。

 

倉儲最佳實踐

  • 為一個T類型的實體,盡可能地使用IRepository<T>。不要創建自定義的倉儲,除非確實需要。預定義的倉儲方法可滿足大部分情況。
  • 如果你正在創建一個自定義倉儲(通過擴展IRepository〈TEntity>):
    • 倉儲類應該沒有狀態,也就是說:你不應該定義一個倉儲級別狀態的對象且一個倉儲方法的調用不應該影響另一個調用。
    • 自定義倉儲方法不應該包含業務邏輯或應用邏輯。它應該只是執行數據相關或ORM相關的任務。
    • 雖然倉儲可以使用依賴注入,但盡可能少或不定義對其它服務的依賴。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved