DTO 到实体映射工具

2024-03-19

我有一个实体类Person及其对应的DTO类PersonDto.

public class Person: Entity
{
  public virtual string Name { get; set; }
  public virtual string Phone { get; set; }
  public virtual string Email { get; set; }
  public virtual Sex Sex { get; set; }
  public virtual Position Position { get; set; }
  public virtual Division Division { get; set; }
  public virtual Organization Organization { get; set; }
}

public class PersonDto: Dto
{
  public string Name { get; set; }
  public string Phone { get; set; }
  public string Email { get; set; }
  public Guid SexId { get; set; }
  public Guid PositionId { get; set; }
  public Guid DivisionId { get; set; }
  public Guid OrganizationId { get; set; }
}

收到 DTO 对象后,我必须将其转换为人员实体。现在我完全手动完成。代码如下所示。

public class PersonEntityMapper: IEntityMapper<Person, PersonDto>
{
  private IRepository<Person> _personRepository;
  private IRepository<Sex> _sexRepository;
  private IRepository<Position> _positionRepository;
  private IRepository<Division> _divisionRepository;
  private IRepository<Organization> _organizationRepository;

  public PersonEntityMapper(IRepository<Person> personRepository,
                            IRepository<Sex> sexRepository,
                            IRepository<Position> positionRepository,
                            IRepository<Division> divisionRepository,
                            IRepository<Organization> organizationRepository)
  {
    ... // Assigning repositories
  }

  Person Map(PersonDto dto)
  {
    Person person = CreateOrLoadPerson(dto);

    person.Name = dto.Name;
    person.Phone = dto.Phone;
    person.Email = dto.Email;

    person.Sex = _sexRepository.LoadById(dto.SexId);
    person.Position = _positionRepository.LoadById(dto.PositionId);
    person.Division = _divisionRepository.LoadById(dto.DivisionId);
    person.Organization = _organizationRepository.LoadById(dto.OrganizationId);

    return person;
  }
}

该代码实际上很简单。但随着实体数量的增加,映射器类的数量也会增加。结果是有很多类似的代码。另一个问题是,当存在模式关联时,我必须为其他存储库添加构造函数参数。我尝试注入某种存储库工厂,但它闻起来有一种臭名昭著的味道Service Locator所以我恢复了原来的解决方案。

这些映射器的单元测试还会产生许多类似的测试方法。

说了这么多,我想知道是否存在一种解决方案可以减少手动编写的代码量并使单元测试更容易。

提前致谢。

UPDATE

我完成了任务Value Injecter但后来我意识到我可以安全地删除它,其余的仍然可以工作。这是最终的解决方案。

public abstract class BaseEntityMapper<TEntity, TDto> : IEntityMapper<TEntity, TDto>
        where TEntity : Entity, new()
        where TDto : BaseDto
    {
        private readonly IRepositoryFactory _repositoryFactory;

        protected BaseEntityMapper(IRepositoryFactory repositoryFactory)
        {
            _repositoryFactory = repositoryFactory;
        }

        public TEntity Map(TDto dto)
        {
            TEntity entity = CreateOrLoadEntity(dto.State, dto.Id);

            MapPrimitiveProperties(entity, dto);
            MapNonPrimitiveProperties(entity, dto);

            return entity;
        }

        protected abstract void MapNonPrimitiveProperties(TEntity entity, TDto dto);

        protected void MapPrimitiveProperties<TTarget, TSource>(TTarget target, TSource source, string prefix = "")
        {
            var targetProperties = target.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).OrderBy(p => p.Name);
            var sourceProperties = source.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).OrderBy(p => p.Name);

            foreach (var targetProperty in targetProperties) {
                foreach (var sourceProperty in sourceProperties) {
                    if (sourceProperty.Name != string.Format("{0}{1}", prefix, targetProperty.Name)) continue;
                    targetProperty.SetValue(target, sourceProperty.GetValue(source, null), null);
                    break;
                }
            }
        }

        protected void MapAssociation<TTarget, T>(TTarget target, Expression<Func<T>> expression, Guid id) where T : Entity
        {
            var repository = _repositoryFactory.Create<T>();
            var propertyInfo = (PropertyInfo)((MemberExpression)expression.Body).Member;
            propertyInfo.SetValue(target, repository.LoadById(id), null);
        }

        private TEntity CreateOrLoadEntity(DtoState dtoState, Guid entityId)
        {
            if (dtoState == DtoState.Created) return new TEntity();

            if (dtoState == DtoState.Updated) {
                      return _repositoryFactory.Create<TEntity>().LoadById(entityId);
            }
            throw new BusinessException("Unknown DTO state");
        }
    }  

每个实体的映射是使用派生自的具体类执行的BaseEntityMapper。那个是为了Person实体看起来像这样。

public class PersonEntityMapper: BaseEntityMapper<Person, PersonDto>
    {
        public PersonEntityMapper(IRepositoryFactory repositoryFactory) : base(repositoryFactory) {}

        protected override void MapNonPrimitiveProperties(Person entity, PersonDto dto)
        {
            MapAssociation(entity, () => entity.Sex, dto.SexId);
            MapAssociation(entity, () => entity.Position, dto.PositionId);
            MapAssociation(entity, () => entity.Organization, dto.OrganizationId);
            MapAssociation(entity, () => entity.Division, dto.DivisionId);
        }
    }

显式调用MapAssociation防止将来的属性重命名。


您可以看一下两个最常用的对象-对象映射器:

自动映射器 http://automapper.org/

AutoMapper 是一个简单的小库,旨在解决一个欺骗性的问题 复杂的问题 - 摆脱将一个对象映射到的代码 其他。这种类型的代码写起来相当沉闷和无聊,所以 为什么不发明一种工具来为我们做到这一点呢?

价值注入者 http://valueinjecter.codeplex.com/

ValueInjecter 允许您定义自己的基于约定的匹配 算法(ValueInjections)以匹配(注入)源 值到目标值。

有一篇关于SO的比较文章:AutoMapper 与 ValueInjecter https://stackoverflow.com/questions/4663577/automapper-vs-valueinjecter

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

DTO 到实体映射工具 的相关文章

  • 静态类变量与外部变量相同,只是具有类作用域吗?

    在我看来 静态类变量与外部变量相同 因为你只需要declare它在static int x extern int x语句 并在其他地方实际定义它 通常在 cpp 文件中 静态类变量 h file class Foo static int x
  • 如何在编译C代码时禁用警告?

    我正在使用 32 位 Fedora 14 系统 我正在使用编译我的源代码gcc 有谁知道如何在编译c代码时禁用警告 EDIT 是的 我知道 最好的办法是修复这些警告以避免任何未定义 未知的行为 但目前在这里 我第一次编写了巨大的代码 并且在
  • 矩阵向量变换

    我正在编写一个代码来制作软件蒙皮器 骨骼 皮肤动画 并且我正处于 优化 阶段 蒙皮器工作得很好 并且在 Core 上 1 09 毫秒内对 4900 个三角形网格与 22 个骨骼进行蒙皮Duo 2 Ghz 笔记本 我需要知道的是 1 有人可以
  • 单线程公寓问题

    从我的主窗体中 我调用以下命令来打开一个新窗体 MyForm sth new MyForm sth show 一切都很好 但是这个表单有一个组合框 当我将其 AutoCompleteMode 切换为建议和追加时 我在显示表单时遇到了这个异常
  • 防止GDB中的PLT(过程链接表)断点

    在最新版本的 GDB 中 在库函数调用上设置断点会导致多个实际断点 调用过程链接表 PLT 实际的函数调用 这意味着当调用库函数时 我们每次都会经历两次中断 在以前的 GDB 版本中 只会创建 2 因此您只能得到一次中断 那么问题来了 是否
  • C#中Enum中定义的value__是什么

    What value 可能在这里 value MSN ICQ YahooChat GoogleTalk 我运行的代码很简单 namespace EnumReflection enum Messengers MSN ICQ YahooChat
  • 特征密集稀疏矩阵乘积是线程化的吗?

    我知道稀疏密集产品是根据文档进行线程化的 https eigen tuxfamily org dox TopicMultiThreading html https eigen tuxfamily org dox TopicMultiThre
  • 使用 AdHocWorkspace 会导致“不支持语言‘C#’”。

    在VS2015中使用Microsoft CodeAnalysis CSharp Workspaces的RC2 这段代码会抛出异常 var tree CSharpSyntaxTree ParseText var workspace new A
  • 允许使用什么类型的内容作为 C 预处理器宏的参数?

    老实说 我很了解 C 编程语言的语法 但对 C 预处理器的语法几乎一无所知 尽管我有时在编程实践中使用它 所以问题来了 假设我们有一个简单的宏 它扩展为空 define macro param 可以放入宏调用构造中的语法有哪些限制 调用宏时
  • 设计 Javascript 前端 <-> C++ 后端通信

    在我最近的将来 我将不得不制作一个具有 C 后端和 Web 前端的系统 要求 目前 我对此了解不多 我认为前端将触发数据传输 而不是后端 所以不需要类似 Comet 的东西 由于在该领域的经验可能很少 我非常感谢您对我所做的设计决策的评论
  • .NET JIT 编译的代码缓存在哪里?

    NET 程序首先被编译为 MSIL 代码 当它被执行时 JIT编译器会将其编译为本机机器代码 我想知道 这些JIT编译的机器代码存储在哪里 它只存储在进程的地址空间中吗 但由于程序的第二次启动比第一次快得多 我认为即使在执行完成后 该本机代
  • 使用(linq to sql)更新错误

    我有两个表 通过外键 CarrierID 绑定 Carrier CarrierID CarrierName CarrierID 1 CarrierName DHL CarrierID 2 CarrierName Fedex Vendor V
  • 相当于 C# 中 Java 的“ByteBuffer.putType()”

    我正在尝试通过从 Java 移植代码来格式化 C 中的字节数组 在 Java 中 使用方法 buf putInt value buf putShort buf putDouble 等等 但我不知道如何将其移植到 C 我尝试过 MemoryS
  • “int i=1,2,3”和“int i=(1,2,3)”之间的区别 - 使用逗号运算符的变量声明[重复]

    这个问题在这里已经有答案了 int i 1 2 3 int i 1 2 3 int i i 1 2 3 这些说法有什么区别 我无法找出任何具体原因 Statement 1 Result Compile error 运算符的优先级高于 运算符
  • ASP.NET Core Razor Page 多路径路由

    我正在使用 ASP NET Core 2 0 Razor Pages 不是 MVC 构建系统 但在为页面添加多个路由时遇到问题 例如 所有页面都应该能够通过 abc com language 访问segment shop mypage 或
  • 如何将对象转换为传递给函数的类型?

    这不会编译 但我想做的只是将对象转换为传递给函数的 t public void My Func Object input Type t t object ab TypeDescriptor GetConverter t ConvertFro
  • C++ [Windows] 可执行文件所在文件夹的路径[重复]

    这个问题在这里已经有答案了 我需要访问一些文件fstream在我的 Windows 上的 C 应用程序中 这些文件都位于我的exe文件所在文件夹的子文件夹中 获取当前可执行文件的文件夹路径的最简单且更重要的 最安全的方法是什么 Use 获取
  • 如何在 Javascript 中将字符串数组转换为特定的树结构

    我从后端获取文件路径列表 它代表文件夹结构 如下所示 paths path to file1 doc path to file2 doc foo bar doc 路径的长度是任意的 为了使用文件树组件 角度2树组件 https github
  • 从有符号字符转换为无符号字符然后再转换回来?

    我正在使用 JNI 并有一个 jbyte 类型的数组 其中 jbyte 表示为有符号字符 即范围从 128 到 127 jbyte 表示图像像素 对于图像处理 我们通常希望像素分量的范围为0到255 因此 我想将jbyte值转换为0到255
  • 使用 System.Security.Cryptography 将 Win32 Crypto API 调用转换为 C#

    我接到一项任务 要删除我们产品的一个 dll 并将其替换为纯 C 的 dll 旧的 DLL 是 NET 2 0 托管 C C CLI 它包装了对 Win32 本机加密 API 的调用 新的 DLL 应公开具有相同名称和方法的新对象 但应使用

随机推荐

  • 如何仅使用查找和替换来查找并计算字符串中子字符串的所有出现次数?

    输入需要更低 最后程序必须打印出现的次数 例如内存 smthing str input if smthing smthing lower smthing find mem 我对此彻底崩溃了 所以我无法走多远 我忘了告诉你我不能使用计数或列表
  • openlayers 3 可以使用 gif 渲染动画标记吗

    我想问如何让标记像 openlayers 2 一样显示动画 gif 图片 它可以显示动画标记 我想要的是显示动画 gif 标记而不使标记移动 这可能吗 style anchorXUnits fraction anchorYUnits pix
  • 如何在 Emacs 中为 LaTeX 模式定义两个 ispell 字典?

    我有一份包含许多第二语言引文的文档 我如何将第二语言定义为 Ispell 我在文件顶部有这个 Local IspellDict brasileiro 我尝试了这个 但没有成功 Local IspellDict brasileiro engl
  • Java 中的异步事件调度

    我正在将 C 程序移植到 Java 该程序大量使用委托和委托的BeginInvoke异步通知事件的方法 以数据通信线程为例 它可能必须将其状态以及 GUI 通知给另一个工作线程 在我看来 通知不同班级的各种事件的最佳方法是有一个IClass
  • JavaScript 中的 getElementsByClassName().forEach() 函数不起作用

    我试图使用 JavaScript 通过类名获取 HTML 的每个元素 然后根据 a 中的值更改其高度和宽度range object onchange 浏览器显示错误 document getElementsByClassName forEa
  • VB6 有什么好的 TDD 工具或资源吗?

    是的 我知道我已经落后于时代了 但是我这里有一个古老的 VB6 编辑器应用程序 我相信没有人会很快升级到 NET 它使用了几个第三方 DLL 工具 并且由于它仍然使用旧的 RichEdit 控件 我基本上可以仅用这个工具来创建我自己的 Bu
  • 匹配法语和德语字符的正则表达式

    我正在解析请求参数以查找任何易受攻击的字符以防止 XSS 威胁 我们的网络应用程序支持英语以外的法语和德语 我使用以下正则表达式来实现此目的 但它无法处理法语和德语 a zA Z0 9 r n 对此的任何建议都将受到高度赞赏 p L 将匹配
  • 如何确定 PostScript 字体的高度和深度?

    我正在寻找估计 PostScript 字体高度 上升部分的空间 和深度 下降部分的空间 的 PostScript 代码 字体的边界框可以吗 FontBBox 用于此目的 这是我的问题的一些背景 字体的字形位于基线上 显然 带有下降部分的字形
  • 使用并行赋值交换数组元素

    感兴趣这个问题 https stackoverflow com questions 4181808 swapping in ruby 我玩过一些数组和方法调用的并行赋值 这是一个典型的示例 尝试按数组中的值交换两个成员 deck A B C
  • symfony2创建没有实体类的表单

    使用Symfony2 3 4 我正在尝试创建一个不使用类型的表单 它实际上是一个非常小的表单 只有两个选择从数据库加载它们的选项 到目前为止它有效 我不能做的是获取表单数据 在控制器 提交时 我尝试按照说明进行操作here http sym
  • Firebase Firestore,查询用户朋友的帖子

    我正在寻找使用 Firebase 创建社交媒体源 我的数据结构如下 users uid details friends uid friends sub collection fuid details posts postId postedB
  • lock(){} 是锁定资源,还是锁定一段代码?

    我仍然很困惑 当我们写这样的东西时 Object o new Object var resource new Dictionary
  • 是否可以指定何时运行构建功能?

    TeamCity 8 1 4 内部版本 30168 我注意到构建功能始终作为构建步骤中第一步的第一部分运行 但是是否可以使构建功能运行 例如作为步骤2的第一部分 或者作为 Step1 的最后一部分 任何帮助表示赞赏 如果感兴趣 这是我的具体
  • 如何在 R 数据框中用 NA 替换空字符串?

    我的第一个方法是使用na strings 当我从 csv 读取数据时 由于某种原因这不起作用 我也尝试过 df df lt NA 这给了我一个错误 不能使用矩阵或数组进行列索引 我只尝试了该专栏 df col df col lt NA 这会
  • 试飞已过期

    我正在尝试使用试飞 https developer apple com app store Testflight 新的 分享我的应用程序 我现在已经与我的 3 位内部测试人员分享了iTunes连接 https itunesconnect a
  • JPA:查询实体内的可嵌入列表

    我正在尝试根据实体列表中的某些条件 提取 可嵌入类 借助 JPQL 或 Criteria API 我不是这方面的专家 所以请帮助我 已经用谷歌搜索了 4 个小时 没有任何结果 这些是课程 Entity public class PostOf
  • 强化修复经常被误用的身份验证

    当我使用 Fortify 进行扫描时 我在下面的代码中遇到了诸如 经常被误用 身份验证 之类的漏洞 这个问题有解决办法吗 我看过相关帖子 但没能找到解决方案 使用 ESAPI 我提供了主机名和 ipadress 的正则表达式 但它不起作用
  • 在php中获取上个月的日期

    我想知道上个月的日期 我写了这个 prevmonth date M Y 这给了我当前的月份 年份 我不知道是否应该使用strtotime mktime 有什么关于时间戳的吗 我是否需要在之后添加一些内容来重置 以便我网站上的所有时间戳的日期
  • F# 中的对象表达式和捕获状态

    是什么让第一次实施KO type IToto abstract Toto unit gt unit new IToto with member this Toto fun gt new IToto with member this Toto
  • DTO 到实体映射工具

    我有一个实体类Person及其对应的DTO类PersonDto public class Person Entity public virtual string Name get set public virtual string Phon