通过 http/tcp 传输发送 ORM 模型类
这是一个关于当必须通过 http 和 tcp 传输在应用程序之间传递 ORM 模型时如何处理 ORM 模型的一般问题,但具体来说,我通过 Castle Active Record 使用 NHibernate,如果它有什么区别的话。传输中的数据格式可以是 JSON,也可以是 XML。
通过传输传递 ORM 模型时,我遇到了三个问题:
XML/JSON 序列化时出现循环引用错误。这些错误可以通过在关系属性上使用 ScriptIgnore 和 XmlIgnore 属性来解决,但这不是一个很好的解决方案,因为有时您想要包含被忽略的相关对象,并且如果删除忽略属性,则可能会在其他地方导致循环引用错误。
序列化时延迟加载。假设您有类 A,它与类 B 具有 1:1 关系。您序列化类 A,它将尝试调用数据库来获取并序列化类 B。有时您不再处于允许延迟加载的正确会话中在序列化时会抛出错误。这也可以通过忽略属性来解决,但出于同样的原因,这不是一个好的解决方案。
ORM 模型的基类不能序列化为 XML,但是是密封的。我不知道这个问题有什么解决方案,但我已经遇到过很多次了。
解决上述问题的一种方法是根本不跨传输方式来回传递 ORM 模型,我听很多人说这是推荐的解决方案。但是,如何以干净的方式做到这一点呢?假设您有 ModelA,您希望使用某种传输方式将其从一个应用程序传递到另一个应用程序。
您创建一个 ModelAView 类,它具有 ModelA 的所有属性,但没有所有 ORM 属性、基类和延迟加载功能。解决方案已经不干净了,因为您现在有 2 个具有相同重复属性的类。当您想要更改模型时,您必须在两个类中更改它。
您从数据库中获取一个 ModelA 实例,并且必须逐个字段填充 ModelAView 的属性。也许您有一个 ModelAView 的构造函数,它传入一个 ModelA 实例,并在其中设置属性,这样它就更干净了。但是,您仍然必须逐个属性设置值属性,这可能很麻烦。您可以尝试执行某种克隆功能,但是如果它有子对象,那么您需要深度克隆,并且似乎没有一个好的方法可以做到这一点,特别是因为 View 类不一定拥有所有子对象具有与 ModelA 类相同的属性。
您通过传输将 ModelAView 传递到另一个应用程序。
另一个应用程序希望可以使用 ModelAView 来完成它需要做的任何事情。但是,如果它需要更新 ModelA 的属性并将它们保存到数据库,那么它需要执行相反的操作,将所有属性从 ModelAView 复制到 ModelA,然后保存到数据库。
因此,我在这里写了很多内容,但我希望我已经明确了在这些情况下处理 ORM 模型时遇到的挑战。有没有人可以分享一些经过尝试的真实方法来处理这些问题的经验,这些方法不需要代码复制和/或将值从一个对象逐个属性复制到另一个对象?
This is a general question about how you deal with ORM models when having to pass them between applications via http and tcp transports, however specifically I'm using NHibernate via Castle Active Record, if it makes any difference. The format of the data over the transport could be JSON or it could be XML.
There are three issues that I run into when passing ORM models over transports:
Circular reference errors upon XML/JSON serialization. These errors can be resolved by using ScriptIgnore and XmlIgnore attributes on relationship properties, however it's not a great solution because sometimes you want to include relationed objects that are ignored, and you could cause circular ref errors elsewhere if you remove the ignore attributes.
Lazy loading upon serialization. Let's say you have class A which has a 1:1 relation to class B. You serialize class A, and it will try to call the db to get and serialize class B. Sometimes you are no longer in a proper session that allows lazy loading at the point of serializing and that throws errors. This can be resolved with the ignore attributes too, but isn't a good solution for the same reason.
Base classes of ORM models aren't serializable to XML but are sealed. I don't know of any solution to this one, but I've run into it many times.
One solution to the issues above is to not pass ORM models back and forth across transports at all, and I've heard many say that is the recommended solution. However, how do you then do this in a clean manner? Let's say you have ModelA, which you want to pass from one app to another using some sort of transport.
You create a ModelAView class, which has all of the properties of ModelA except without all of the ORM attributes, base classes, and lazy loading features. Already the solution isn't clean, as you now have 2 classes with the same duplicated properties. When you want to make a change to the model, you will have to change it in both classes.
You grab a ModelA instance from the database and have to field-by-field populate the properties of the ModelAView. Maybe you have a constructor for ModelAView which passes in a ModelA instance, and sets the properties in there so it's a little cleaner. However you're still having to set the values property by property, which can be cumbersome. You can try to do some sort of clone functionality, however if it has child objects then you need to deep clone, and it doesn't seem like there's a good way to do this, especially since the View class won't necessarily have all of the same properties that the ModelA class does.
You pass ModelAView over the transport to the other app.
The other app hopefully can just use ModelAView for whatever it needs to do. However if it needs to update properties of ModelA and save them to the db, then it would need to then do the opposite, copy all the properties from ModelAView to ModelA and then save to the db.
So I've written a lot here, but I hope I've made clear the challenges I'm having with dealing with ORM models in these situations. Does anyone have any experiences they can share on tried and true methods to deal with these issues that don't require code duplication and/or property-by-property copying of values from one object to another?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我想你运气不好。我遇到了同样的问题(很多人都遇到过),并且共识似乎是您应该使用 DTO(数据传输对象)。 AutoMapper 让它变得更容易一些。
如果您使用 NH 的结果转换器将结果作为 DTO 返回,您可能会省去从实体转换为 DTO 的麻烦。
我们最终为每个实体创建了几个不同的 DTO,每个 DTO 包含不同的子项或集合,例如 Customer、CustomerWithOrders 等。需要 DTO 的客户端必须提前知道它需要什么,以便可以请求正确的类型。
不过,维护它并不算太糟糕,因为我们的实体没有太大变化。
I think you're out of luck. I ran into the same issue (lots of people have) and the consensus seems to be that you should use DTO's (Data Transfer Objects). AutoMapper makes it a little easier.
You might save yourself the hassle of converting from Entities to DTO's if you use NH's result transformers to return your results as DTO's.
We ended up creating a couple different DTO's per entity, each one containing different children or collections, e.g. Customer, CustomerWithOrders, etc. The client that needs a DTO has to know what it needs ahead of time so it can request the right kind.
Maintaining it hasn't been too bad though, since our entities don't change much.