应用服务

应用服务

文档目录

本节内容:

应用服务把领域逻辑暴露给展现层。一个应用服务被展现层使用一个DTO(数据传输对象)参数所调用,使用领域对象执行一些特殊业务逻辑并返回一个DTO给展现层。因此,展现层是完全独立于领域层,在一个理想的分层应用里,展现层从不直接使用领域对象。

IApplicationService 接口

在ABP里,一个应用服务应当实现IApplicationService接口,为每个应用服务创建一个接口是好的做法,所以我们先为一个应用服务创建一个接口,如下所示:

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

IPersonAppService只有一个方法,它被展现层用来创建一个新的person,CreatePersonInput是一个DTO对象,如下所示:

public class CreatePersonInput
{
    [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<Person>执行数据库操作,它使用构造器注入模式,这里我们使用依赖注入
  • PersonAppService实现IApplicationService(因为IPersonAppService扩展了IApplicationService),它自动被ABP注入到依赖注入系统,然后被其它类注入并使用。这里命名约定很重要,查看依赖注入文档获取更多信息。
  • CreatePerson方法取得CreatePersonInput,它是一个输入的DTO并被ABP自动验证,更多细节查看DTO验证文档。

ApplicationService 类

一个应用服务应当实现IApplicationService接口,如上所示,也可随意的继承于ApplicationService基类,因为它内部实现了IApplicationService,同时ApplicationService有些基本的功能,使得日志、本地化等更容易。建议为你的应用服务创建一个特殊的继承于ApplicationService类的基类,从而可以为所有应用服务添加一些通用的功能,一个应用服务示例如下:

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...
    }
}

你有一个在构造器里定义了LocalizationSourceName的基类,这样,你就不用为所有的服务类重复定义了,这个主题的更多信息请查看日志本地化文档。

CrudAppService 和 AsyncCrueAppService 类

如果你需要为一个特定的实体创建一个包含Create、Update、Delete、GetAll方法的应用服务,可以从CrudAppService(或AsyncCrudAppService,创建异步方法)继承,CrudAppService基类是一个以相关Entity和DTO类型为参数的泛型,并可通过重写功能进行你需要的定制。

简单的CRUD应用服务示例

假设你有一个Task实体,定义如下:

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

我们为该实体创建一个DTO

(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特性创建实体与DTO之间的映射配置,现在,我们可以创建一个应用服务,如:

public class TaskAppService : AsyncCrudAppService<Task, TaskDto>
{
    public TaskAppService(IRepository<Task> repository)
        : base(repository)
    {

    }
}

我们注入仓储并把它传给基类(如果要使用同步的方法,我们可以从CrudAppService继承)。这就是所有代码!TaskAppService现在就已经有了简单的CRUD方法,如果你想为这个应用服务创建一个接口,可以像下面这样:

public interface ITaskAppService : IAsyncCrudAppService<TaskDto>
{

}

注意:IAsyncCrudAppService没有以实体(Task)作为泛型参数,因为实体与实现相关,不应该包含在公开的接口里,接下来,我们就可以为TaskAppService类实现ITaskAppService接口:

public class TaskAppService : AsyncCrudAppService<Task, TaskDto>, ITaskAppService
{
    public TaskAppService(IRepository<Task> repository)
        : base(repository)
    {

    }
}

定制CRUD应用服务

获取列表

Crud应用服务的GetAll方法默认以PagedAndSortedResultRequestInput为参数,该参数提供可选的排序和分页参数,但你可能想为GetAll方法添加另一个参数,例如:你想添加一些自定义过滤,这种情况,你可以为GetAll方法创建一个DTO,如:

public class GetAllTasksInput : PagedAndSortedResultRequestInput
{
    public TaskState? State { get; set; }
}

我们从PagedAndSortedResultRequestInput继承(不是必须,但可以直接从基类得到paging和sorting参数),并添加一个可空的State属性,用来过滤task。现在我们应该修改TaskAppService,使它接受自定义过滤:

public class TaskAppService : AsyncCrudAppService<Task, TaskDto, int, GetAllTasksInput>
{
    public TaskAppService(IRepository<Task> repository)
        : base(repository)
    {

    }

     CreateFilteredQuery(GetAllTasksInput input)
    {
         input.State.Value);
    }
}

首先,我们添加GetAllTasksInputAsyncCrudAppService第4个泛型参数(第三个是实体主键的类型),然后重写CreateFilteredQuery方法实现自定义过滤,该方法是AsyncCrudAppService类的一个扩展点(WhereIf是ABP的一个扩展方法,用来简化条件过滤,但实质上我们所做的是简单过滤一个IQueryable)。

注意:如果你已创建应用服务接口,那么你就需要为接口添加相同的泛型参数。

创建和更新

注意:我们为获取、创建和更新Task使用相同的DTO(TaskDto),在现实应用里,这可能不太好,所以我们想定制创建和更新的DTO,让我们从创建一个CreateTaskInput类开始:

(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; }
}

接着创建一个UpdateTaskInput DTO:

(Task))]
public class UpdateTaskInput : CreateTaskInput, IEntityDto
{
    public int Id { get; set; }

    public TaskState State { get; set; }
}

我们为Update操作从CreateTaskInput上继承所有属性(但你也可以不这么做),此处必须实现实现IEntity(或不同于int类型的主键的IEntity<PrimaryKey>),因为我们需要知道哪一个实体需要更新,最后,我添加了一个额外的属性State,它不包含在CreateTaskInput里。

接下来,我们可以使用这些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);
    }
}

不需要修改其它的代码。

其它

如果你想定制Get和Delete方法的input的DTO,AsyncCrudAppService可以接受更多的泛型参数。同样,所有的基类方法都是virtual,所以你可以重写它们进行行为定制。

工作单元

在ABP里一个应用服务方法就是一个工作单元,因此,任何一个应用服务方法都是事务性的,并自动在方法结束时保存修改到数据库。

更多信息查看工作单元文档。

一个应用服务的生命周期

所有应用服务实体都是Transient(短暂的),也就说:它们都是暂存于每次使用里。更多信息查看依赖注入文档。

分类: ASP.NET Boilerplate

时间: 2024-07-29 19:07:25

应用服务的相关文章

如何对应用服务性能问题诊断(Tomcat、Weblogic中间件)

在我们web项目中,我们常见的web应用服务器有Tomcat.Weblogic.WebSphere.它们是互联网应用系统的基础架构软件,也叫"中间件",负责处理动态在页面请求,并为应用提供了名字.事务.安全.消息.数据访问等,此外,它们还是提供应该构建的开发.部署.运行及管理功能. 当我们对项目做性能测试时,我们如何更好地监控它们,并诊断出性能问题呢?下以是我对Tomcat和Weblogic的一些性能监控分析方法: 1.  Tomcat性能监控分析 Tomcat是一个免费的开放源代码的

ABP官方文档翻译 4.1 应用服务

应用服务 IApplicationService接口 ApplicationService类 CrudService和AsyncCrudAppService类 简单的CRUD应用服务示例 自定义CRUD应用服务 GettingList Create和Update 其他方法参数 CRUD权限 工作单元 应用服务生命周期 应用服务将领域逻辑暴露给展示层.在展示层使用DTO(数据传输对象)作为参数调用应用服务,应用服务使用领域对象执行一些特定的业务逻辑,并返回DTO到展示层.因此,展示层与领域层是完全

SAP 应用服务负载均衡的实现

     共两步,一是服务器的设置,二是客户端登陆设置.     先在SAP中使用SMLG 进行服务器分组.实例名是SAP系统中定义过的,你没法删也没改.(可能是俺不会,会的教教).我们先建一个Group,然后给Group命名为X,把实例加到组中.服务器的配置就这么简单.     下面在用户端设置SAP message服务器,为下一步作准备.     进入用户的windows安装目录,使用系统变量%windir%可以确定具体位置,找到并更改文件sapmsg.ini文件,改完文件内容如下:[Mes

R12 应用服务群

Oracle EBS R12 支持一些服务群,如下所示:  Service Group                  Supports                                            Root Services  Oracle Process Manager (OPMN) 进程管理器 Web Entry Point Services Web入口点服务  HTTP Server Web Application Services Web应用程序服务 OACO

正确、安全地停止SpringBoot应用服务

引言 Spring Boot,作为Spring框架对“约定优先于配置(Convention Over Configuration)”理念的最佳实践的产物,它能帮助我们很快捷的创建出独立运行.产品级别的基于Spring框架的应用,大部分Spring Boot应用只需要非常少的配置就可以快速运行起来,是一个与微服务(MicroServices)相当契合的微框架.网络上关于Spring Boot的QuickStart式中文内容已经相当丰富,但是对于部署后怎样便捷.安全地停止服务(shutdown),还

三、基础功能模块,用户类别管理——锁、EF并发处理、领域服务、应用服务的划分

在上一章节中,我们处理了MVC多级目录问题,参见<二.处理MVC多级目录问题——以ABP为基础架构的一个中等规模的OA开发日志>.从这章开始,我们将进入正式的开发过程.首先,我们要完成系统的基础设置模块(在后续的功能中,需要大量使用这些基础设置信息).和一般的OA系统不同,在律所OA系统中,用户类别管理是基础模块中非常重要.使用频率非常高的一个基础模块.虽然此功能只是很小的一个字典项设置,但是其中涉及了锁.并发处理.领域服务于应用服务的划分等繁琐问题. UI功能页面介绍(因用户功能未完成,欠缺

不停应用服务,在线建立或重做mysql主从复制的案例,包含一般模式和GTID模式

mysql主从嘛,绝大多数公司都有用到,GTID发展到现在也是越来越多人用,停止应用服务来做主从,略显low了,现在都流行在线做,不影响业务,多实际是吧,不啰嗦了,现在就来看看案例. 先说明,案例分两种方案,实现的意义是一样的,一种是mysqldump方式,一种是xtrabackup方式,视乎实际情况,因为有些业务不一定能用xtrabackup的. 先说mysqldump方式, 因为mysql自带,不需要再做些什么,比较方便易用,不过需要强调一下,数据量太大的话,mysqldump就略显不足了,

将图片服务和应用服务分离

需求 将图片服务和应用服务分离 将图片服务和应用服务放在同一个服务器的话,应用服务器很容易会因为图片的高I/O负载而崩溃,因此对于有些大型网站项目,我们有必要将图片服务器和应用服务器分离. 192.168.0.1  作为nfs服务器(同时也是图片服务器)  192.168.0.2作为客户端, 一: 首先在图片服务器安装好NFS,  需对nfs做出如下配置 nfs服务器: vi /etc/exports 在里面加入本机需要共享的文件目录和对应的客户端地址及权限 添加: /data/pic 192.

应用服务攻击工具clusterd

应用服务攻击工具clusterd clusterd是一款Python语言编写的开源应用服务攻击工具.该工具支持七种不同的应用服务平台,如JBoss.ColdFusion.WebLogic.Tomcat.Railo.Axis2.Glassfish.该工具可以自动对目标服务器进行指纹识别.侦测.漏洞利用.该工具预先集成了几十种攻击载荷,方便用户直接使用.同时,该工具也允许用户添加新的应用服务平台.指纹信息.部署工具和漏洞攻击载荷.

应用服务&amp;领域服务

应用服务&领域服务 DDD理论学习系列--案例及目录 1. 引言 单从字面理解,不管是领域服务还是应用服务,都是服务.而什么是服务?从SOA到微服务,它们所描述的服务都是一个宽泛的概念,我们可以理解为服务是行为的抽象.从前缀来看,根据DDD的经典分层架构,它们又隶属于不同的层,应用服务属于应用层,领域服务属于领域层. 应用层(Application):负责展现层与领域层之间的协调,协调业务对象来执行特定的应用程序任务.它不包含业务逻辑. 领域层(Domain):负责表达业务概念,业务状态信息以及