【问题标题】:How do I map the DTO files to my Models in my .Net Core project如何将 DTO 文件映射到我的 .Net Core 项目中的模型
【发布时间】:2019-03-07 23:03:59
【问题描述】:

我以前从未使用过 .Net Core 项目,但有使用过 .Net 的历史,包括 MVC 和实体框架。我正在使用一个新的 .Net Core 项目,它有五个解决方案文件夹,EHA.PROJ.API、EHA.PROJ.DTO、EHA.PROJ.Repository、EHA.PROJ.Repository.Test 和 EHA.PROJ.Web。 EHA.PROJ.DTO 文件夹有很多文件,例如 CategoryDTO.cs,看起来像这样

namespace EHA.PROJ.DTO
{
    public class CategoryDescDTO
    {
        public int CategoryRef { get; set; }

        public string CategoryName { get; set; }
    }
}

我希望设置一个映射安排,以将数据从 EHA.PROJ.DTO 文件获取到我的 EHA.PROJ.Web 文件夹中我的模型文件夹中的模型文件。我一直在浏览,因为我以前从未做过这样的事情,因为我以前使用实体框架处理来自 DAL 文件夹的数据,并通过连接字符串完成连接。我猜必须有一些过程来映射我的 dbContext 中的数据以连接两个文件夹中的文件。我确实找到了一些关于 AutoMapper 的信息,但不确定如何实现它。

.Net Core 的这种安排对我来说是新的,所以如果有人可以提供任何示例或指出正确的方向,我将不胜感激。

【问题讨论】:

  • AutoMapper 工具以两种方式工作,它允许您获取模型数据并返回到客户端并返回到 DTO,反之亦然,您绝对应该研究 AutoMapper。

标签: c# api asp.net-core visual-studio-2017 automapper


【解决方案1】:

您的第一个问题是您的网络项目中有实体。马上,您在 Web 项目和数据层之间建立了紧密耦合,这几乎否定了所有其他层的意义:DTO、存储库等。您希望将实体和上下文移出真实的数据层(即与您的 Web 项目分开的类库项目)。

然后,您要决定数据层应该扩展多远。如果 API 是为网站提供数据,那么您实际上希望从 Web 项目中删除对数据层的所有依赖项。您的 DTO 项目将在 API 和 Web 项目之间共享,并且您的 API 将发送/接收您的 DTO,在后台从您的实体来回映射。

但是,如果您要这样做,那么存储库项目应该完全消失。只需让您的 API 直接与 EF 和您的实体一起工作。您的抽象是 API 本身;不需要另一个。拥有存储库层的唯一原因是 API 和 Web 都将直接使用存储库,这实际上不是一个很好的模式。您将不可避免地最终得到一堆特定于每个项目的重复逻辑。

简单地说,在使用像 EF 这样的 ORM 时,存储库模式是多余的。 ORM 你的数据层。您只是在使用第三方提供的 DAL,而不是您自己创建的 DAL。存储库模式仅在直接使用 ADO.NET 之类的东西直接使用 SQL 时才有意义。否则,摆脱它。

如果您的目标只是隐藏数据层,那么拥有 API 就足够抽象了。该网站对底层数据源一无所知,API 实际上只是一个通过 HTTP 返回 JSON 而不是直接对象实例的服务层,即 API 本质上是您的“存储库”层。 p>

转向基于微服务的架构可以进一步改善这种情况。这样一来,您基本上就拥有了多个小型、自包含的 API,它们只与您的域或功能的一部分一起工作。每个都可以直接使用 EF,或者完全不同的 ORM,甚至完全不同的堆栈。您可以在 Node.js 或 python 等基础上构建 API。网站只是向各种服务发出请求以获取所需的数据,并不知道或关心这些服务的实际工作方式。

【讨论】:

    【解决方案2】:

    由于易用性和内置的依赖注入,我在 .NET Core 项目中使用 Automapper 已经有一段时间了。

    从 PM 安装:

    Install-Package AutoMapper
    Install-Package AutoMapper.Extensions.Microsoft.DependencyInjection
    

    Startup.cs,ConfigureServices方法中注册:

    services.AddAutoMapper(typeof(Startup));

    创建一个类来保存您的映射,例如MappingProfile.cs 使用 automapper 中的 Profile,您可以定义映射。

    public class MappingProfile : Profile
    {
        public MappingProfile()
        {
            CreateMap<Operator, OperatorDto>().ReverseMap();
        }
    }
    

    }

    上面的映射告诉automapper Operator可以映射到OperatorDtoOperatorDto可以映射到Operator

    在你的控制器中,你可以注入一个IMapper

        private readonly IMapper _mapper;
    
        public OperatorsController(IMapper mapper)
        {
            _mapper = mapper;
        }
    

    并映射如下值:

    var dto = _mapper.Map&lt;OperatorDto&gt;(op); // Map op object to dto

    var op = _mapper.Map&lt;Operator&gt;(dto); // Map dto to op object

    Automapper 提供自定义映射,如果您需要的话。

    虽然使用 Automapper 执行映射非常容易,但您需要学习该框架。

    我相信学习它是值得的,因为它将为您节省大量未来编写映射代码的时间。

    【讨论】:

    • 在这种情况下,您必须为每个模型和 dto 添加一个代码行到您的配置文件类。有时我们有 600 个模型。
    【解决方案3】:

    这篇文章是一个很好的参考开始:https://buildplease.com/pages/repositories-dto/

    我的建议是有一个 DTO 汇编器,将您的模型映射到 DTO 对象。所以,你从你的 DTO 类开始:

    namespace EHA.PROJ.DTO
    {
        public class CategoryDescDTO
        {
            public int CategoryRef { get; set; }
    
            public string CategoryName { get; set; }
        }
    }
    

    然后构建汇编器:

    public class CategoryDescAssembler {
        public CategoryDescDTO WriteDto(CategoryDesc categoryDesc) {
            var categoryDescDto = new CategoryDescDTO();
            categoryDescDto.CategoryRef = categoryDesc.CategoryRef;
            categoryDescDto.CategoryName = categoryDesc.CategoryName;
            return categoryDescDto;
        }
    }
    

    现在您实现该服务来完成获取 DTO 对象所需的所有工作:

    public class CategoryDescService : ICategoryDescService {
        private readonly IRepository<CategoryDesc> _categoryDescRepository;
        private readonly CategoryDescAssembler _categoryDescAssembler;
    
        public CategoryDescService(IRepository<CategoryDesc> categoryDescRepository, CategoryDescAssembler categoryDescAssembler) {
            _categoryDescRepository= categoryDescRepository;
            _categoryDescAssembler= categoryDescAssembler;
        }
    
        public CategoryDescDTO GetCategoryDesc(int categoryRef) {
            var categDesc = _categoryDescRepository.Get(x => x.CategoryRef == categoryRef);
            return _categoryDescAssembler.WriteDto(categDesc);
        }
    }
    

    界面如下:

    public interface ICategoryDescService
    {
        CategoryDescDTO GetCategoryDesc(int categoryRef);
    } 
    

    然后您需要将该服务添加到您的 Startup.cs:

    public void ConfigureServices(IServiceCollection services)
    {
        ...
        services.AddTransient<ICategoryDescService, CategoryDescService>();
    }
    

    现在你可以从你的视图控制器调用你的服务了。

    【讨论】:

    • 谢谢 MarioMendieta,这看起来正是我所需要的,但我可以问几个问题。我假设 CategoryDe​​scAssembler 位于 Web 端,我需要为每个 DTO 文件(目前大约有 25 个)使用不同的 Assembler,这些文件将与 CategoryDe​​scService.cs 并存,并为每个 DTO 文件重复该过程?
    • 这完全取决于您的业务逻辑。您可能有一个像 CategoryService.cs 这样的服务,它将处理与类别有关的所有逻辑。因此,一个服务可以使用许多实体,并且可能有许多实体根本不被单个服务使用。此外,汇编器不一定位于 Web 层。您可能在您的存储库和您的 Web 层之间有一个中间层来处理这个问题。同样,这取决于您的具体用例。看看这个答案:stackoverflow.com/a/6621636/5090767
    • 谢谢你,给了我足够的动力
    猜你喜欢
    • 2011-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-22
    • 2021-11-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多