构建应用层服务

简介:

今天谈谈《构建应用层服务》。

理论学习:

应用服务提供了一些门面样式方法来分离表现层和领域层。这样做的目的也是为了解耦,以后表现层就不用直接和业务逻辑层(核心层)打交道了,而是通过应用服务层(相当于媒介)来处理。应用服务层不仅定义了很多服务方法供表现层直接调用,而且还提供了一些Dtos(Data Transfer Object)。

说到Dto,好处确实挺多的。

第一,可以将它理解为一个简化的实体,更方便。比如,我们有一个User表,里面有Id,Name,Password,IsDeleted,CreationTime等等。那么我们简化的UserDto对象就只含有Name和IsDeleted两个字段就够用了,因为表现层就只用到了这两个字段。

第二,更安全,性能更好。如果不用Dto而用实体类的话,最后生成的查询就会将实体的所有字段都会查询出来。这样一来就暴露了一些重要的数据给一些我们不想这些数据被看到的人。

第三,应用扩展性更好,耦合度降低。表现层是通过一个Dto对象作为参数来调用应用服务方法的,然后使用领域对象(实体类对象)执行特定的业务逻辑并返回一个Dto对象给表现层,所以,表现层完全独立于领域层。在一个理想的应用中,表现层和领域层不会直接打交道。

第四,序列化问题。当返回一个数据对象到表现层时,很可能会在某个地方序列化。比如,在一个返回JSON的MVC方法中,你的对象可能会被序列化成Json发送到客户端。如果返回一个实体的话会有问题的。比如,这个User实体有一个Role的应用,如果要序列化User的话也会序列化Role。甚至Role可能有List<Permission>,Permission又有一个PermissionGroup的引用等等…你敢想象序列化之后的对象吗?亦可以轻易地一下子序列化整个数据库。所以,在这种情况下返回一个安全序列化的、特别设计的DTOs是一个好的做法。

image

先来定义Dtos,看下面代码:

复制代码
namespace Noah.ChargeStation.Application.CitiesApp.Dto
{
    public class CityInput : IInputDto
    {
        public string Name { get; set; }
        public string Code { get; set; }
        public string ProvinceCode { get; set; }
    }

    public class GetCityInput : IInputDto
    {
        public string Name { get; set; }
        public string ProvinceCode { get; set; }
    }

    public class CreateCityInput : IInputDto, IShouldNormalize
    {
        [Required]
        public string Name { get; set; }
        [Required]
        public string Code { get; set; }
        [Required]
        public string ProvinceCode { get; set; }
        public DateTime UpdatedTime { get; set; }
        public string UpdatedBy { get; set; }

        public void Normalize()
        {
            if (UpdatedTime==null)
            {
                UpdatedTime=DateTime.Now;
            }
        }
    }
}
复制代码

这个是输入方向的Dto,实现了IInputDto接口,这样的话ABP可以自动帮助我们进行数据校验。当然,我们也可以添加数据注解进行校验。校验之后,还可以实现IShouldNormalize接口来设置缺省值。

复制代码
namespace Noah.ChargeStation.Application.CitiesApp.Dto
{
    public class CityOutput:IOutputDto
    {
        public string Code { get; set; }
        public string Name { get; set; }
        public string ProvinceCode { get; set; }

    }

    public class GetCitiesOutput:IOutputDto
    {
        public List<CityDto> Cities { get; set; }
    }
}
复制代码

以上是输出方向的Dto。

 

接下来我定义一个城市表的服务接口ICityAppService,我的命名规范是”I+实体类单数+AppService”。

复制代码
namespace Noah.ChargeStation.Application.CitiesApp
{
    public interface ICityAppService:IApplicationService
    {
        GetCitiesOutput GetCities(GetCityInput input);
        Task<GetCitiesOutput> GetCitiesAsync(GetCityInput input);
        void UpdateCity(CityInput input);
        Task UpdateCityAsync(CityInput input);
        void CreateCity(CityInput input);
        Task CreateCityAsync(CityInput input);
    }
}
复制代码

以上定义的方法有同步和异步两个版本。

接下来实现应用服务接口,这里只实现一个方法GetCities(…),望读者举一反三。

复制代码
public class CityAppService : ChargeStationAppServiceBase, ICityAppService
    {
        private readonly IRepository<Cities> _cityRepository;
        public CityAppService(IRepository<Cities> cityRepository)
        {
            _cityRepository = cityRepository;
        }
        public GetCitiesOutput GetCities(GetCityInput input)
        {
            //根据不同条件进行查询不同的结果

            //Mapper.CreateMap<Cities, CityOutput>();
            //根据城市名称查询城市数据
            if (!string.IsNullOrEmpty(input.Name))
            {
                var cityEntity = _cityRepository.GetAllList(c => c.Name == input.Name).FirstOrDefault();
                return new GetCitiesOutput() { CityDto = Mapper.Map<CityDto>(cityEntity) };
            }
            //根据省份编码查询城市数据
            if (!string.IsNullOrEmpty(input.ProvinceCode))
            {
                var cityEntityList = _cityRepository.GetAllList(c => c.ProvinceCode == input.ProvinceCode);
                return new GetCitiesOutput() { CityDtoList = Mapper.Map<List<CityDto>>(cityEntityList) };
            }

            return null;
        }

        public void UpdateCity(CityInput input)
        {
            Logger.Info("Updating a City for input: " + input);
        }

        public void CreateCity(CityInput input)
        {
            //var city = _cityRepository.FirstOrDefault(c => c.Name == input.Name);
            //if (city != null)
            //{
            //    throw new UserFriendlyException("该城市数据已经存在!");
            //}
            //city = new City() { Code = input.Code, Name = input.Name, ProvinceCode = input.ProvinceCode };
            //_cityRepository.Insert(city);
        }


        public Task<GetCitiesOutput> GetCitiesAsync(GetCityInput input)
        {
            throw new System.NotImplementedException();
        }

        public Task UpdateCityAsync(CityInput input)
        {
            throw new System.NotImplementedException();
        }

        public Task CreateCityAsync(CityInput input)
        {
            throw new System.NotImplementedException();
        }
    }
复制代码

这里又出现了个新东西AutoMapper,关于这个的使用,我这几天会专门开一个关于AutoMapper的专题,敬请期待。代码没有难度,也就不多做解释了。今天就到这里吧。下次再讲。




本文转自tkbSimplest博客园博客,原文链接:http://www.cnblogs.com/farb/p/4930968.html,如需转载请自行联系原作者


目录
相关文章
|
搜索推荐 API 数据安全/隐私保护
使用Selenium进行网页登录和会话管理
使用Selenium进行网页登录和会话管理
|
11月前
|
监控 关系型数据库 MySQL
MySQL自增ID耗尽应对策略:技术解决方案全解析
在数据库管理中,MySQL的自增ID(AUTO_INCREMENT)属性为表中的每一行提供了一个唯一的标识符。然而,当自增ID达到其最大值时,如何处理这一情况成为了数据库管理员和开发者必须面对的问题。本文将探讨MySQL自增ID耗尽的原因、影响以及有效的应对策略。
490 3
|
12月前
|
XML 大数据 网络安全
大数据-03-Hadoop集群 免密登录 超详细 3节点云 分发脚本 踩坑笔记 SSH免密 集群搭建(一)
大数据-03-Hadoop集群 免密登录 超详细 3节点云 分发脚本 踩坑笔记 SSH免密 集群搭建(一)
209 5
|
Python
SyntaxError :invalid syntax错误解决办法
SyntaxError :invalid syntax错误解决办法
3627 1
|
机器学习/深度学习 算法 决策智能
Python求解旅行商问题
欢迎关注我的微信公众号:Python学习杂记
451 0
|
存储 Rust 程序员
Rust结构体详解:定义、使用及方法
Rust结构体详解:定义、使用及方法
204 0
|
数据安全/隐私保护 Docker 容器
银河麒麟v10-arm版离线安装Portainer
银河麒麟v10-arm版离线安装Portainer
1013 0
|
人工智能
AI创造营来啦,让人人都成为超级助理的创造者
就是现在,加入我们,和钉钉一起进入超级助理的未来世界!
AI创造营来啦,让人人都成为超级助理的创造者
|
关系型数据库 MySQL API
微服务框架 go-zero 快速实战
微服务框架 go-zero 快速实战
623 1
|
算法 Java Go
ETCD(六)ETCD和Zookeeper
ETCD(六)ETCD和Zookeeper
309 0