Using DTO to transfer data between service layer and UI layer

感情迁移 提交于 2019-11-29 20:31:12

Your service should receive DTOs, map them to business entities and send them to the repository. It should also retrieve business entities from the repository, map them to DTOs and return the DTOs as reponses. So your business entities never get out from the business layer, only the DTOs do.

Then your UI\Weblayer should be unaware of the business entities. The web layer should only know about the DTOs. To enforce this rule is very important that your UI layer does not uses the service implementation classes (which should be private), just the interfaces. And the service interfaces shouldn´t depend on the business entities, just the DTOs.

So you need service interfaces based on DTOs, and your base service class needs another generic argument for the DTO. I like to have a base class for entities and DTOs so they can be declared as:

//Your UI\presentation layer will work with the interfaces (The inheriting ones) 
//so it is very important that there is no dependency
//on the business entities in the interface, just on the DTOs!
protected interface IRepoService<TDto> 
    where TDto: DTOBase
{
    //I'm just adding a couple of methods  but you get the idea
    TDto GetByID(object id);
    void Update(TDto entityToUpdateDto)
}

//This is the interface that will be used by your UI layer
public IImageService: IRepoService<ImageDTO>
{
}

//This class and the ones inheriting should never be used by your 
//presentation\UI layer because they depend on the business entities!
//(And it is a best practice to depend on interfaces, anyway)
protected abstract class RepoService<TEntity, TDto> : IRepoService<TDto> 
    where TEntity : EntityBase
    where TDto: DTOBase
{
    ... 
}

//This class should never be used by your service layer. 
//Your UI layer should always use IImageService
//You could have a different namespace like Service.Implementation and make sure
//it is not included by your UI layer
public class ImageService : RepoService<ImageModel, ImageDto>, IImageService
{
    ...
}

You then need a way of adding the mapping between entities and DTO to that base service without actually implementing the mapping (as it depends on each concrete entity and DTO classes). You could declare abstract methods that perform the mapping and will need to be implemented on each specific service (like ImageService). The implemantion of the base RepoService would look like:

public TDto GetByID(object id)
{
    //I'm writing it this way so its clear what the method is doing
    var entity = repo.GetByID(id);
    var dto = this.EntityToDto(entity);
    return dto;
}

public void Update(TDto entityToUpdateDto)
{
    var entity = this.DtoToEntity(entityToUpdateDto)
    repo.Update(entity);
}

//These methods will need to be implemented by every service like ImageService
protected abstract TEntity DtoToEntity(TDto dto);
protected abstract TDto EntityToDto(TEntity entity);

Or you could declare mapping services, adding a dependency with an appropiated mapping service that should be provided by your IOC (This makes more sense if you need the same mapping on different services). The implementation of RepoService would look like:

private IRepository<TEntity> _repo;
private IDtoMappingService<TEntity, TDto> _mappingService;

public RepoService(IUnitOfWork repo, IDtoMappingService<TEntity, TDto> mapping)
{
    _repo = repo.GetRepository<TEntity>();
    _mappingService = mapping;
}

public TDto GetByID(object id)
{
    //I'm writing it this way so its clear what the method is doing
    var entity = repo.GetByID(id);
    var dto = _mappingService.EntityToDto(entity);
    return dto;
}

public void Update(TDto entityToUpdateDto)
{
    var entity = _mappingService.DtoToEntity(entityToUpdateDto)
    repo.Update(entity);
}

//You will need to create implementations of this interface for each 
//TEntity-TDto combination
//Then include them in your dependency injection configuration
public interface IDtoMappingService<TEntity, TDto>
    where TEntity : EntityBase
    where TDto: DTOBase
{
    public TEntity DtoToEntity(TDto dto);
    public TDto EntityToDto(TEntity entity);
}

In both cases (abstract methods or mapping services), you can implement the mapping between the entities and DTOs manually or using a tool like Automapper. But you should be very careful when using the AutoMapper and entity framework, although that is another topic! (Google a bit about that and collect some information on the topic. As a first advice pay attention to the queries being executed against the database when loading data so you don´t load more than needed or send many queries. When saving data pay attention to your collections and relationships)

Long post maybe, but I hope it helps!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!