• ABP框架


    文档目录

    本节内容:

    应用服务把领域逻辑暴露给展现层。一个应用服务被展现层使用一个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

    [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特性创建实体与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)
        {
    
        }
    
        protected override IQueryable<Task> CreateFilteredQuery(GetAllTasksInput input)
        {
            return base.CreateFilteredQuery(input)
                .WhereIf(input.State.HasValue, t => t.State == input.State.Value);
        }
    }

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

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

    创建和更新

    注意:我们为获取、创建和更新Task使用相同的DTO(TaskDto),在现实应用里,这可能不太好,所以我们想定制创建和更新的DTO,让我们从创建一个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; }
    }

    接着创建一个UpdateTaskInput DTO:

    [AutoMapTo(typeof(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(短暂的),也就说:它们都是暂存于每次使用里。更多信息查看依赖注入文档。

  • 相关阅读:
    seriviceWorker 小结
    Number.prototype.toLocalString() js
    浏览器h5新建文件 保存到本地(相当于浏览器写文件)
    ios 当margin-left margin-right 超过设备宽度
    数组变char
    字符窜转数字
    stream 的方式遍历
    LinkedHashSet 去掉重复数据
    前端判断是否为空字符窜
    前端去掉空格的方法
  • 原文地址:https://www.cnblogs.com/kid1412/p/6003490.html
Copyright © 2020-2023  润新知