如何将领域对象(ORM)映射到数据传输对象(DTO)的建议

13

我正在工作的当前系统使用Castle Activerecord来提供域对象和数据库之间的ORM(对象关系映射)。这很好,大多数情况下都能正常工作!

问题出现在Castle Activerecord对异步执行的支持上,更具体地说是管理对象所属的会话的SessionScope。长话短说,会发生糟糕的事情!

因此,我们正在寻找一种简单的方式来自动(思考“自动魔法”)将域对象(知道存在DB并且关心它)转换为DTO对象(不知道DB的存在,也不关心会话、映射属性或所有ORM相关的事项)。

有人有建议吗?起初我正在寻找一个基本的一对一对象映射。域对象Person将被映射到PersonDTO。我不想手动完成这个过程,因为这太浪费时间了。

显然,反射会让人想到,但我希望有些更好的IT知识可以提供更“酷”的建议。

哦,我正在使用C#,ORM对象如前所述是由Castle ActiveRecord映射的。


示例代码:

根据@ajmastrean的请求,我已经链接到一个我(糟糕地)模拟的示例。该示例包括捕获表单、捕获表单控制器对象、activerecord 存储库和一个异步助手。它稍微有点大(3MB),因为我包括了运行所需的ActiveRecored dll文件。您需要在本地机器上创建一个名为ActiveRecordAsync的数据库,或者只需更改.config文件。

示例的基本详情:

捕获表单

捕获表单引用了控制器

private CompanyCaptureController MyController { get; set; } 

在表单的初始化过程中,它会调用MyController.Load()方法。 private void InitForm () { 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方法,该方法从表单中调用,然后调用异步帮助程序异步调用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 的功能。

5个回答

9

我曾经解决过一个非常类似的问题,那就是将许多旧的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 to PersonDTO示例中:

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

任何在Person和PersonDTO上具有相同名称和类型的属性都将由运行时生成的方法复制,任何后续调用都不必再次生成,但会重用相同的已生成代码对于那些按照顺序的类型(即将PersonDTO复制到Person也会产生生成代码的影响)。
这是太多的代码要发布,但如果您感兴趣,我会努力上传一个样本到SkyDrive并在此处发布链接。
Richard

4
使用ValueInjecter,您可以将任何东西映射到任何东西,例如:

  • 对象 <-> 对象
  • 对象 <-> 表单/Web表单
  • DataReader -> 对象

它还具有很酷的功能,如展平和取消展平。

下载包含许多示例。


2

链接现在已经失效了。太好了,又一个死链。 - Bobson

0

实际上,我现在完全感到困惑了。因为你说:“因此,我们正在寻找一种简单的方法来自动地从领域对象(知道DB存在并关心它的对象)转换为DTO对象(不知道DB的存在,也不关心会话、映射属性或ORM中的所有事情)。”

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

  2. 您能否提供更多有关延迟加载问题的详细信息?


0

非常抱歉这里没有提供详细信息,但是一种基本的面向对象方法是将DTO作为ActiveRecord类的成员,并让ActiveRecord委托访问器和修改器给DTO。您可以使用代码生成或重构工具从AcitveRecord类快速构建DTO类。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接