我一直在努力解决这个问题,但是在ASP.NET MVC上这个特定主题的信息似乎很少。我一直在谷歌搜索几天,并没有真正能够解决这个特定的问题。
我有一个3层项目。业务,DAL和UI / Web层。在DAL中是dbcontext,存储库和工作单元。在业务层中是包含所有接口和EF模型的域层。在业务层中,还有一个服务层,其中包含用于EF模型的DTO和用于访问存储库的通用存储库服务。 This图片应该有助于解释它。
我的问题是,我似乎无法弄清楚如何使用DTO从业务层传输数据。
我为DTO创建了服务类。我有一个ImageDTO和模型和图像锚点相同。我为每个DTO创建了一个服务类。所以我有一个图像服务和主播服务。这些服务继承存储库服务,并且目前实现自己的服务。但就我而言,那就是那个。由于这些服务具有通过IoC接收IUnitOfWork接口的构造函数,因此我几乎陷入困境。
如果我直接从UI引用服务,一切正常,但我无法理解如何使用DTO将数据从服务层传输到UI层,反之亦然。 / p>
我的服务层:
商业/服务/ DTO的
public class AnchorDto
{
public int Id { get; set; }
public int x1 { get; set; }
public int y1 { get; set; }
public int x2 { get; set; }
public int y2 { get; set; }
public string description { get; set; }
public int imageId { get; set; }
public int targetImageId { get; set; }
public AnchorDto(int Id, int x1, int y1, int x2, int y2, string description, int imageId, int targetImageId)
{
// Just mapping input to the DTO
}
}
public class ImageDto
{
public int Id { get; set; }
public string name { get; set; }
public string title { get; set; }
public string description { get; set; }
public virtual IList<AnchorDto> anchors { get; set; }
public ImageDto(int Id, string name, string title, string description, IList<AnchorDto> anchors )
{
// Just mapping input to DTO
}
}
商务/服务/服务
public class RepoService<TEntity> : IRepoService<TEntity> where TEntity : class
{
private IRepository<TEntity> repo;
public RepoService(IUnitOfWork repo)
{
this.repo = repo.GetRepository<TEntity>();
}
public IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "")
{
return repo.Get(filter, orderBy, includeProperties);
}
public TEntity GetByID(object id)
{
return repo.GetByID(id);
}
public void Insert(TEntity entity)
{
repo.Insert(entity);
}
public void Delete(object id)
{
repo.Delete(id);
}
public void Delete(TEntity entityToDelete)
{
repo.Delete(entityToDelete);
}
public void Update(TEntity entityToUpdate)
{
repo.Update(entityToUpdate);
}
}
Image Service,IImageService接口当前是空的,直到我弄清楚我需要实现什么。
public class ImageService : RepoService<ImageModel>, IImageService
{
public ImageService(IUnitOfWork repo)
: base(repo)
{
}
}
目前我的控制器并没有真正起作用,并且没有使用服务层,所以我决定不再包含任何这些。我计划在将此问题排序后,使用自动映射器将DTO映射到ViewModels。
所以,现在,请那些知识渊博的人给我这个我想念的想法,以便我能解决这个问题吗?
答案 0 :(得分:35)
您的服务应该接收DTO,将它们映射到业务实体并将它们发送到存储库。它还应该从存储库中检索业务实体,将它们映射到DTO并将DTO作为响应返回。因此,您的业务实体永远不会离开业务层,只有DTO会这样做。
然后您的UI \ Weblayer应该不知道业务实体。 Web层应该只知道DTO。要强制执行此规则非常重要,您的UI层不使用服务实现类(应该是私有的),只使用接口。服务接口不应该依赖于业务实体,只能依赖于DTO。
因此,您需要基于DTO的服务接口,并且您的基本服务类需要DTO的另一个通用参数。我喜欢有实体和DTO的基类,所以它们可以声明为:
//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
{
...
}
然后,您需要一种方法将实体和DTO之间的映射添加到该基本服务,而无需实际实现映射(因为它取决于每个具体实体和DTO类)。您可以声明执行映射的抽象方法,并且需要在每个特定服务上实现(如ImageService
)。基础RepoService的实现如下:
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);
或者您可以声明映射服务,使用应由IOC提供的适当映射服务添加依赖项(如果您需要在不同服务上使用相同的映射,则更有意义)。 RepoService的实现如下:
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);
}
在这两种情况下(抽象方法或地图服务),您可以手动或使用Automapper之类的工具实现实体和DTO之间的映射。但是在使用AutoMapper和实体框架时应该非常小心,尽管这是另一个主题! (谷歌有点关于这一点并收集有关该主题的一些信息。作为第一个建议,请注意在加载数据时对数据库执行的查询,以免加载超过需要或发送许多查询。保存数据时注意你的收藏和关系)
可能很长,但我希望它有所帮助!