C# 使用 NHibernate 映射一对多的最小且正确的方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29985158/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 04:26:04  来源:igfitidea点击:

Minimal and correct way to map one-to-many with NHibernate

c#nhibernateormnhibernate-mapping

提问by markvgti

I am new to NHibernate and C#, so please be gentle!

我是 NHibernate 和 C# 的新手,所以请保持温和!

I have the following two NHibernate entities:

我有以下两个 NHibernate 实体:

Employee
{
    private long _id;
    private String _name;
    private String _empNumber;
    private IList<Address> _addresses;

    //Properties...
}

and

Address
{
    private long _id;
    private String _addrLine1;
    private String _addrLine2;
    private String _city;
    private String _country;
    private String _postalCode;

    //Properties
}

and they have a one-to-manyrelationship from Employeeto Address(each employee can have multiple addresses in their record). Conveniently ignoring the fact that more than one employee may reside at the same address.

并且他们有one-to-manyEmployee到的关系Address(每个员工的记录中可以有多个地址)。方便地忽略多个员工可能居住在同一地址的事实。

I understand this from the perspective of objects in memory (the NHibernate entities). What I am struggling with are the mapping files (and I am taking an easy example here). This is what I have come up with so far:

我是从内存中的对象(NHibernate 实体)的角度理解这一点的。我正在努力解决的是映射文件(我在这里举了一个简单的例子)。这是我到目前为止想出的:

// Intentionally left out XML and <hibernate-mapping> 
// Mappings for class 'Employee'. -->
<class name="Employee" table="Employees">
    <id name="ID">
        <generator class="native">
    </id>

    <property name="Name" />
    <property name="EmpNumber" />

    <bag name="Addresses">
        <key column="AddressId" />
        <one-to-many class="Address" />
    </bag>
</class>

and

// Intentionally left out XML and <hibernate-mapping> .
// Mappings for class 'Address'
<class name="Address" table="Addresses">
    <id name="ID">
        <generator class="native">
    </id>

    // Intentionally left out name="Employee" 
    // as I don't have corresponding field in Address entity.
    <many-to-one class="Employee" column="EmployeeID" cascade="all" />

    <property name="AddrLine1" />
    <property name="AddrLine2" />
    <property name="City" />
    <property name="Country" />
    <property name="PostalCode" />
</class>
  1. Is this correct?
  2. If not, it seems like what I am missing here is a field in the Addressentity that is a reference to the corresponding Employeeentity. But if so, then I can't understand why this is required: I don't need to fetch an Addressfrom an Employee, only the other way round...
  1. 这样对吗?
  2. 如果没有,似乎我在这里缺少的是Address实体中的一个字段,它是对相应Employee实体的引用。但如果是这样,那么我无法理解为什么需要这样做:我不需要Address从 an 中获取an Employee,只需要反过来 ......

采纳答案by Radim K?hler

Just few hints, summarizing the most suitable standards I found out when working with NHibernate.

只是一些提示,总结了我在使用 NHibernate 时发现的最合适的标准。

1) If there is a bi-directionalreference in persitence (DB column), express it in C#code bi-directionalas well.

1)如果持久性(DB列)中双向引用,也用代码双向表达。C#

Other words, if a childhas reference to parent, parentshould have reference to child.

换句话说,如果孩子有对parent 的引用,parent应该有对child 的引用。

public class Employee
{
    ...
    public virtual IList<Address> { get; set; }
}
public class Address
{
    ...
    public virtual Employee Employee { get; set; }
}

This represents Business Domain as is. Address belongs to Employee and Employee belongs to Address.

这代表业务领域。地址属于雇员,雇员属于地址。

If we for some reasons really want to restrict that, we should rather protectedmodifier, but still keep the reference in C#

如果我们出于某些原因真的想限制它,我们应该protected修改,但仍将引用保留在C#

2) Use inverse="true". This could be used only if we mapped both sides (as above), and will lead to more "expected and optimized" INSERT and UPDATE scritps

2)使用inverse="true"。这只能在我们映射双方(如上)时使用,并且会导致更多“预期和优化”的 INSERT 和 UPDATE 脚本

Read more here:

在此处阅读更多信息:

inverse = “true” example and explanationby mkyong

inverse = “true” 示例和解释由 mkyong

3) Use batch fetching mapping almost everwhere. That will avoid 1 + N issues during querying. Read more:

3) 几乎在任何地方都使用批量获取映射。这将在查询期间避免 1 + N 问题。阅读更多:

few details about batch fetching

关于批量获取的一些细节

4) In case, that one object (in our case Employee)is root(the other does not make so much sense without it)- use cascading. Read more:

4)如果一个对象(in our case Employee)root(没有它另一个没有那么多意义)- 使用级联。阅读更多:

nhibernate - Create child by updating parent, or create explicitly?

nhibernate - 通过更新父级来创建子级,还是显式创建?

Rules 2,3,4 in a mapping snippets:

映射片段中的规则 2,3,4:

<class name="Employee" ... batch-size="25">
  ...
  <bag name="Addresses"
       lazy="true" 
       inverse="true" 
       batch-size="25" 
       cascade="all-delete-orphan" >
    // wrong! This columns is the same as for many-to-one
    //<key column="AddressId" />
    // it is the one column expressing the relation
    <key column="EmployeeId" />
    <one-to-many class="Address" />
  </bag>

<class name="Address" ... batch-size="25">
  ...
  <many-to-one not-null="true" name="Employee" column="EmployeeID" />

3) if we use inverse="truedo not forget to assign both sides of relation (mostly critical during creation)

3)如果我们使用inverse="true不要忘记分配关系的双方(在创建期间主要是关键)

The reason is:

原因是:

we instruct NHibernate - the other side (Address)is responsible for persisting relation. But to do that properly, that Addressneeds to have reference to Employee- to be able to persist its ID into its column in Address table.

我们指示 NHibernate - 另一方( Address)负责持久化关系。但是要正确地做到这一点,Address需要引用Employee- 以便能够将其 ID 保存到地址表中的列中。

So this should be the standard code to create new Address

所以这应该是创建新地址的标准代码

Employee employee = ... // load or create new
Address address = new Address 
{
    ...
    Employee = employee, // this is important
};
Employee.Addresses.Add(address);
session.SaveOrUpdate(employee);  // cascade will trigger the rest

We can also introduce some method like AddAddress()which will hide this complexity, but setting both sides is a good prectice.

我们也可以引入一些方法AddAddress()来隐藏这种复杂性,但设置两边是一个很好的方法。

回答by Najera

You should add the cascade all-delete-orphanin the one-to-manyrelation, if you delete the Employeethe address will be deleted too.

您应该all-delete-orphanone-to-many关系中添加级联,如果删除Employee该地址也将被删除。

If you don't need Employeereference create a inverse=falserelation like this: here

如果您不需要Employee参考创建这样的inverse=false关系:here