本篇目錄
應用服務用於將領域邏輯暴露給展現層。展現層調用具有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:
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,它是通過ABP自動注冊到依賴注入系統的,然后被其他的類注入並使用。
- CreatePerson方法以 CreatePersonInput作為參數。它是一個輸入DTO,會被ABP自動驗證。
ApplicationService類
應用服務類應該實現應用服務接口(IApplicationService)。此外,還可以選擇從ApplicationService基類派生。這樣,IApplicationService也被繼承實現了。而且,ApplicationService有一些基本功能,使得logging和 本土化更加簡單。建議為你的繼承了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。這樣,就不用為所有的服務類重復定義了。
工作單元###
在ABP中,應用服務方法默認是一個工作單元。
數據庫連接和事務管理
假如說我們想要在一個必須是事務的應用服務方法中調用兩個倉儲方法。例如:
public void CreatePerson(CreatePersonInput input)
{
var person = new Person { Name = input.Name, EmailAddress = input.EmailAddress };
_personRepository.Insert(person);
_statisticsRepository.IncrementPeopleCount();
}
我們將一個person實體插入到People表中,然后總人數自增,並保存到另一個表的字段中。這個方法都是用不同的倉儲實現的,但是共享了相同的連接和事務。
在CreatePerson方法開始時,ABP會自動打開數據庫連接並開始事務。在方法結束時,如果沒有異常發生,會自動提交事務並關閉數據庫連接。這樣,在CreatePerson方法中的所有數據庫操作都是事務的(原子的),如果有任何異常拋出,操作就會回滾。因此,在這個方法中的兩個倉儲共享相同的連接和事務。
當調用倉儲的GetAll()方法時,會返回一個IQueryable
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);
}
//獲取分頁結果list
var people = query.Skip(input.SkipCount).Take(input.MaxResultCount).ToList();
return new SearchPeopleOutput {People = Mapper.Map<List<PersonDto>>(people)};
}
因為一個應用服務方法是一個工作單元,所以在執行這個方法期間數據庫連接是打開的。如果在一個不是應用服務的類中調用了GetAll()方法,那么應該顯式使用工作單元。
注意這里使用了AutoMapper類庫將 List
自動保存更改
對於工作單元方法,ABP會在方法結束時自動保存所有的更改。假設我們有一個更新一個人的名字的應用服務方法:
public void UpdateName(UpdateNameInput input)
{
var person = _personRepository.Get(input.PersonId);
person.Name = input.NewName;
}
只需要這樣,name字段就改變了。我們甚至都不要調用_personRepository.Update方法。ORM框架會跟蹤工作單元內的實體的所有更改,並將更改反映給數據庫。
更多
請查看《工作單元》。
應用服務的生命周期###
所有的應用服務實例都是Transient(每次使用時創建)。ABP強烈建議使用依賴注入技術。當一個應用服務類需要注入時,該類的一個新實例會在依賴注入容器中自動創建。更多內容請查看《依賴注入》。
