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

时间:2020-03-05 18:47:00  来源:igfitidea点击:

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

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

因此,我们正在寻找一种方法,可以轻松地(自动地思考)从Domain对象(他们知道数据库存在并且在乎)转换为DTO对象(他们对DB一无所知,并且不在乎会话,映射属性或者所有事物ORM)。

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

显然已经想到了反思,但是我希望本站点周围有一些更好的IT知识,可以提出"更酷"的建议。

哦,我正在使用C#工作,就像在使用Castle ActiveRecord映射之前所说的ORM对象一样。

示例代码:

根据@ajmastrean的请求,我已链接到一个我(很糟糕)一起嘲笑的示例。该示例具有捕获表单,捕获表单控制器,域对象,activerecord存储库和异步帮助器。它有点大(3MB),因为我包含了ActiveRecored dll才能运行它。我们将需要在本地计算机上创建一个名为ActiveRecordAsync的数据库,或者仅更改.config文件。

示例的基本细节:

捕获表格

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

private CompanyCaptureController MyController { get; set; }

初始化窗体时,它将调用MyController.Load()
私人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);
    }
}

这是发生"坏东西"的地方,因为我们使用的公司子级列表设置为"延迟加载"。

控制器

控制器具有从表单中调用的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功能的存储库。

解决方案

回答

我解决了一个与此非常相似的问题,在该问题中,我将许多旧的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并在此处发布链接。

理查德

回答

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

回答

其实我现在完全被弄糊涂了。
因为我们说的是:"因此,我们正在寻找一种方法,可以轻松地(自动地思考)从Domain对象(他们知道数据库存在并且在乎)转换为DTO对象(他们对数据库一无所知并且不在意会话) ,映射属性或者所有事物ORM)。"

  • 域对象知道并关心数据库吗?这不是域对象的全部内容仅包含业务逻辑并且完全不了解DB和ORM吗?...。我们必须拥有这些对象吗?如果它们包含所有内容,则只需要修复它们即可。这就是为什么我有点困惑DTO的外观
  • 我们能否提供更多有关延迟加载所面临的问题的详细信息?

回答

我们应该自动贴图我在这里写过的博客:

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

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

回答

使用ValueInjecter,通过它我们可以将任何内容映射到任何内容,例如

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

它具有很酷的功能,例如:展平和展平

下载中包含许多样本