構建應用層服務


返回總目錄《一步一步使用ABP框架搭建正式項目系列教程》


今天談談《構建應用層服務》。

理論學習:

應用服務提供了一些門面樣式方法來分離表現層和領域層。這樣做的目的也是為了解耦,以后表現層就不用直接和業務邏輯層(核心層)打交道了,而是通過應用服務層(相當於媒介)來處理。應用服務層不僅定義了很多服務方法供表現層直接調用,而且還提供了一些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的專題,敬請期待。代碼沒有難度,也就不多做解釋了。今天就到這里吧。下次再講。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM