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

发布于 2024-07-05 22:44:21 字数 2344 浏览 2 评论 0原文

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

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

因此,我们正在寻找一种方法,可以轻松地从域对象(知道数据库存在并关心)转换(自动思考)到 DTO 对象(对数据库一无所知,也不关心会话、映射属性或所有事物)对象关系映射)。

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

显然,我想到了反思,但我希望通过这个网站上流传的一些更好的 IT 知识,可以建议“更酷”。

哦,我正在使用 C# 工作,ORM 对象如之前所说,使用 Castle ActiveRecord 进行映射。


示例代码:

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

示例的基本细节:

捕获表单

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

private CompanyCaptureController MyController { get; set; } 

在初始化表单时,它调用 MyController.Load() 私有无效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 方法,然后调用 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 功能的存储库。

The current system that I am working on makes use of Castle Activerecord to provide ORM (Object Relational Mapping) between the Domain objects and the database. This is all well and good and at most times actually works well!

The problem comes about with Castle Activerecords support for asynchronous execution, well, more specifically the SessionScope that manages the session that objects belong to. Long story short, bad stuff happens!

We are therefore looking for a way to easily convert (think automagically) from the Domain objects (who know that a DB exists and care) to the DTO object (who know nothing about the DB and care not for sessions, mapping attributes or all thing ORM).

Does anyone have suggestions on doing this. For the start I am looking for a basic One to One mapping of object. Domain object Person will be mapped to say PersonDTO. I do not want to do this manually since it is a waste.

Obviously reflection comes to mind, but I am hoping with some of the better IT knowledge floating around this site that "cooler" will be suggested.

Oh, I am working in C#, the ORM objects as said before a mapped with Castle ActiveRecord.


Example code:

By @ajmastrean's request I have linked to an example that I have (badly) mocked together. The example has a capture form, capture form controller, domain objects, activerecord repository and an async helper. It is slightly big (3MB) because I included the ActiveRecored dll's needed to get it running. You will need to create a database called ActiveRecordAsync on your local machine or just change the .config file.

Basic details of example:

The Capture Form

The capture form has a reference to the contoller

private CompanyCaptureController MyController { get; set; } 

On initialise of the form it calls MyController.Load()
private void InitForm ()
{
MyController = new CompanyCaptureController(this);
MyController.Load();
}
This will return back to a method called 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);
    }
}

this is where the "bad stuff" occurs, since we are using the child list of Company that is set as Lazy load.

The Controller

The controller has a Load method that was called from the form, it then calls the Asyc helper to asynchronously call the LoadCompany method and then return to the Capture form's LoadComplete method.

public void Load ()
{
    new AsyncListLoad<Company>().BeginLoad(LoadCompany, Form.LoadCompleted);
}

The LoadCompany() method simply makes use of the Repository to find a know company.

public Company LoadCompany()
{
    return ActiveRecordRepository<Company>.Find(Setup.company.Identifier);
}

The rest of the example is rather generic, it has two domain classes which inherit from a base class, a setup file to instert some data and the repository to provide the ActiveRecordMediator abilities.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

微暖i 2024-07-12 22:44:21

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

理查德

I solved a problem very similar to this where I copied the data out of a lot of older web service contracts into WCF data contracts. I created a number of methods that had signatures like this:

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

The first time this method (or any of the other overloads) executes for two types, it looks at the properties of each type, and decides which ones exist in both based on name and type. It takes this 'member intersection' and uses the DynamicMethod class to emil the IL to copy the source type to the target type, then it caches the resulting delegate in a threadsafe static dictionary.

Once the delegate is created, it's obscenely fast and I have provided other overloads to pass in a delegate to copy over properties that don't match the intersection criteria:

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

... so you could do this for your Person to PersonDTO example:

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

And any properties on both Person and PersonDTO (again, that have the same name and type) would be copied by a runtime emitted method and any subsequent calls would not have to be emitted, but would reuse the same emitted code for those types in that order (i.e. copying PersonDTO to Person would also incur a hit to emit the code).

It's too much code to post, but if you are interested I will make the effort to upload a sample to SkyDrive and post the link here.

Richard

末骤雨初歇 2024-07-12 22:44:21

您应该自动映射我在此处发布的博客:

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

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

You should automapper that I've blogged about here:

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

As long as the properties are named the same on both your objects automapper will handle it.

久伴你 2024-07-12 22:44:21

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

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

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

下载包含大量示例

use ValueInjecter, with it you can map anything to anything e.g.

  • object <-> object
  • object <-> Form/WebForm
  • DataReader -> object

and it has cool features like: flattening and unflattening

the download contains lots of samples

入怼 2024-07-12 22:44:21

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

My apologies for not really putting the details in here, but a basic OO approach would be to make the DTO a member of the ActiveRecord class and have the ActiveRecord delegate the accessors and mutators to the DTO. You could use code generation or refactoring tools to build the DTO classes pretty quickly from the AcitveRecord classes.

燃情 2024-07-12 22:44:21

事实上我现在完全困惑了。
因为你是说:“因此,我们正在寻找一种方法来轻松地从域对象(知道数据库存在并关心)转换(自动思考)到 DTO 对象(对数据库一无所知,也不关心会话) 、映射属性或所有 ORM)。”

  1. 域对象了解并关心数据库吗? 这难道不是域对象的全部意义在于仅包含业务逻辑并且完全不了解 DB 和 ORM?...您必须拥有这些对象吗? 如果它们包含所有这些内容,您只需要修复它们...这就是为什么我有点困惑 DTO 是如何进入图片的

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

Actually I got totally confussed now.
Because you are saying: "We are therefore looking for a way to easily convert (think automagically) from the Domain objects (who know that a DB exists and care) to the DTO object (who know nothing about the DB and care not for sessions, mapping attributes or all thing ORM)."

  1. Domain objects know and care about DB? Isn't that the whole point of domain objects to contain business logic ONLY and be totally unaware of DB and ORM?....You HAVE to have these objects? You just need to FIX them if they contain all that stuff...that's why I am a bit confused how DTO's come into picture

  2. Could you provide more details on what problems you're facing with lazy loading?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文