更新包异常
Updating a bag exception
我有这样的数据库表:
[tblRecord]
RecordId MyColumn1 MyColumn2
----------------------------------
112 somedata8 somedata7
112 somedata6 somedata1
148 somedata3 somedata5
[tblRecordFruit]
RecordId FruitTypeId
-------------------------
112 53
112 85
148 16
[tblFruitType]
FruitTypeId Text
----------------------
53 Apple
85 Banana
16 Orange
和相应的 NHibernate 映射:
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="Record, Infrastructure.Interface"
table="tblRecord">
<id name="Id" type="Int32" unsaved-value="null">
<column name="RecordId" length="4" sql-type="int" not-null="true" unique="true" index="PK_tblRecord" />
<generator class="native" />
</id>
...
<bag name="Fruits" table="tblRecordFruit" inverse="false" lazy="true" cascade="save-update">
<key>
<column name="RecordId" length="4" sql-type="int" not-null="true" />
</key>
<many-to-many
class="FruitType, Infrastructure.Interface">
<column name="FruitTypeId" length="2" sql-type="smallint" not-null="true" />
</many-to-many>
</bag>
...
</class>
</hibernate-mapping>
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="FruitType, Infrastructure.Interface"
table="tblFruitType"
mutable="false"
lazy="false">
<id name="Id" type="Int16" unsaved-value="0">
<column name="FruitTypeID" sql-type="smallint" not-null="true" unique="true" index="PK_Fruit" />
<generator class="native" />
</id>
<property name="Text" type="String">
<column name="Text" length="255" sql-type="varchar" not-null="true" />
</property>
</class>
</hibernate-mapping>
和 C# 代码使用 NHiberate 获取和 Save
/SaveOrUpdate
/Merge
数据:
public Interface.SortableBindingList<T> GetByCriteria (NHibernate.ICriteria criteria)
{
System.Collections.Generic.IList<T> myList = criteria.List<T>();
SortableBindingList<T> mySortableList = ConvertToGenericBindingList(myList);
return mySortableList;
}
using (NHibernate.ITransaction tx = this.session.BeginTransaction())
{
// this.session.Save (this.Record);
// this.session.SaveOrUpdate (this.Record);
this.session.Merge(this.Record);
tx.Commit(); // Exception here
}
因此用户创建了一条记录(或打开一条没有 FruitTypes
的记录)并添加了一些水果类型,然后保存。一切都很好。
然后用户再次打开该记录并向其添加另一个 FruitType
。
Record
class 定义了一个集合:
public virtual System.Collections.Generic.IList<FruitType> Fruits
然后像这样添加一个水果:
Record.Fruits.Add(fruitType);
保存时抛出异常:
Violation of PRIMARY KEY constraint 'PK_tblRecordFruit_1'.
Cannot insert duplicate key in object 'dbo.tblRecordFruit'. The duplicate key value is (112, 53).
似乎是将另一行 (112,53)
插入 tblRecordFruit
而不是更新它。 MyColumn1
或 MyColumn2
中 tblRecord
中的任何数据都可以正常更新。
正如您在 C# 代码中看到的那样,我已经尝试了 .Save()
、.SaveOrUpdate()
和 .Merge()
,并且都导致了相同的异常。
NHibernate 版本 4.0.0.4000
我该怎么做?
如果我理解正确的话,您正在尝试更新 Record
object 与 session 断开连接。因此,您将 Merge
用于 session re-connection.
在这种情况下,要使 Merge
为您的 collection 工作,他们需要 merge
映射中的级联选项。因此,请尝试更新您的 Fruits
映射:
cascade="merge,save-update"
或简单地使用 cascade="all"
来涵盖所有情况:
<bag name="Fruits" table="tblRecordFruit" inverse="false" lazy="true" cascade="all">
P.S。 SaveOrUpdate
调用实际上应该与您现有的 Fruits
映射 cascade="save-update"
一起正常工作。也许您在更新映射之前已经尝试过,否则请确保异常相同并提供 Fruit
.
的映射
更新 1 如前所述,我假设您正在尝试更新分离的实体(从不同 session 加载的实体)。请确认并提供详细信息,究竟是如何发生的? UI 上显示的状态是 serialized/de-serialized 吗?
在这种情况下,您应该知道在保存 object 之后需要序列化状态。因为在保存 Fruits
collection 类型更改为内部 NHibernate collection 之后,正确处理进一步的更新是必需的。因此,当为现有的 Record
object record.Fruits
调用 record.Fruits.Add(obj)
时,它不能是 List<T>
类型,它应该是某种 NHibernate 类型(据说是 PersistangGenericType<T>
) .
所以请确保您没有覆盖现有 object 的 collection 属性:
record.Fruits = new List<FruitType>();// WRONG for existing record...
//Instead clear existing record collection:
record.Fruits.Clear(); //Correct
同时在调试器中进行以下检查:
//You somehow obtain record instance that you want to update
var record = DeserializeOrLoadState(recordId);
...//When you update existing record
record.Fruits.Add(fruitType1);// Make sure in debugger that for existing record record.Fruits is NHibernate type PesistentGenericBag<T> and not List<T>
...//When you SaveOrUpdate/Merge existing record
session.Merge(record);// <- make sure in debugger that for existing record.Fruits is NHibernate type PesistentGenericBag<T> and not List<T>
...
SerializeState(record);// serialize state only after session.SaveOrUpdate
更新 2
It is not a PersistentGenericBag. This used to work. Is it possible that a change from NHibernate v2 to v4 now causes this problem?
我不知道它是如何在 NHibernate v2 中完成的 - 但肯定可以在您的代码中修复它。所以你需要调查为什么它仍然是 List
- 这表明你做错了什么。
一些建议:
1) 如果您的 Fruits
属性 有 setter - 删除它以确保所有操作都是通过 IList
方法完成的:
public class Record
{
public virtual IList<FruitType> Fruits {get; } = new List<FruitType>()
}
2) 调查这部分Then the user opens that record again and adds another FruitType to it.
。你还没有说它是如何实现的。 UI <-> 实体映射在这里是如何实现的?当从数据库加载现有实体时,我很确定它不是 List
并且属于 NHibernate 类型。所以你应该调试并找到它更改为 List
.
的地方
3) 同时尝试将级联设置更改为 cascade="all-delete-orphan"
。我认为这不会改变任何东西 - 但以防万一。
异常:
Violation of PRIMARY KEY constraint 'PK_tblRecordFruit_1'.
Cannot insert duplicate key in object 'dbo.tblRecordFruit'. The duplicate key value is (112, 53).
表示违反了主键约束。这意味着您正试图复制主键列的值。
查看映射中的以下代码块:
<bag name="Fruits" table="tblRecordFruit" inverse="false" lazy="true" cascade="save-update">
<key>
<column name="RecordId" length="4" sql-type="int" not-null="true" />
</key>
RecordId
似乎是您在 tblRecordFruit
table 上的主键。如果是这种情况,很明显你不能将你在问题中提到的示例数据插入 table:
RecordId FruitTypeId
-----------------------
112 53
112 85
148 16
值 112
不能在列 RecordId
中重复。如果您的代码试图这样做,很明显您会得到您所说的异常。
您是否正在寻找 composite key(在 RecordId
和 FruitTypeId
列)?
First, a caveat: composite keys are certainly mappable in NHibernate, but it's a little trickier than a typical single identity key would be. Compared to a normal key, there's some extra setup work, queries are more painful, and they tend to be less optimized in terms of lazy loading. Because of these things, experienced NHibernate users often avoid composite keys entirely when possible.
....
....
<composite-id>
<key-many-to-one class="SuperShop.Domain.OrderItemComponent,SuperShop.Domain" name="OrderItemComponent" column="OrderItemProductID" />
<key-property name="DetailType" column="DetailTypeID" type="SuperShop.Domain.DetailTypes,SuperShop.Domain" />
</composite-id>
<version name="LastModifiedOn"....
我有这样的数据库表:
[tblRecord]
RecordId MyColumn1 MyColumn2
----------------------------------
112 somedata8 somedata7
112 somedata6 somedata1
148 somedata3 somedata5
[tblRecordFruit]
RecordId FruitTypeId
-------------------------
112 53
112 85
148 16
[tblFruitType]
FruitTypeId Text
----------------------
53 Apple
85 Banana
16 Orange
和相应的 NHibernate 映射:
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="Record, Infrastructure.Interface"
table="tblRecord">
<id name="Id" type="Int32" unsaved-value="null">
<column name="RecordId" length="4" sql-type="int" not-null="true" unique="true" index="PK_tblRecord" />
<generator class="native" />
</id>
...
<bag name="Fruits" table="tblRecordFruit" inverse="false" lazy="true" cascade="save-update">
<key>
<column name="RecordId" length="4" sql-type="int" not-null="true" />
</key>
<many-to-many
class="FruitType, Infrastructure.Interface">
<column name="FruitTypeId" length="2" sql-type="smallint" not-null="true" />
</many-to-many>
</bag>
...
</class>
</hibernate-mapping>
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="FruitType, Infrastructure.Interface"
table="tblFruitType"
mutable="false"
lazy="false">
<id name="Id" type="Int16" unsaved-value="0">
<column name="FruitTypeID" sql-type="smallint" not-null="true" unique="true" index="PK_Fruit" />
<generator class="native" />
</id>
<property name="Text" type="String">
<column name="Text" length="255" sql-type="varchar" not-null="true" />
</property>
</class>
</hibernate-mapping>
和 C# 代码使用 NHiberate 获取和 Save
/SaveOrUpdate
/Merge
数据:
public Interface.SortableBindingList<T> GetByCriteria (NHibernate.ICriteria criteria)
{
System.Collections.Generic.IList<T> myList = criteria.List<T>();
SortableBindingList<T> mySortableList = ConvertToGenericBindingList(myList);
return mySortableList;
}
using (NHibernate.ITransaction tx = this.session.BeginTransaction())
{
// this.session.Save (this.Record);
// this.session.SaveOrUpdate (this.Record);
this.session.Merge(this.Record);
tx.Commit(); // Exception here
}
因此用户创建了一条记录(或打开一条没有 FruitTypes
的记录)并添加了一些水果类型,然后保存。一切都很好。
然后用户再次打开该记录并向其添加另一个 FruitType
。
Record
class 定义了一个集合:
public virtual System.Collections.Generic.IList<FruitType> Fruits
然后像这样添加一个水果:
Record.Fruits.Add(fruitType);
保存时抛出异常:
Violation of PRIMARY KEY constraint 'PK_tblRecordFruit_1'.
Cannot insert duplicate key in object 'dbo.tblRecordFruit'. The duplicate key value is (112, 53).
似乎是将另一行 (112,53)
插入 tblRecordFruit
而不是更新它。 MyColumn1
或 MyColumn2
中 tblRecord
中的任何数据都可以正常更新。
正如您在 C# 代码中看到的那样,我已经尝试了 .Save()
、.SaveOrUpdate()
和 .Merge()
,并且都导致了相同的异常。
NHibernate 版本 4.0.0.4000
我该怎么做?
如果我理解正确的话,您正在尝试更新 Record
object 与 session 断开连接。因此,您将 Merge
用于 session re-connection.
在这种情况下,要使 Merge
为您的 collection 工作,他们需要 merge
映射中的级联选项。因此,请尝试更新您的 Fruits
映射:
cascade="merge,save-update"
或简单地使用 cascade="all"
来涵盖所有情况:
<bag name="Fruits" table="tblRecordFruit" inverse="false" lazy="true" cascade="all">
P.S。 SaveOrUpdate
调用实际上应该与您现有的 Fruits
映射 cascade="save-update"
一起正常工作。也许您在更新映射之前已经尝试过,否则请确保异常相同并提供 Fruit
.
更新 1 如前所述,我假设您正在尝试更新分离的实体(从不同 session 加载的实体)。请确认并提供详细信息,究竟是如何发生的? UI 上显示的状态是 serialized/de-serialized 吗?
在这种情况下,您应该知道在保存 object 之后需要序列化状态。因为在保存 Fruits
collection 类型更改为内部 NHibernate collection 之后,正确处理进一步的更新是必需的。因此,当为现有的 Record
object record.Fruits
调用 record.Fruits.Add(obj)
时,它不能是 List<T>
类型,它应该是某种 NHibernate 类型(据说是 PersistangGenericType<T>
) .
所以请确保您没有覆盖现有 object 的 collection 属性:
record.Fruits = new List<FruitType>();// WRONG for existing record...
//Instead clear existing record collection:
record.Fruits.Clear(); //Correct
同时在调试器中进行以下检查:
//You somehow obtain record instance that you want to update
var record = DeserializeOrLoadState(recordId);
...//When you update existing record
record.Fruits.Add(fruitType1);// Make sure in debugger that for existing record record.Fruits is NHibernate type PesistentGenericBag<T> and not List<T>
...//When you SaveOrUpdate/Merge existing record
session.Merge(record);// <- make sure in debugger that for existing record.Fruits is NHibernate type PesistentGenericBag<T> and not List<T>
...
SerializeState(record);// serialize state only after session.SaveOrUpdate
更新 2
It is not a PersistentGenericBag. This used to work. Is it possible that a change from NHibernate v2 to v4 now causes this problem?
我不知道它是如何在 NHibernate v2 中完成的 - 但肯定可以在您的代码中修复它。所以你需要调查为什么它仍然是 List
- 这表明你做错了什么。
一些建议:
1) 如果您的 Fruits
属性 有 setter - 删除它以确保所有操作都是通过 IList
方法完成的:
public class Record
{
public virtual IList<FruitType> Fruits {get; } = new List<FruitType>()
}
2) 调查这部分Then the user opens that record again and adds another FruitType to it.
。你还没有说它是如何实现的。 UI <-> 实体映射在这里是如何实现的?当从数据库加载现有实体时,我很确定它不是 List
并且属于 NHibernate 类型。所以你应该调试并找到它更改为 List
.
3) 同时尝试将级联设置更改为 cascade="all-delete-orphan"
。我认为这不会改变任何东西 - 但以防万一。
异常:
Violation of PRIMARY KEY constraint 'PK_tblRecordFruit_1'. Cannot insert duplicate key in object 'dbo.tblRecordFruit'. The duplicate key value is (112, 53).
表示违反了主键约束。这意味着您正试图复制主键列的值。
查看映射中的以下代码块:
<bag name="Fruits" table="tblRecordFruit" inverse="false" lazy="true" cascade="save-update">
<key>
<column name="RecordId" length="4" sql-type="int" not-null="true" />
</key>
RecordId
似乎是您在 tblRecordFruit
table 上的主键。如果是这种情况,很明显你不能将你在问题中提到的示例数据插入 table:
RecordId FruitTypeId ----------------------- 112 53 112 85 148 16
值 112
不能在列 RecordId
中重复。如果您的代码试图这样做,很明显您会得到您所说的异常。
您是否正在寻找 composite key(在 RecordId
和 FruitTypeId
列)?
First, a caveat: composite keys are certainly mappable in NHibernate, but it's a little trickier than a typical single identity key would be. Compared to a normal key, there's some extra setup work, queries are more painful, and they tend to be less optimized in terms of lazy loading. Because of these things, experienced NHibernate users often avoid composite keys entirely when possible.
....
....<composite-id> <key-many-to-one class="SuperShop.Domain.OrderItemComponent,SuperShop.Domain" name="OrderItemComponent" column="OrderItemProductID" /> <key-property name="DetailType" column="DetailTypeID" type="SuperShop.Domain.DetailTypes,SuperShop.Domain" /> </composite-id> <version name="LastModifiedOn"....