应用服务Application Services

Application Services用于将域逻辑公开给表示层。使用DTO(数据传输对象)作为参数从表示层调用应用服务。它还使用域对象来执行某些特定的业务逻辑,并将DTO返回给表示层。因此,表示层与域层完全隔离。
在理想的分层应用程序中,表示层永远不会直接使用域对象。
IApplicationService接口
在ASP.NET Boilerplate中,应用程序服务应实现 IApplicationService接口。 为每个Application Service 创建一个接口是很好的。

首先,让我们为应用程序服务定义一个接口:

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

IPersonAppService只有一个方法。它由表示层用于创建新人。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 来执行数据库操作。它使用构造函数注入 模式,从而使用依赖注入。
PersonAppService实现IApplicationService(因为IPersonAppService扩展了IApplicationService)。它由ASP.NET Boilerplate自动注册到Dependency Injection系统,可以被其他类注入和使用。命名约定在这里很重要。有关详细信息,请参阅依赖项注入文档。
该CreatePerson方法获取CreatePersonInput。它是一个输入DTO,由ASP.NET Boilerplate自动验证。有关 详细信息,请参阅 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和AsyncCrudAppService类
如果您需要为特定实体创建具有Create,Update,Delete,Get,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应用程序服务将PagedAndSortedResultRequestDto作为默认的GetAll方法的参数获取,它提供可选的排序和分页参数。您可能还想为GetAll方法添加其他参数。例如,您可能想要添加一些 自定义过滤器。在这种情况下,您可以为GetAll方法创建DTO。例:

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

这里我们继承自PagedAndSortedResultRequestInput。这不是必需的,但如果需要,可以使用基类中的分页和排序参数。我们还添加了一个可选的State属性来按状态过滤任务。有了这个,我们更改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);
    }
}

首先,我们将GetAllTask​​sInput添加为AsyncCrudAppService类的第4个通用参数(第3个是实体的PK类型)。然后我们重写CreateFilteredQuery方法以应用自定义过滤器。此方法是AsyncCrudAppService类的扩展点。注意,WhereIf是ABP的扩展方法,以简化条件过滤。我们在这里做的只是过滤IQueryable。

注意:如果您创建了应用程序服务接口,则还需要为该接口添加相同的通用参数!

创建和更新
请注意,我们使用相同的DTO(TaskDto)来获取,创建 和更新可能对现实应用程序不利的任务,因此我们可能希望自定义创建和更新DTO。

让我们从创建CreateTaskInput类开始:

[AutoMapTo(typeof(Task))]
public class CreateTaskInput
{
    [Required]
    [StringLength(Task.MaxTitleLength)]
    public string Title { get; set; }

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

这里我们继承CreateTaskInput以包含Update操作的所有属性(您可能需要不同的东西)。实施 IEntity(对于不同的PK比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方法自定义输入DTO,AsyncCrudAppService可以获得更多通用参数。基类的所有方法都是虚拟的,因此您可以覆盖它们以自定义行为。
CRUD权限
您需要授权您的CRUD方法吗?如果是,则可以设置预定义的权限属性:GetPermissionName,GetAllPermissionName,CreatePermissionName,UpdatePermissionName和DeletePermissionName。如果设置了基本CRUD类,则会自动检查权限。

在这里,您可以在构造函数中设置它:

public class TaskAppService : AsyncCrudAppService<Task, TaskDto>
{
    public TaskAppService(IRepository<Task> repository) 
        : base(repository)
    {
        CreatePermissionName = "MyTaskCreationPermission";
    }
}

或者,您可以覆盖相应的权限检查器方法以手动检查权限:CheckGetPermission(),CheckGetAllPermission(),CheckCreatePermission(),CheckUpdatePermission(),CheckDeletePermission()。默认情况下,它们都使用相关的权限名称调用CheckPermission(…)方法。
简单地说,这会调用IPermissionChecker.Authorize(…)方法。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值