C# 处理遗留数据库时,在 NHibernate 中建模多对一关系的最佳方法是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10915/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Best way to model Many-To-One Relationships in NHibernate When Dealing With a Legacy DB?
提问by Mark Struzinski
Warning - I am very new to NHibernate. I know this question seems simple - and I'm sure there's a simple answer, but I've been spinning my wheels for some time on this one. I am dealing with a legacy db which really can't be altered structurally. I have a details table which lists payment plans that have been accepted by a customer. Each payment plan has an ID which links back to a reference table to get the plan's terms, conditions, etc. In my object model, I have an AcceptedPlan class, and a Plan class. Originally, I used a many-to-one relationship from the detail table back to the ref table to model this relationship in NHibernate. I also created a one-to-many relationship going in the opposite direction from the Plan class over to the AcceptedPlan class. This was fine while I was simply reading data. I could go to my Plan object, which was a property of my AcceptedPlan class to read the plan's details. My problem arose when I had to start inserting new rows to the details table. From my reading, it seems the only way to create a new child object is to add it to the parent object and then save the session. But I don't want to have to create a new parent Plan object every time I want to create a new detail record. This seems like unnecessary overhead. Does anyone know if I am going about this in the wrong way?
警告 - 我对 NHibernate 很陌生。我知道这个问题看起来很简单 - 我相信有一个简单的答案,但我已经在这个问题上旋转了一段时间。我正在处理一个在结构上确实无法改变的遗留数据库。我有一个详细信息表,其中列出了客户接受的付款计划。每个付款计划都有一个 ID,该 ID 链接回参考表以获取计划的条款、条件等。在我的对象模型中,我有一个 AcceptedPlan 类和一个 Plan 类。最初,我在 NHibernate 中使用了从详细表返回到 ref 表的多对一关系来建模这种关系。我还创建了一个从 Plan 类到 AcceptedPlan 类的相反方向的一对多关系。这很好,而我只是在阅读数据。我可以转到我的 Plan 对象,它是我的 AcceptedPlan 类的一个属性来读取计划的详细信息。当我不得不开始向详细信息表插入新行时,我的问题就出现了。从我的阅读来看,似乎创建新子对象的唯一方法是将其添加到父对象中,然后保存会话。但是我不想每次要创建新的详细记录时都必须创建一个新的父计划对象。这似乎是不必要的开销。有谁知道我是否以错误的方式解决这个问题?我不想每次要创建新的详细记录时都必须创建一个新的父计划对象。这似乎是不必要的开销。有谁知道我是否以错误的方式解决这个问题?我不想每次要创建新的详细记录时都必须创建一个新的父计划对象。这似乎是不必要的开销。有谁知道我是否以错误的方式解决这个问题?
采纳答案by DavidWhitney
I'd steer away from having child object containing their logical parent, it can get very messy and very recursive pretty quickly when you do that. I'd take a look at how you're intending to use the domain model before you do that sort of thing. You can easily still have the ID references in the tables and just leave them unmapped.
我会避免让子对象包含其逻辑父对象,当你这样做时,它会很快变得非常混乱和非常递归。在你做那种事情之前,我会看看你打算如何使用域模型。您仍然可以轻松地在表中保留 ID 引用,而无需映射它们。
Here are two example mappings that might nudge you in the right direction, I've had to adlib table names etc but it could possibly help. I'd probably also suggest mapping the StatusId to an enumeration.
这里有两个示例映射可能会推动您朝着正确的方向前进,我不得不使用 adlib 表名等,但它可能会有所帮助。我可能还建议将 StatusId 映射到枚举。
Pay attention to the way the bag effectivly maps the details table into a collection.
注意包有效地将详细信息表映射到集合中的方式。
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping default-cascade="save-update" xmlns="urn:nhibernate-mapping-2.2">
<class lazy="false" name="Namespace.Customer, Namespace" table="Customer">
<id name="Id" type="Int32" unsaved-value="0">
<column name="CustomerAccountId" length="4" sql-type="int" not-null="true" unique="true" index="CustomerPK"/>
<generator class="native" />
</id>
<bag name="AcceptedOffers" inverse="false" lazy="false" cascade="all-delete-orphan" table="details">
<key column="CustomerAccountId" foreign-key="AcceptedOfferFK"/>
<many-to-many
class="Namespace.AcceptedOffer, Namespace"
column="AcceptedOfferFK"
foreign-key="AcceptedOfferID"
lazy="false"
/>
</bag>
</class>
</hibernate-mapping>
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping default-cascade="save-update" xmlns="urn:nhibernate-mapping-2.2">
<class lazy="false" name="Namespace.AcceptedOffer, Namespace" table="AcceptedOffer">
<id name="Id" type="Int32" unsaved-value="0">
<column name="AcceptedOfferId" length="4" sql-type="int" not-null="true" unique="true" index="AcceptedOfferPK"/>
<generator class="native" />
</id>
<many-to-one
name="Plan"
class="Namespace.Plan, Namespace"
lazy="false"
cascade="save-update"
>
<column name="PlanFK" length="4" sql-type="int" not-null="false"/>
</many-to-one>
<property name="StatusId" type="Int32">
<column name="StatusId" length="4" sql-type="int" not-null="true"/>
</property>
</class>
</hibernate-mapping>
回答by DavidWhitney
The approach I'd take to model this is as follows:
我采用的建模方法如下:
Customer object contains an ICollection <PaymentPlan> PaymentPlans which represent the plans that customer has accepted.
Customer 对象包含一个 ICollection <PaymentPlan> PaymentPlans,表示客户已接受的计划。
The PaymentPlan to the Customer would be mapped using a bag which uses the details table to establish which customer id's mapped to which PaymentPlans. Using cascade all-delete-orphan, if the customer was deleted, both the entries from details and the PaymentPlans that customer owned would be deleted.
将使用包映射到客户的 PaymentPlan,该包使用详细信息表来确定哪个客户 ID 映射到哪个 PaymentPlan。使用级联 all-delete-orphan,如果客户被删除,则来自 details 的条目和客户拥有的 PaymentPlans 都将被删除。
The PaymentPlan object contains a PlanTerms object which represented the terms of the payment plan.
PaymentPlan 对象包含一个表示付款计划条款的 PlanTerms 对象。
The PlanTerms would be mapped to a PaymentPlan using a many-to-one mapping cascading save-update which would just insert a reference to the relevant PlanTerms object in to the PaymentPlan.
PlanTerms 将使用多对一映射级联保存更新映射到 PaymentPlan,这只会将相关 PlanTerms 对象的引用插入到 PaymentPlan 中。
Using this model, you could create PlanTerms independantly and then when you add a new PaymentPlan to a customer, you'd create a new PaymentPlan object passing in the relevant PlanTerms object and then add it to the collection on the relevant Customer. Finally you'd save the Customer and let nhibernate cascade the save operation.
使用此模型,您可以独立创建 PlanTerms,然后当您向客户添加新的 PaymentPlan 时,您将创建一个传入相关 PlanTerms 对象的新 PaymentPlan 对象,然后将其添加到相关客户的集合中。最后,您将保存 Customer 并让 nhibernate 级联保存操作。
You'd end up with a Customer object, a PaymentPlan object and a PlanTerms object with the Customer (customer table) owning instances of PaymentPlans (the details table) which all adhear to specific PlanTerms (the plan table).
您最终会得到一个 Customer 对象、一个 PaymentPlan 对象和一个 PlanTerms 对象,其中 Customer(客户表)拥有 PaymentPlans(详细信息表)的实例,这些实例都遵循特定的 PlanTerms(计划表)。
I've got some more concrete examples of the mapping syntax if required but it's probably best to work it through with your own model and I don't have enough information on the database tables to provide any specific examples.
如果需要,我有一些更具体的映射语法示例,但最好使用您自己的模型来完成它,而且我没有足够的数据库表信息来提供任何特定示例。
回答by lomaxx
I don't know if this is possibly because my NHibernate experience is limited, but could you create a BaseDetail class which has just the properties for the Details as they map directly to the Detail table.
我不知道这是否可能是因为我的 NHibernate 经验有限,但是您能否创建一个 BaseDetail 类,该类仅具有 Details 的属性,因为它们直接映射到 Detail 表。
Then create a second class that inherits from the BaseDetail class that has the additional Parent Plan object so you can create a BaseDetail class when you want to just create a Detail row and assign the PlanId to it, but if you need to populate a full Detail record with the Parent plan object you can use the inherited Detail class.
然后创建一个继承自 BaseDetail 类的第二个类,该类具有附加的 Parent Plan 对象,因此当您只想创建一个 Detail 行并将 PlanId 分配给它时,您可以创建一个 BaseDetail 类,但如果您需要填充一个完整的 Detail记录与父计划对象可以使用继承的 Detail 类。
I don't know if that makes a whole lot of sense, but let me know and I'll clarify further.
我不知道这是否有意义,但请告诉我,我会进一步澄清。
回答by Jon Limjap
I think the problem you have here is that your AcceptedOffer object contains a Plan object, and then your Plan object appears to contain an AcceptedOffers collection that contains AcceptedOffer objects. Same thing with Customers. The fact that the objects are a child of each other is what causes your problem, I think.
我认为您在这里遇到的问题是您的 AcceptedOffer 对象包含一个 Plan 对象,然后您的 Plan 对象似乎包含一个 AcceptedOffers 集合,该集合包含 AcceptedOffer 对象。客户也是一样。我认为,对象是彼此的孩子这一事实是导致您出现问题的原因。
Likewise, what makes your AcceptedOffer complex is it has a two responsibilities: it indicates offers included in a plan, it indicates acceptance by a customer. That violates the Single Responsibility Principle.
同样,AcceptedOffer 的复杂之处在于它有两个职责:它表示计划中包含的报价,它表示客户接受。这违反了单一职责原则。
You may have to differentiate between an Offer that is under a Plan, and an Offer that is accepted by customers. So here's what I'm going to do:
您可能需要区分计划下的优惠和客户接受的优惠。所以这就是我要做的:
- Create a separate Offer object which does not have a state, e.g., it does not have a customer and it does not have a status -- it only has an OfferId and the Plan it belongs to as its attributes.
- Modify your Plan object to have an Offers collection (it does not have to have accepted offer in its context).
- Finally, modify your AcceptedOffer object so that it contains an Offer, the Customer, and a Status. Customer remains the same.
- 创建一个单独的没有状态的 Offer 对象,例如,它没有客户也没有状态——它只有一个 OfferId 和它所属的计划作为它的属性。
- 修改您的 Plan 对象以具有 Offers 集合(它不必在其上下文中接受报价)。
- 最后,修改您的 AcceptedOffer 对象,使其包含报价、客户和状态。客户保持不变。
I think this will sufficiently untangle your NHibernate mappings and object saving problems. :)
我认为这将足以解决您的 NHibernate 映射和对象保存问题。:)
回答by Jon Limjap
A tip that may (or may not) be helpful in NHibernate: you can map your objects against Views as though the View was a table. Just specify the view name as a table name; as long as all NOT NULL fields are included in the view and the mapping it will work fine.
一个在 NHibernate 中可能(也可能没有)有用的提示:您可以将对象映射到视图,就像视图是一个表一样。只需将视图名称指定为表名即可;只要所有 NOT NULL 字段都包含在视图和映射中,它就可以正常工作。
回答by DavidWhitney
Didn't see your database diagram whilst I was writing.
我写的时候没有看到你的数据库图。
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping default-cascade="save-update" xmlns="urn:nhibernate-mapping-2.2">
<class lazy="false" name="Namespace.Customer, Namespace" table="Customer">
<id name="Id" type="Int32" unsaved-value="0">
<column name="customer_id" length="4" sql-type="int" not-null="true" unique="true" index="CustomerPK"/>
<generator class="native" />
</id>
<bag name="AcceptedOffers" inverse="false" lazy="false" cascade="all-delete-orphan">
<key column="accepted_offer_id"/>
<one-to-many class="Namespace.AcceptedOffer, Namespace"/>
</bag>
</class>
</hibernate-mapping>
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping default-cascade="save-update" xmlns="urn:nhibernate-mapping-2.2">
<class lazy="false" name="Namespace.AcceptedOffer, Namespace" table="Accepted_Offer">
<id name="Id" type="Int32" unsaved-value="0">
<column name="accepted_offer_id" length="4" sql-type="int" not-null="true" unique="true" />
<generator class="native" />
</id>
<many-to-one name="Plan" class="Namespace.Plan, Namespace" lazy="false" cascade="save-update">
<column name="plan_id" length="4" sql-type="int" not-null="false"/>
</many-to-one>
</class>
</hibernate-mapping>
Should probably do the trick (I've only done example mappings for the collections, you'll have to add other properties).
应该可以解决问题(我只为集合做过示例映射,您必须添加其他属性)。