Application Services are used to expose domain logic to the presentation layer. An Application Service is called from presentation layer with a DTO (Data Transfer Object) as parameter, uses domain objects to perform some specific business logic and returns a DTO back to the presentation layer. Thus, Presentation layer is completely isolated from Domain layer. In an ideally layered application, presentation layer never directly works with domain objects.
应用程序服务用于将域逻辑暴露到表示层中。应用服务是从表现层与DTO(数据传输对象)为参数,使用域对象执行一些具体的业务逻辑,并返回一个DTO返回给表现层。因此,表示层与域层完全隔离。在理想的分层应用程序中,表示层从不直接使用域对象。
IApplicationService Interface
In ASP.NET Boilerplate, an application service should implement IApplicationService interface. It's good to create an interface for each Application Service. So, we first define an interface for an application service as shown below:
public interface IPersonAppService : IApplicationService
{
void CreatePerson(CreatePersonInput input);
}
IPersonAppService has only one method. It's used by presentation layer to create a new person. CreatePersonInput is a DTO object as shown below:
public class CreatePersonInput
{
[Required]
public string Name { get; set; } public string EmailAddress { get; set; }
}
Then we can implement the 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);
}
}
There are some important points here:
- PersonAppService uses IRepository<Person> to perform database operations. It uses constructor injection pattern. We're using dependency injection here.
- PersonAppService implements IApplicationService (since IPersonAppService extends IApplicationService), it's automatically registered to Dependency Injection system by ASP.NET Boilerplate and can be injected and used by other classes. Naming convention is important here.(命名约定在这里很重要) See dependency injection document for more.
- CreatePerson method gets CreatePersonInput. It's an input DTO and automatically validated by ASP.NET Boilerplate. See DTO and validation documents for details.
ApplicationService Class
An application service should implement IApplicationService interface as declared above. Also, optionally, can be derived from ApplicationService base class. Thus, IApplicationService is inherently implemented. Also, ApplicationService class has some base functionality that makes easy to logging, localization and so on... It's suggested to create a special base class for your application services that extends ApplicationService class. Thus, you can add some common functionality for all your application services. A sample application service class is shown below:
应用服务应该实现iapplicationservice接口以上的声明。另外,还可以实现应用服务的基类。因此,iapplicationservice本质上是实现。同时,应用服务类的一些基本功能,使得日志,本地化等等…建议创建您的应用程序服务,扩展应用服务类特殊的基类。因此,您可以为所有应用程序服务添加一些公共功能。下面显示了一个示例应用程序服务类:
public class TaskAppService : ApplicationService, ITaskAppService
{
public TaskAppService()
{
LocalizationSourceName = "SimpleTaskSystem";
} public void CreateTask(CreateTaskInput input)
{
//Write some logs (Logger is defined in ApplicationService class)
Logger.Info("Creating a new task with description: " + input.Description); //Get a localized text (L is a shortcut for LocalizationHelper.GetString(...), defined in ApplicationService class)
var text = L("SampleLocalizableTextKey"); //TODO: Add new task to database...
}
}
You can have a base class which defines LocalizationSourceName in it's constructor. Thus, you do not repeat it for all service classes. See logging and localization documents for more informations on this topics.
你可以在它的构造函数有一个基类定义了localizationsourcename。因此,不要为所有服务类重复它。有关此主题的更多信息,请参见日志和本地化文档。
CrudAppService and AsyncCrudAppService Classes
If you need to create an application service that will have Create, Update, Delete, Get, GetAll methods for a specific entity, you can inherit from CrudAppService (or AsyncCrudAppService if you want to create async methods) class to create it easier. CrudAppService base class is generic which gets related Entity and DTO types as generic arguments and is extensible which allows you to override functionality when you need to customize it.
如果你需要创建一个应用程序服务,将创建,更新,删除,得到的,对于一个特定的实体获得的方法,你可以从crudappservice(或asynccrudappservice如果你想创建异步方法)的类来创建更容易。crudappservice基类是通用的,得到了相关的实体和DTO类型作为泛型参数是可扩展的,允许你覆盖的功能时,你需要定制。
Simple CRUD Application Service Example
Assume that we have a Task entity defined below:
public class Task : Entity, IHasCreationTime
{
public string Title { get; set; } public string Description { get; set; } public DateTime CreationTime { get; set; } public TaskState State { get; set; } public Person AssignedPerson { get; set; }
public Guid? AssignedPersonId { get; set; } public Task()
{
CreationTime = Clock.Now;
State = TaskState.Open;
}
}
And we created a DTO for this entity:
[AutoMap(typeof(Task))]
public class TaskDto : EntityDto, IHasCreationTime
{
public string Title { get; set; } public string Description { get; set; } public DateTime CreationTime { get; set; } public TaskState State { get; set; } public Guid? AssignedPersonId { get; set; } public string AssignedPersonName { get; set; }
}
AutoMap attribute creates auto mapping configuration between entity and dto. Now, we can create an application service as shown below:
public class TaskAppService : AsyncCrudAppService<Task, TaskDto>
{
public TaskAppService(IRepository<Task> repository)
: base(repository)
{ }
}
We injected the repository and passed it to the base class (We could inherit from CrudAppService if we want to create sync methods instead of async methods). That's all! TaskAppService now have simple CRUD methods. If you want to define an interface for the application service, you can create your interface as shown below:
我们注入仓库交给基类(我们可以从crudappservicej继承如果我们想创建同步方法代替异步方法)。这就是全部!taskappservice现在有简单的CRUD方法。如果您想为应用程序服务定义一个接口,您可以创建如下所示的接口:
public interface ITaskAppService : IAsyncCrudAppService<TaskDto>
{ }
Notice that IAsyncCrudAppService does not get the entity (Task) as generic argument. Because, entity is related to implementation and should not be included in public interface. Now, we can implement ITaskAppService interface for the TaskAppService class:
注意,iasynccrudappservice没有实体(任务)作为泛型参数。因为实体与实现有关,不应该包含在公共接口中。现在,我们可以实现对taskappservice类itaskappservice接口:
public class TaskAppService : AsyncCrudAppService<Task, TaskDto>, ITaskAppService
{
public TaskAppService(IRepository<Task> repository)
: base(repository)
{ }
}
Customize CRUD Application Services(定制的CRUD应用服务)
Getting List
Crud application service gets PagedAndSortedResultRequestDto as argument for GetAll method as default, which provides optional sorting and paging parameters. But you may want to add another parameters for GetAll method. For example, you may want to add some custom filters. In that case, you can create a DTO for GetAll method. Example:
CRUD应用服务得到PagedAndSortedResultRequestDto为getAll方法作为默认参数,它提供了可选的排序和分页参数。但你可能要添加另一个参数的获得方法。例如,您可能需要添加一些自定义过滤器。在这种情况下,你可以创建一个DTO为getAll方法。例子:
public class GetAllTasksInput : PagedAndSortedResultRequestDto
{
public TaskState? State { get; set; }
}
We inherit from PagedAndSortedResultRequestInput (which is not required, but wanted to use paging & sorting parameters form the base class) and added an optional State property to filter tasks by state. Now, we should change TaskAppService in order to apply the custom filter:
我们从PagedAndSortedResultRequestInput(这是不需要的,但要使用分页和排序的参数形式的基类),添加一个可选的状态属性的状态滤波任务。现在,我们应该改变taskappservice为了应用自定义过滤器:
public class TaskAppService : AsyncCrudAppService<Task, TaskDto, int, GetAllTasksInput>
{
public TaskAppService(IRepository<Task> repository)
: base(repository)
{ } protected override IQueryable<Task> CreateFilteredQuery(GetAllTasksInput input)
{
return base.CreateFilteredQuery(input)
.WhereIf(input.State.HasValue, t => t.State == input.State.Value);
}
}
First, we added GetAllTasksInput as 4th generic parameter to AsyncCrudAppService class (3rd one is PK type of the entity). Then overrided CreateFilteredQuery method to apply custom filters. This method is an extension point for AsyncCrudAppService class (WhereIf is an extension method of ABP to simplify conditional filtering. But actually what we do is to simply filter an IQueryable).
首先,我们增加了GetAllTasksInput为第四个泛型参数asynccrudappservice类(第三个是PK型的实体)。然后超越createfilteredquery方法应用自定义过滤器。该方法是asynccrudappservice类扩展点(如果是一个扩展方法和简化条件过滤。但实际上我们做的是简单的过滤一个IQueryable)。
Note that: If you have created application service interface, you need to add same generic arguments to that interface too.
注意:如果您已经创建了应用程序服务接口,那么您也需要向该接口添加相同的泛型参数。
Create and Update
Notice that we are using same DTO (TaskDto) for getting, creating and updating tasks which may not be good for real life applications. So, we may want to customize create and update DTOs. Let's start by creating aCreateTaskInput class:
注意,我们使用的是同样的DTO(taskdto)获取、创造和更新的任务,这可能不是现实生活中的应用很好。因此,我们可能需要自定义创建和更新DTOs。让我们开始创建createtaskinput类:
[AutoMapTo(typeof(Task))]
public class CreateTaskInput
{
[Required]
[MaxLength(Task.MaxTitleLength)]
public string Title { get; set; } [MaxLength(Task.MaxDescriptionLength)]
public string Description { get; set; } public Guid? AssignedPersonId { get; set; }
}
And create an UpdateTaskInput DTO:
[AutoMapTo(typeof(Task))]
public class UpdateTaskInput : CreateTaskInput, IEntityDto
{
public int Id { get; set; } public TaskState State { get; set; }
}
I wanted to inherit from CreateTaskInput to include all properties for Update operation (but you may want different). Implementing IEntity (or IEntity<PrimaryKey> for different PK than int) is required here, because we need to know which entity is being updated. Lastly, I added an additional property, State, which is not in CreateTaskInput.
我想从createtaskinput包括更新操作的所有属性(但你可能需要不同的)。实现认同(or IEntity<PrimaryKey> for different PK than int)是必须的,因为我们需要知道哪些实体正在更新。最后,我添加了一个额外的属性,状态,这是不是在CreateTaskInput。
Now, we can use these DTO classes as generic arguments for AsyncCrudAppService class, as shown below:
现在,我们可以使用这些DTO类作为asynccrudappservice类泛型参数,如下图所示:
public class TaskAppService : AsyncCrudAppService<Task, TaskDto, int, GetAllTasksInput, CreateTaskInput, UpdateTaskInput>
{
public TaskAppService(IRepository<Task> repository)
: base(repository)
{ } protected override IQueryable<Task> CreateFilteredQuery(GetAllTasksInput input)
{
return base.CreateFilteredQuery(input)
.WhereIf(input.State.HasValue, t => t.State == input.State.Value);
}
}
No need to an additional code change.
不需要额外的代码更改。
Other Method Arguments(其他方法的参数)
AsyncCrudAppService can get more generic arguments if you want to customize input DTOs for Get and Delete methods. Also, all methods of the base class is virtual, so you can override them to customize the behaviour.
asynccrudappservice可以得到更一般的参数如果你想自定义输入DTOs和删除方法。此外,基类的所有方法都是虚拟的,因此您可以重写它们以自定义行为。
CRUD Permissions(CRUD权限)
You probably need to authorize your CRUD methods. There are pre-defined permission properties you can set: GetPermissionName, GetAllPermissionName, CreatePermissionName, UpdatePermissionName and DeletePermissionName. Base CRUD class automatically checks permissions if you set them. You can set it in the constructor as shown below:
你可能需要授权你的CRUD方法。有预先定义的权限属性可以设置:getpermissionname,getallpermissionname,CreatePermissionName,updatepermissionname和deletepermissionname。基础的CRUD类自动检查权限,如果你让他们。您可以在构造函数中设置它,如下所示:
public class TaskAppService : AsyncCrudAppService<Task, TaskDto>
{
public TaskAppService(IRepository<Task> repository)
: base(repository)
{
CreatePermissionName = "MyTaskCreationPermission";
}
}
Alternatively, you can override appropriate permission checker methods to manually check permissions: CheckGetPermission(), CheckGetAllPermission(), CheckCreatePermission(), CheckUpdatePermission(), CheckDeletePermission(). As default, they all calls CheckPermission(...) method with related permission name which simply calls IPermissionChecker.Authorize(...) method.
或者,你可以覆盖相应权限检查方法手动检查权限:checkgetpermission(),checkgetallpermission(),checkcreatepermission(),checkupdatepermission(),checkdeletepermission()。在默认的情况下,他们的所有调用checkPermission(…)与相关权限名称简单地调用IPermissionChecker法。授权(…)方法。
Unit of Work
An application service method is a unit of work by default in ASP.NET Boilerplate. Thus, any application service method is transactional and automatically saves all database changes at the end of the method.
应用服务的方法是通过在ASP.NET样板默认工作单元。因此,任何应用程序服务方法都是事务性的,并在方法结束时自动保存所有数据库更改。
See unit of work documentation for more.
Lifetime of an Application Service
All application service instances are Transient. It means, they are instantiated per usage. See Dependency Injection documentation for more information.
所有应用程序服务实例都是暂时的。它意味着,每个使用都实例化它们。有关更多信息,请参见依赖注入文档。
ABP框架系列之十:(Application-Services-应用服务)的更多相关文章
-
ABP框架系列之三十九:(NLayer-Architecture-多层架构)
Introduction Layering of an application's codebase is a widely accepted technique to help reduce com ...
-
ABP框架系列之十八:(Data-Transfer-Objects-数据转换对象)
Data Transfer Objects are used to transfer data between Application Layer and Presentation Layer. 数据 ...
-
ABP框架系列之五十二:(Validating-Data-Transfer-Objects-验证数据传输对象)
Introduction to validation Inputs of an application should be validated first. This input can be sen ...
-
ABP框架系列之十二:(Audit-Logging-审计日志)
Introduction Wikipedia: "An audit trail (also called audit log) is a security-relevant chronolo ...
-
ABP框架系列之五十四:(XSRF-CSRF-Protection-跨站请求伪造保护)
Introduction "Cross-Site Request Forgery (CSRF) is a type of attack that occurs when a maliciou ...
-
ABP框架系列之三十四:(Multi-Tenancy-多租户)
What Is Multi Tenancy? "Software Multitenancy refers to a software architecture in which a sing ...
-
ABP框架系列之四十九:(Startup-Configuration-启动配置)
ASP.NET Boilerplate provides an infrastructure and a model to configure it and modules on startup. A ...
-
ABP框架系列之三十二:(Logging-登录)
Server Side(服务端) ASP.NET Boilerplate uses Castle Windsor's logging facility. It can work with differ ...
-
ABP框架系列之四十六:(Setting-Management-设置管理)
Introduction Every application need to store some settings and use these settings in somewhere in th ...
随机推荐
-
SimpleDateFormat日期格式化
public class T { /** * @param args */ public static void main(String[] args) { // TODO Auto-generate ...
-
UITableView中容易忽略的知识点
1.取消余下的分割线 tableView.tableFooterView = UIView() 2.分割线顶格 override func viewDidLayoutSubviews() { self ...
-
C注意,使用的语言字符串
转载请注明出处! 在C语言没有具体的字符串数据类型,字符串的字符串常量和字符数组的形式. 实际上该字符串是零个或更多字符的字符串.并在整个位模式0NUL字节结束.因此,字符串所包括的字符内部不能出现N ...
-
使用DTM ( Dynamic Topic Models )进行主题演化实验
最近想研究下Dynamic Topic Models(DTM),论文看了看,文科生的水平确实是看不懂,那就实验一下吧,正好Blei的主页上也提供了相应的C++工具, http://www.cs.pri ...
-
Cs231n课堂内容记录-Lecture 9 深度学习模型
Lecture 9 CNN Architectures 参见:https://blog.csdn.net/qq_29176963/article/details/82882080#GoogleNet_ ...
-
A - Points and Segments CodeForces - 429E
题解: 方法非常巧妙的一道题 首先考虑要求全部为0怎么做 发现是个欧拉回路的问题(很巧妙) 直接dfs一遍就可以了 而这道题 要求是-1,1,0 我们可以先离散化 完了之后判断每个点被奇数还是偶数条边 ...
-
JavaScript实现两小时倒计时
[构思] 因为只需要的是两小时,所以时间直接写死,然后通过setInterval每1000ms对时间进行减1操作 前期未考虑到当时分秒小于10的状态,所以后面又加上了一个checkTime()来进行限 ...
-
spring Springmvc mybatis maven整合
一.准备工作 1. 首先创建一个表: CREATE TABLE `t_user` ( `USER_ID` int(11) NOT NULL AUTO_INCREMENT, `USER_NAME` ch ...
-
监听程序未启动或数据库服务未注册到该监听程序。启动该监听程序并注册数据库服务 然后重新运行 em configuration assistant。
在WIN 7/64Bit上安装ORACLE 11gR2后,管理网页Database Control(如:https://localhost:1158/em)始终登录不进去,总是说密码错误,使用配置工具 ...
-
CF1137F Matches Are Not a Child&#39;s Play(LCT思维题)
题目 CF1137F 很有意思的题目 做法 直接考虑带修改的做法,上一次最大值为u,这次修改v,则最大值为v了 我们发现:\(u-v\)这条链会留到最后,序列里的其他元素相对位置不变,这条链会\(u\ ...