Nhibernate一对多会导致插入问题

时间:2015-07-16 14:28:34

标签: c# nhibernate nhibernate-mapping

我正在尝试添加新的交叉引用表以将帐户与软件包相关联,并在我尝试保存帐户时遇到一个奇怪的问题。

此交叉引用表具有自己的属性,因此我使用它们的集合而不是包。

当我在帐户上调用SaveOrUpdate时,NHibernate显然决定尝试使用null对象填充其他帐户属性(如Product)。我已经确认注释掉新映射不再导致Product setter受到攻击。 (它也会击中其他多对一的映射对象,对于Product来说没什么特别的,这只是一个例子)。

同样重要的是要注意,这仅在插入时发生。在更新和插入时,我们将从某些表单输入设置ProductFk。在更新Account.Product已通过从数据库中检索帐户设置。在插入时,会创建一个新帐户,其中设置了ProductFk但不是Product。我怀疑这是关于Product为null的原因导致NHibernate在插入之前尝试填充它,但是不明白为什么添加这个新映射会触发它。

这是我的帐户映射文件:

 <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>

和我的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>

所以当我在插入

上执行此操作时
NHibernateSession.SaveOrUpdate(entity);

我在Account中的setter中看到了断点,其值为null(当然,这会失败。)

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

但只有当我包含新的映射时。所以,这是关于尝试添加这个我真正不理解的新系列的东西。

当我构建一个新帐户时,我可能会在ProductFk之外添加设置产品,但这是一种解决方法,并不能解释为什么添加这种新关系会导致系统对产品采取不同的行为(等等)账户的财产。

1 个答案:

答案 0 :(得分:0)

ORM世界中的重点是(好吧,也许只是NHibernate)

  • ValueType (string including)的分配按原样处理。当的原始来源发生变化时,不会再次重新检查(重新评估,重新分配)
  • ReferenceType的分配是不同的。分配给属性的引用Product表示C#引用。此时,Product_ID 已解决且已分配。因此,NHibernate可以根据需要等待ISession.Flush()并正确命令所有INSERT语句。
    • 首先插入产品......
    • 检索到Product_ID ...
    • 并且可以用于根实体的INSERT - 作为Product_ID

要使所有工作正常,我们只需要更改此映射

// 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" />

是关于由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" />

另外,因为有一些神奇的 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;
    }
}