ABP(現代ASP.NET樣板開發框架)系列之1五、ABP應用層——應用服務(Application services)

點這裏進入ABP系列文章總目錄html

 

基於DDD的現代ASP.NET開發框架--ABP系列之1五、ABP應用層——應用服務(Application services)
git

 

ABP是「ASP.NET Boilerplate Project (ASP.NET樣板項目)」的簡稱。github

ABP的官方網站http://www.aspnetboilerplate.com數據庫

ABP在Github上的開源項目https://github.com/aspnetboilerplate架構

本文由東莞-天道提供翻譯app


 

應用服務用於將領域(業務)邏輯暴露給展示層。展示層經過傳入DTO(數據傳輸對象)參數來調用應用服務,而應用服務經過領域對象來執行相應的業務邏輯而且將DTO返回給展示層。所以,展示層和領域層將被徹底隔離開來。在一個理想的層級項目中,展示層應該從不直接訪問領域對象。框架

IApplicationService接口

在ABP中,一個應用服務須要實現IApplicationService接口。最好的實踐是針對每一個應用服務都建立相應的接口。因此,咱們首先定義一個應用服務接口,以下所示:函數

public interface IPersonAppService : IApplicationService
{
    void CreatePerson(CreatePersonInput input);
}

IPersonAppService只有一個方法,它將被展示層調用來建立一個新的Person。CreatePersonInput是一個DTO對象,以下所示:網站

public class CreatePersonInput : IInputDto
{
    [Required]
    public string Name { get; set; }

    public string EmailAddress { get; set; }
}

接着,咱們實現IPersonAppService接口:ui

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

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

    public void CreatePerson(CreatePersonInput input)
    {
        var person = _personRepository.FirstOrDefault(p => p.EmailAddress == input.EmailAddress);
        if (person != null)
        {
            throw new UserFriendlyException("There is already a person with given email address");
        }

        person = new Person { Name = input.Name, EmailAddress = input.EmailAddress };
        _personRepository.Insert(person);
    }
}

如下是幾個重要提示:

  • PersonAppService經過IRepository來執行數據庫操做。它經過構造器注入模式來生成。咱們在這裏使用了依賴注入。
  • PersonAppService實現了IApplicationService(經過IPersonAppService繼承IApplicationService)。ABP會自動地把它註冊到依賴注入系統中,並能夠注入到別的類型中使用。
  • CreatePerson方法須要一個CreatePersonInput類型的參數。這是一個做爲輸入的DTO,它將被ABP自動驗證其數據有效性。能夠查看DTO和數據有效性驗證(Validation)文檔獲取相關細節。

應用服務類型

應用服務(Application Services)須要實現IApplicationService接口。固然,你能夠選擇將你的應用服務(Application Services)繼承自ApplicationService基類,這樣你的應用服務也就天然而然的實現IApplicationService接口了。ApplicationService基類提供了方便的日誌記錄和本地化功能。在此建議你針對你的應用程序建立一個應用服務基類繼承自ApplicationService類型。這樣你就能夠添加一些公共的功能來提供給你的全部應用服務使用。一個應用服務示例以下所示:

public class TaskAppService : ApplicationService, ITaskAppService
{
    public TaskAppService()
    {
        LocalizationSourceName = "SimpleTaskSystem";
    }

    public void CreateTask(CreateTaskInput input)
    {
        //記錄日誌,Logger定義在ApplicationService中
        Logger.Info("Creating a new task with description: " + input.Description);

        //獲取本地化文本(L是LocalizationHelper.GetString(...)的簡便版本, 定義在 ApplicationService類型)
        var text = L("SampleLocalizableTextKey");

        //TODO: Add new task to database...
    }
}

本例中咱們在構造函數中定義了LocalizationSourceName,但你能夠在基類中定義它,這樣你就不須要在每一個具體的應用服務中定義它。查看日誌記錄(logging)和本地化(localization)文檔能夠獲取更多的相關信息。

工做單元

在ABP中,一個應用服務方法默認是一個工做單元。

(1)鏈接 & 事務管理 (For connection & transaction management)

在應用服務方法中,若是咱們須要調用兩個倉儲方法,那麼這些方法必須爲一個事務。舉個例子:

public void CreatePerson(CreatePersonInput input)
{
    var person = new Person { Name = input.Name, EmailAddress = input.EmailAddress };    
    _personRepository.Insert(person);
    _statisticsRepository.IncrementPeopleCount();
}

咱們向Person表插入一個數據,接着在其餘表中修改了Person計數字段的值。這兩個操做實現於不一樣的倉儲中,可是它們使用了相同的數據鏈接和事務。這是怎麼實現的呢?

對於UOW模式,當事務啓動而且開始執行CreatePerson方法的時候,ABP會自動地打開數據庫。在方法結束時,若是未發生異常該事務將會被提交,並確保關閉數據庫鏈接。所以,CreatePerson方法中的全部數據庫操做將做爲一個事務(具備原子性),當有異常拋出時這些事務中的操做將會回滾。因此,示例中的兩個倉儲方法使用了相同的數據鏈接和事務。

當你調用倉儲中的GetAll()方法時,它將返回一個IQueryable。數據庫鏈接應會在調用倉儲方法後打開。這是由於IQueryable和LINQ的延遲執行。當你調用相似ToList()方法時,數據庫查詢纔會真正的開始執行。來看下面的示例:

public SearchPeopleOutput SearchPeople(SearchPeopleInput input)
{
    //獲取 IQueryable<Person>
    var query = _personRepository.GetAll();

    //過濾數據
    if (!string.IsNullOrEmpty(input.SearchedName))
    {
        query = query.Where(person => person.Name.StartsWith(input.SearchedName));
    }

    if (input.IsActive.HasValue)
    {
        query = query.Where(person => person.IsActive == input.IsActive.Value);
    }

    //獲取分頁
    var people = query.Skip(input.SkipCount).Take(input.MaxResultCount).ToList();

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

因爲一個應用服務(Application Services)方法就是一個工做單元,因此數據庫鏈接在方法執行期間都是開啓的。若是你在非應用服務(Application Services)中調用GetAll(),你須要顯式的使用工做單元模式。如:在Controller的Action方法中要使用GetAll()或調用多個有對數據庫操做的AppService方法時, 應該將Action方法使用virtual修飾,並在Action的上面經過[UnitOfWork]進行顯示開啓工做單元模式。

注意我使用了AutoMapper庫將List轉換成List。能夠查看DTO文檔獲取相關細節。

譯者-天道注:這裏要說一下,就是uow和非uow模式的區別,兩種模式對於數據庫鏈接的打開和關閉是不一樣的。對於控制器的方法,ABP默認是非 uow模式,此時若是調用方法會報錯,提示數據庫未鏈接。解決的辦法是在方法加上virtual。

(2)自動保存數據修改 (For automatically saving changes)

對於工做單元方法(應用服務(Application Services)方法),在方法結束時ABP將會自動保存全部數據修改。假設咱們須要一個應用服務(Application Services)方法來更新一個Person的Name:

public void UpdateName(UpdateNameInput input)
{
    var person = _personRepository.Get(input.PersonId);
    person.Name = input.NewName;
}

就是這樣,Name被成功修改!咱們甚至不須要調用_personRepository.Update方法。ORM框架在工做單元中會跟蹤全部實體修改並將修改更新到數據庫中。

應用服務的生命週期

全部應用服務(Application Services)實例的生命週期都是暫時的(Transient)。這意味着在每次使用都會建立新的應用服務(Application Services)實例。ABP堅定地使用依賴注入技術。當一個應用服務(Application Services)類型須要被注入時,該應用服務(Application Services)類型的新實例將會被依賴注入容器自動建立。查看依賴注入(Dependency Injection)文檔獲取更多信息。

 


但願更多國內的架構師能關注到ABP這個項目,也許這其中有能幫助到您的地方,也許有您的參與,這個項目能夠發展得更好。

歡迎加QQ羣:

ABP架構設計交流羣:134710707 ABP架構設計交流羣      ABP架構設計交流2羣: 579765441ABP架構設計交流羣2

 

點這裏進入ABP系列文章總目錄

相關文章
相關標籤/搜索