有关如何从域(ORM)对象映射到数据传输对象(DTO)的建议
我正在使用的当前系统使用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->对象
它具有很酷的功能,例如:展平和展平
下载中包含许多样本