关于如何从域 (ORM) 对象映射到数据传输对象 (DTO) 的建议

Posted

技术标签:

【中文标题】关于如何从域 (ORM) 对象映射到数据传输对象 (DTO) 的建议【英文标题】:Suggestions on how to map from Domain (ORM) objects to Data Transfer Objects (DTO) 【发布时间】:2008-09-03 13:17:59 【问题描述】:

我正在开发的当前系统利用 Castle Activerecord 在域对象和数据库之间提供 ORM(对象关系映射)。这一切都很好,而且大多数时候实际上效果很好!

问题来自于 Castle Activerecords 对异步执行的支持,更具体地说,是管理对象所属会话的 SessionScope。长话短说,坏事发生了!

因此,我们正在寻找一种方法来轻松地将域对象(他们知道数据库存在并关心)转换(自动思考)到 DTO 对象(他们对数据库一无所知并且不关心会话、映射属性)或所有东西ORM)。

有没有人对此提出建议。首先,我正在寻找对象的基本一对一映射。域对象 Person 将被映射为 PersonDTO。我不想手动执行此操作,因为这很浪费。

很明显,我想到了反思,但我希望通过这个网站周围的一些更好的 IT 知识,“cooler”会被建议。

哦,我在 C# 中工作,如前所述,使用 Castle ActiveRecord 映射的 ORM 对象。


示例代码:

应@ajmastrean 的要求,我将linked 提供给我(严重)一起模拟的一个示例。该示例有一个捕获表单、一个捕获表单controller对象、activerecord repository和一个async 帮手。它有点大(3MB),因为我包含了运行它所需的 ActiveRecored dll。您需要在本地计算机上创建一个名为 ActiveRecordAsync 的数据库,或者只需更改 .config 文件。

示例的基本细节:

捕获表格

捕获表单有对控制器的引用

private CompanyCaptureController MyController  get; set;  

在初始化表单时它调用 MyController.Load() 私人无效初始化表格() MyController = new CompanyCaptureController(this); MyController.Load(); 这将返回一个名为 LoadComplete() 的方法

public void LoadCompleted (Company loadCompany)

    _context.Post(delegate
    
         CurrentItem = loadCompany;
         bindingSource.DataSource = CurrentItem;
         bindingSource.ResetCurrentItem();
         //TOTO: This line will thow the exception since the session scope used to fetch loadCompany is now gone.
         grdEmployees.DataSource = loadCompany.Employees;
         , null);
    

这是 “坏东西” 发生的地方,因为我们正在使用设置为延迟加载的 Company 的子列表。

控制者

控制器有一个从表单调用的 Load 方法,然后它调用 Asyc 助手以异步调用 LoadCompany 方法,然后返回 Capture 表单的 LoadComplete 方法。

public void Load ()

    new AsyncListLoad<Company>().BeginLoad(LoadCompany, Form.LoadCompleted);

LoadCompany() 方法只是利用存储库来查找已知公司。

public Company LoadCompany()

    return ActiveRecordRepository<Company>.Find(Setup.company.Identifier);

该示例的其余部分相当通用,它有两个继承自基类的域类、一个用于插入一些数据的设置文件和一个用于提供 ActiveRecordMediator 功能的存储库。

【问题讨论】:

【参考方案1】:

我解决了一个与此非常相似的问题,我将许多旧 Web 服务合同中的数据复制到 WCF 数据合同中。我创建了许多具有这样签名的方法:

public static T ChangeType<S, T>(this S source) where T : class, new()

此方法(或任何其他重载)第一次为两种类型执行时,它会查看每种类型的属性,并根据名称和类型决定哪些属性存在于这两种类型中。它采用这个“成员交集”并使用 DynamicMethod 类来模拟 IL 以将源类型复制到目标类型,然后将生成的委托缓存在线程安全的静态字典中。

一旦创建了委托,它的速度非常快,而且我提供了其他重载来传递委托,以复制与交集条件不匹配的属性:

public static T ChangeType<S, T>(this S source, Action<S, T> additionalOperations) where T : class, new()

...所以您可以为您的 Person 到 PersonDTO 示例执行此操作:

Person p = new Person( /* set whatever */);
PersonDTO = p.ChangeType<Person, PersonDTO>();

并且 Person 和 PersonDTO 上的任何属性(同样,具有相同的名称和类型)将由运行时发出的方法复制,并且不必发出任何后续调用,但会重用相同的发出代码 对于那些按顺序排列的类型(即,将 PersonDTO 复制到 Person 也会导致发出代码的命中)。

发布的代码太多,但如果您有兴趣,我会努力将示例上传到 SkyDrive 并在此处发布链接。

理查德

【讨论】:

【参考方案2】:

使用ValueInjecter,通过它你可以将任何东西映射到任何东西,例如

对象 对象 对象 表单/WebForm DataReader -> 对象

它具有很酷的功能,例如:扁平化和非扁平化

下载包含大量示例

【讨论】:

【参考方案3】:

你应该使用我在博客中提到的自动映射器:

http://januszstabik.blogspot.com/2010/04/automatically-map-your-heavyweight-orm.html#links

只要两个对象上的属性名称相同,自动映射器就会处理它。

【讨论】:

现在是死链接。是的,linkrot。【参考方案4】:

我很抱歉没有真正把细节放在这里,但基本的 OO 方法是让 DTO 成为 ActiveRecord 类的成员,并让 ActiveRecord 将访问器和修改器委托给 DTO。您可以使用代码生成或重构工具从 AcitveRecord 类中快速构建 DTO 类。

【讨论】:

【参考方案5】:

其实我现在完全糊涂了。 因为您在说:“因此,我们正在寻找一种方法来轻松地将域对象(他们知道数据库存在并关心)转换(自动思考)到 DTO 对象(他们对数据库一无所知并且不关心会话) , 映射属性或所有事物 ORM)。”

    域对象知道并关心 DB?这难道不是域对象的全部意义在于仅包含业务逻辑并且完全不知道 DB 和 ORM 吗?....您必须拥有这些对象?如果它们包含所有这些东西,你只需要修复它们......这就是为什么我有点困惑 DTO 是如何出现的

    您能否详细说明您在延迟加载时遇到的问题?

【讨论】:

以上是关于关于如何从域 (ORM) 对象映射到数据传输对象 (DTO) 的建议的主要内容,如果未能解决你的问题,请参考以下文章

Node.js关于ORM框架以及sequelize模块的使用

通过java反射实现简单的关于MongoDB的对象关系映射(ORM).

GreenDao数据库框架

对象关系映射(ORM)

第54篇ORM对象关系映射 如何使用ORM与数据可建立连接

ORMJPAHibernateMyBatis之间的关系梳理