构建应用层服务

简介:

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

理论学习:

应用服务提供了一些门面样式方法来分离表现层和领域层。这样做的目的也是为了解耦,以后表现层就不用直接和业务逻辑层(核心层)打交道了,而是通过应用服务层(相当于媒介)来处理。应用服务层不仅定义了很多服务方法供表现层直接调用,而且还提供了一些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,如需转载请自行联系原作者


目录
相关文章
|
4月前
|
JavaScript Java 网络安全
|
2月前
|
机器学习/深度学习
网络层构建
【8月更文挑战第19天】网络层构建。
30 3
|
2月前
|
安全 网络协议 5G
5G的核心协议有哪些?
【8月更文挑战第31天】
89 0
|
3月前
|
域名解析 存储 网络协议
一次读懂网络分层:应用层到物理层全解析
**网络五层模型简介:** 探索网络服务的分层结构,从应用层开始,包括HTTP(网页传输)、SMTP(邮件)、DNS(域名解析)和FTP(文件传输)协议。传输层涉及TCP(可靠数据传输)和UDP(高效但不可靠)。网络层由IP(路由数据包)、ICMP(错误报告)和路由器构成。数据链路层处理MAC地址和帧传输,物理层规定了电缆、连接器和信号标准。了解这些基础,有助于深入理解网络运作机制。
257 5
|
5月前
|
传感器 数据可视化 JavaScript
物联网架构:感知层、网络层和应用层
【5月更文挑战第30天】物联网(IoT)由感知层、网络层和应用层构成。感知层利用传感器(如DHT11)收集环境数据;网络层通过ESP8266等设备将数据传输至云端;应用层提供用户服务,如Node-RED实现数据可视化。示例代码展示了Arduino读取温湿度,ESP8266连接Wi-Fi及Node-RED数据可视化流程。物联网架构为数据处理与服务提供全面支持,预示其在各领域广阔的应用前景。
285 2
|
5月前
|
Linux
嵌入式软件实现应用层和硬件层分层管理
嵌入式软件实现应用层和硬件层分层管理
55 0
|
域名解析 负载均衡 网络协议
应用层续(上)
应用层续(上)
90 0
应用层续(上)
|
网络协议 安全 小程序
应用层(中)
应用层(中)
75 0
|
缓存 网络协议 分布式数据库
应用层续(中)
应用层续(中)
161 0
|
存储 域名解析 编解码
应用层续(下)
应用层续(下)
211 0