简体   繁体   English

Nhibernate一对多导致插入问题

[英]Nhibernate one-to-many causes insert problems

I'm trying to add new cross-reference tables to relate Accounts to Packages, and coming across a strange problem when I try to save the Account. 我正在尝试添加新的交叉引用表以将Accounts与Packages相关联,并且在尝试保存Account时遇到了一个奇怪的问题。

This cross-reference table has attributes of its own, so that's why I used a collection of them instead of Packages. 此交叉引用表具有其自己的属性,因此这就是为什么我使用它们的集合而不是Packages的原因。

What happens is when I call SaveOrUpdate on the Account, NHibernate apparently decides to try to fill in other Account properties (like Product) with null objects. 发生的是,当我在该帐户上调用SaveOrUpdate时,NHibernate显然决定尝试使用空对象填充其他帐户属性(如“产品”)。 I've verified that commenting out the new mapping no longer causes the Product setter to get hit. 我已经验证了,注释掉新映射不再导致Product setter受到攻击。 (It also hits the other many-to-one mapped objects, it's nothing special about Product, that's just one example). (它还会碰到其他多对一的映射对象,Product没什么特别的,这只是一个示例)。

It's also important to note that this happens on insert only. 同样重要的是要注意,这仅在插入时发生。 On both update and insert, we'll set the ProductFk from some form input. 在更新和插入上,我们将从某些表单输入中设置ProductFk。 On update Account.Product is already set by retrieving the Account from the database. 在更新时,已经通过从数据库检索Account来设置Account.Product。 On insert, a new Account is created where ProductFk is set but not Product. 插入时,将创建一个新帐户,其中设置了ProductFk,但未设置Product。 I suspect it's something about the Product being null causes NHibernate to try to fill it in before insert, but don't understand why adding this new mapping triggers that. 我怀疑这是因为Product为null会导致NHibernate在插入之前尝试填充它,但是不明白为什么添加此新映射会触发该事件。

Here's my Account mapping file: 这是我的帐户映射文件:

 <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="kpa.mko.dal" namespace="kpa.mko.dal.Entities.Accounts">
<class name="Account" table="Accounts">
    <id name="AccountId" type="Int32" unsaved-value="0">
        <generator class="identity" />
    </id>
    <property name="ParentAccountFk" />
    <many-to-one name="ParentAccount" column="ParentAccountFk" insert="false" update="false" />

    <property name="ProductFk" not-null="true" />
    <many-to-one name="Product" column="ProductFk" insert="false" update="false" />
    <property name="DistrictFk" not-null="true" />
    <many-to-one name="District" column="DistrictFk" insert="false" update="false" />
    <property name="Address1" />
    <snipped out other plain properties like Address1 />
    <set name="AccountPackageXrefs" table="Account_Package_xref" inverse="true">
        <key column="AccountFk"/>
        <one-to-many class="Account_Package_xref" />
    </set>
    <sql-insert>
        exec up_gen_Account_Insert @ParentAccountFk = ?, @ExternalAccountId = ?, @ClientAccountNumber = ?, @AccountName = ?
        ,@LogoFk = ?, @AccountTypeFk = ?, @AccountStatusFk = ?, @IsBillableAccount = ?, @ProductFk = ?, @HotLinkAccount = ?
        ,@FormGroupFk = ?, @DistrictFk = ?, @PrimaryEngineerFk = ?, @SecondaryEngineerFk = ?
        ,@Address1 = ?,@Address2 = ?,@City = ?,@StateFk = ?,@Zip = ?,@Country = ?,@Phone = ?,@Fax = ?,@TollFreeNumber = ?
        ,@BillingAddress1 = ?,@BillingAddress2 = ?,@BillingCity = ?,@BillingStateFk = ?,@BillingZip = ?,@BillingCountry = ?
        ,@BillingContactName = ?,@BillingContactPhone = ?,@BillingContactEmail = ?
        ,@TermDate = ?,@VisitValue = ?, @AccountIndustryTypeFk = ?, @CreatedByFk = ?,@CreatedDate = ?,@ModifiedByFk = ?,@ModifiedDate = ?
    </sql-insert>
</class>

and my Account_Package_xref mapping: 和我的Account_Package_xref映射:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="kpa.mko.dal" namespace="kpa.mko.dal.Entities.Accounts">
<class name="Account_Package_xref" table="Account_Package_xref">
    <id name="AccountPackageId" type="Int32" unsaved-value="0">
        <generator class="identity" />
    </id>
    <property name="AccountFk"/>
    <property name="PackageFk"/>
    <property name="PrimaryConsultantFk"/>
    <property name="SecondaryConsultantFk" />
    <property name="CreatedById" column="CreatedByFk" not-null="true" update="false" />
    <property name="CreatedDate" not-null="true" update="false" />
    <property name="ModifiedById" column="ModifiedByFk" not-null="true" />
    <property name="ModifiedDate" not-null="true" />

    <many-to-one name="Account" column="AccountFk" not-null="true" insert="false" update="false"/>
    <many-to-one name="Package" column="PackageFk" not-null="true" insert="false" update="false"/>
</class>
<sql-query name="Account_Pacakge_xref_GetForAccountAndPackage">
    <return class="Account_Package_xref"/>
    exec Account_Package_xref_GetForAccountAndPackage @accountId = :accountId, @packageId = :packageId
</sql-query>

So when I execute this on insert 所以当我在插入时执行

NHibernateSession.SaveOrUpdate(entity);

I see breakpoints hit in the setter here in Account, with a null value (which, of course, fails.) 我在Account中的setter中看到了一个断点,它带有一个空值(这当然会失败)。

public virtual ServicesProduct Product
{
    get { return _product; }
    set { 
        _product = value;
        ProductFk = value.ProductId;
    }
}

But only when I have the new mapping included. 但是只有当我包含新的映射时。 So, it's something about trying to add this new collection which I really don't understand. 因此,这是关于尝试添加这个我真的不理解的新收藏集的事情。

I could probably add setting the Product in addition to the ProductFk when I construct a new Account, but that's a workaround and doesn't explain why adding this new relationship causes the system to act differently with respect to the Product (and like properties) of Account. 构造新帐户时,除了ProductFk之外,我可能还可以添加对Product的设置,但这是一种变通方法,并且不能解释为什么添加此新关系会导致系统相对于产品的(和类似属性)行为有所不同帐户。

The point in ORM world is (ok, maybe just NHibernate) that ORM世界中的重点是(好吧,也许只是NHibernate)

  • assignment of ValueType (string including) is treated AS IS. 按原样对待ValueType (string including)分配。 It is not later re-checked (re-evaluated, re-assigned) again, when the original source of that value has changed 当该的原始来源已更改时,以后不会再次对其进行重新检查(重新评估,重新分配)
  • assigning of ReferenceType is different. ReferenceType分配不同。 Reference ( Product ) assigned to property represents C# reference. 分配给属性的引用Product表示C#引用。 At that moment the Product_ID was not resolved and was not assigned. 当时,Product_ID 尚未解析且未分配。 So, NHibernate could wait for the ISession.Flush() and properly order all INSERT statements - as required. 因此,NHibernate可以等待ISession.Flush()并根据需要正确地排序所有INSERT语句。
    • Firstly Product is inserted... 首先插入产品...
    • its Product_ID is retrieved... 检索到其Product_ID ...
    • and could be used for INSERT of the root entity - as the Product_ID value 并且可以用于根实体的Product_ID作为Product_ID

To make that all working we just have to change this mapping 为了使所有工作正常,我们只需要更改此映射

// not the right way with ORM
<property name="ProductFk" not-null="true" />
<many-to-one name="Product" column="ProductFk" insert="false" update="false" />
<property name="DistrictFk" not-null="true" />
<many-to-one name="District" column="DistrictFk" insert="false" update="false" />

which is about ValueType being managed by NHiberante, into Reference management: 关于由NHiberante管理的ValueType,进入参考管理:

// object relations are mapped. That is ORM
<property name="ProductFk" not-null="true" insert="false" update="false" />
<many-to-one name="Product" column="ProductFk" />
<property name="DistrictFk" not-null="true" insert="false" update="false" />
<many-to-one name="District" column="DistrictFk" />

Also, because there are some magic sql-insert with some store procedures (are you still forced by some DB guy to do that?, no way how to avoid it?) , I would suggest to adjust the entity mapping like this 另外,由于在存储过程中有一些神奇的sql-insert (您是否仍被数据库专家强迫这样做?无法避免这种情况?) ,我建议像这样调整实体映射

// fields
Product _product;

// properties
public virtual int ProductFk { get; set; }
public virtual Product Product
{
    get { return _product; }
    set
    {
        _product = value;
        ProductFk = _product != null ? _product.ProductId : 0;
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM