NHibernate的; 删除子项删除父项?
删除子项(Employee)时,为什么删除了父项(Store) ?
我用常规Cascade.All配置。
用户输入序列非常简单:
- 从空数据库开始
- 添加父级
- 保存,加载(加载=重新加载完整对象图)
- 添加一个孩子
- 保存,加载
- 删除孩子
- 结果:清空数据库。 (父母已删除)
这可能是一个基本的映射错误,因为这是我对NHibernate的第一次采访。 我希望Store成为聚合根 ,并认为通过不在 Store.Staff属性上设置Inverse,然后Store表将负责保存,因此聚合根。 这是一种误解吗? 实际上如果我使用Inverse,我仍然会得到相同的结果。 所以也许这不是问题,但我也想了解这一点。
并且故意不使用更广泛的会话范围,因为我想学习如何使用分离和瞬态实体。
员工删除方法:
class EmployeeRepository public static void Delete(Employee employee) { using (ISession session = FNH_Manager.OpenSession()) { using (ITransaction transaction = session.BeginTransaction()) { if (employee.Id != 0) { var emp = session.Get(typeof(Employee), employee.Id); if (emp != null) { session.Delete(emp); transaction.Commit(); } } } } }
映射
public class StoreMap : ClassMap { public StoreMap() { Id(x => x.Id); Map(x => x.Name); HasMany(x => x.Staff) // 1:m .Inverse() // tried both with and without, what is correct? .Cascade.All(); HasManyToMany(x => x.Products) // m:m .Cascade.All() .Table("StoreProduct"); } } public class EmployeeMap : ClassMap { public EmployeeMap() { Id(x => x.Id); // By default an int Id is generated as identity Map(x => x.FirstName); Map(x => x.LastName); References(x => x.Store); // m:1 } } public class ProductMap : ClassMap { public ProductMap() { Id(x => x.Id).GeneratedBy.Identity(); Map(x => x.Name).Length(20); Map(x => x.Price).CustomSqlType("decimal").Precision(9).Scale(2); HasManyToMany(x => x.StoresStockedIn) .Cascade.All() .Inverse() .Table("StoreProduct"); } }
实体:
public class Store { public int Id { get; private set; } public string Name { get; set; } public IList Products { get; set; } public IList Staff { get; set; } public Store() { Products = new List(); Staff = new List(); } // AddProduct & AddEmployee is required. "NH needs you to set both sides before // it will save correctly" ?? public void AddProduct(Product product) { product.StoresStockedIn.Add(this); Products.Add(product); } public void AddEmployee(Employee employee) { employee.Store = this; Staff.Add(employee); } } public class Employee { public int Id { get; private set; } public string FirstName { get; set; } public string LastName { get; set; } public Store Store { get; set; } }
编写伪代码并生成“SQL”:
程序启动
加载:存储store = StoreRepository.GetAll()
NHibernate:选择this_.Id为Id3_0_,this_.Name为Name3_0_ FROM [Store] this_
添加父级:将存储添加到空集合存储
保存:StoreRepository.SaveOrUpdate(商店)
NHibernate:SELECT store0_.Id as Id3_0_,store0_.Name as Name3_0_ FROM [Store] store0_ WHERE store0_.Id=@p0; @ p0 = 0 [Type:Int32(0)] NHibernate:INSERT INTO [Store](Name)VALUES(@ p0); 选择SCOPE_IDENTITY(); @ p0 = NULL [类型:字符串(4000)]
加载:stores = StoreRepository.GetAll()
NHibernate:选择this_.Id为Id3_0_,this_.Name为Name3_0_ FROM [Store] this_ NHibernate:SELECT products0_.Store_id as Store2_1_,products0_.Product_id as Product1_1_,product1_.Id as Id1_0_,product1_.Name as Name1_0_,product1_.Price as Price1_0_ FROM StoreProduct products0_ left outer join [Product] product1_ on products0_.Product_id = product1_.Id WHERE products0_.Store_id=@p0; @ p0 = 16 [类型:Int32(0)] NHibernate:SELECT staff0_.Store_id as Store4_1_,staff0_.Id as Id1_,staff0_.Id as Id0_0_,staff0_.FirstName as FirstName0_0_,staff0_.LastName as LastName0_0_,staff0_.Store_id as Store4_0_0_ FROM [Employee] staff0_ WHERE staff0_.Store_id=@p0 ; @ p0 = 16 [类型:Int32(0)]
对于所选商店,添加子项:空子集合
保存:StoreRepository.SaveOrUpdate(商店)
NHibernate:SELECT store0_.Id as Id3_0_,store0_.Name as Name3_0_ FROM [Store] store0_ WHERE store0_.Id=@p0; @ p0 = 16 [Type:Int32(0)] NHibernate:SELECT products0_.Store_id as Store2_1_,products0_.Product_id as Product1_1_,product1_.Id as Id1_0_,product1_.Name as Name1_0_,product1_.Price as Price1_0_ FROM StoreProduct products0_ left outer join [Product] product1_ on products0_.Product_id = product1_.Id WHERE products0_.Store_id=@p0; @ p0 = 16 [类型:Int32(0)] NHibernate:SELECT staff0_.Store_id as Store4_1_,staff0_.Id as Id1_,staff0_.Id as Id0_0_,staff0_.FirstName as FirstName0_0_,staff0_.LastName as LastName0_0_,staff0_.Store_id as Store4_0_0_ FROM [Employee] staff0_ WHERE staff0_.Store_id=@p0 ; @ p0 = 16 [类型:Int32(0)] NHibernate:INSERT INTO [Employee](FirstName,LastName,Store_id)VALUES(@ p0,@ p1,@ p2); select SCOPE_IDENTITY(); @ p0 = NULL [Type:String(4000)],@ p1 = NULL [Type:String(4000)],@ p2 = 16 [Type:Int32(0)]
加载:stores = StoreRepository.GetAll()
NHibernate:选择this_.Id为Id3_0_,this_.Name为Name3_0_ FROM [Store] this_ NHibernate:SELECT products0_.Store_id as Store2_1_,products0_.Product_id as Product1_1_,product1_.Id as Id1_0_,product1_.Name as Name1_0_,product1_.Price as Price1_0_ FROM StoreProduct products0_ left outer join [Product] product1_ on products0_.Product_id = product1_.Id WHERE products0_.Store_id=@p0; @ p0 = 16 [类型:Int32(0)] NHibernate:SELECT staff0_.Store_id as Store4_1_,staff0_.Id as Id1_,staff0_.Id as Id0_0_,staff0_.FirstName as FirstName0_0_,staff0_.LastName as LastName0_0_,staff0_.Store_id as Store4_0_0_ FROM [Employee] staff0_ WHERE staff0_.Store_id=@p0 ; @ p0 = 16 [类型:Int32(0)]
删除子项:(删除所选商店的员工)EmployeeRepository.Delete(员工)
NHibernate:SELECT employee0_.Id为Id0_1_,employee0_.FirstName为FirstName0_1_,employee0_.LastName为LastName0_1_,employee0_.Store_id为Store4_0_1_,store1_.Id为Id3_0_,store1_.Name为Name3_0_ FROM [Employee] employee0_ left outer join [Store] store1_ on employee0_.Store_id = store1_.Id WHERE employee0_.Id=@p0; @ p0 = 35 [类型:Int32(0)] NHibernate:SELECT products0_.Store_id as Store2_1_,products0_.Product_id as Product1_1_,product1_.Id as Id1_0_,product1_.Name as Name1_0_,product1_.Price as Price1_0_ FROM StoreProduct products0_ left outer join [Product] product1_ on products0_.Product_id = product1_.Id WHERE products0_.Store_id=@p0; @ p0 = 16 [类型:Int32(0)] NHibernate:SELECT staff0_.Store_id as Store4_1_,staff0_.Id as Id1_,staff0_.Id as Id0_0_,staff0_.FirstName as FirstName0_0_,staff0_.LastName as LastName0_0_,staff0_.Store_id as Store4_0_0_ FROM [Employee] staff0_ WHERE staff0_.Store_id=@p0 ; @ p0 = 16 [类型:Int32(0)] NHibernate:DELETE FROM [Employee] WHERE Id = @ p0; @ p0 = 35 [Type:Int32(0)] NHibernate:DELETE FROM [Store] WHERE Id = @ p0; @ p0 = 16 [Type:Int32(0)]
加载:stores = StoreRepository.GetAll()
NHibernate:选择this_.Id为Id3_0_,this_.Name为Name3_0_ FROM [Store] this_
(没有结果,数据库是空的)
EDIT1:
SQL没有反向
程序启动
加载:存储store = StoreRepository.GetAll()
NHibernate:选择this_.Id为Id3_0_,this_.Name为Name3_0_ FROM [Store] this_
添加父级:将存储添加到空集合存储
保存:StoreRepository.SaveOrUpdate(商店)
NHibernate:SELECT store0_.Id as Id3_0_,store0_.Name as Name3_0_ FROM [Store] store0_ WHERE store0_.Id=@p0; @ p0 = 0 [Type:Int32(0)] NHibernate:INSERT INTO [Store](Name)VALUES(@ p0); 选择SCOPE_IDENTITY(); @ p0 = NULL [类型:字符串(4000)]
加载:stores = StoreRepository.GetAll()
NHibernate:选择this_.Id为Id3_0_,this_.Name为Name3_0_ FROM [Store] this_ NHibernate:SELECT products0_.Store_id as Store2_1_,products0_.Product_id as Product1_1_,product1_.Id as Id1_0_,product1_.Name as Name1_0_,product1_.Price as Price1_0_ FROM StoreProduct products0_ left outer join [Product] product1_ on products0_.Product_id = product1_.Id WHERE products0_.Store_id=@p0; @ p0 = 1 [类型:Int32(0)] NHibernate:SELECT staff0_.Store_id as Store4_1_,staff0_.Id as Id1_,staff0_.Id as Id0_0_,staff0_.FirstName as FirstName0_0_,staff0_.LastName as LastName0_0_,staff0_.Store_id as Store4_0_0_ FROM [Employee] staff0_ WHERE staff0_.Store_id=@p0 ; @ p0 = 1 [类型:Int32(0)]
对于所选商店,添加子项:空子集合
保存:StoreRepository.SaveOrUpdate(商店)
NHibernate:SELECT store0_.Id as Id3_0_,store0_.Name as Name3_0_ FROM [Store] store0_ WHERE store0_.Id=@p0; @ p0 = 1 [Type:Int32(0)] NHibernate:SELECT products0_.Store_id as Store2_1_,products0_.Product_id as Product1_1_,product1_.Id as Id1_0_,product1_.Name as Name1_0_,product1_.Price as Price1_0_ FROM StoreProduct products0_ left outer join [Product] product1_ on products0_.Product_id = product1_.Id WHERE products0_.Store_id=@p0; @ p0 = 1 [类型:Int32(0)] NHibernate:SELECT staff0_.Store_id as Store4_1_,staff0_.Id as Id1_,staff0_.Id as Id0_0_,staff0_.FirstName as FirstName0_0_,staff0_.LastName as LastName0_0_,staff0_.Store_id as Store4_0_0_ FROM [Employee] staff0_ WHERE staff0_.Store_id=@p0 ; @ p0 = 1 [类型:Int32(0)] NHibernate:INSERT INTO [Employee](FirstName,LastName,Store_id)VALUES(@ p0,@ p1,@ p2); select SCOPE_IDENTITY(); @ p0 = NULL [Type:String(4000)],@ p1 = NULL [Type:String(4000)],@ p2 = 1 [Type:Int32(0)] NHibernate:UPDATE [Employee] SET Store_id = @ p0 WHERE Id = @ p1; @ p0 = 1 [Type:Int32(0)],@ p1 = 1 [Type:Int32(0)]
加载:stores = StoreRepository.GetAll()
NHibernate:选择this_.Id为Id3_0_,this_.Name为Name3_0_ FROM [Store] this_ NHibernate:SELECT products0_.Store_id as Store2_1_,products0_.Product_id as Product1_1_,product1_.Id as Id1_0_,product1_.Name as Name1_0_,product1_.Price as Price1_0_ FROM StoreProduct products0_ left outer join [Product] product1_ on products0_.Product_id = product1_.Id WHERE products0_.Store_id=@p0; @ p0 = 1 [类型:Int32(0)] NHibernate:SELECT staff0_.Store_id as Store4_1_,staff0_.Id as Id1_,staff0_.Id as Id0_0_,staff0_.FirstName as FirstName0_0_,staff0_.LastName as LastName0_0_,staff0_.Store_id as Store4_0_0_ FROM [Employee] staff0_ WHERE staff0_.Store_id=@p0 ; @ p0 = 1 [类型:Int32(0)]
删除子项:(删除所选商店的员工)EmployeeRepository.Delete(员工)
NHibernate:SELECT employee0_.Id为Id0_1_,employee0_.FirstName为FirstName0_1_,employee0_.LastName为LastName0_1_,employee0_.Store_id为Store4_0_1_,store1_.Id为Id3_0_,store1_.Name为Name3_0_ FROM [Employee] employee0_ left outer join [Store] store1_ on employee0_.Store_id = store1_.Id WHERE employee0_.Id=@p0; @ p0 = 1 [类型:Int32(0)] NHibernate:SELECT products0_.Store_id as Store2_1_,products0_.Product_id as Product1_1_,product1_.Id as Id1_0_,product1_.Name as Name1_0_,product1_.Price as Price1_0_ FROM StoreProduct products0_ left outer join [Product] product1_ on products0_.Product_id = product1_.Id WHERE products0_.Store_id=@p0; @ p0 = 1 [类型:Int32(0)] NHibernate:SELECT staff0_.Store_id as Store4_1_,staff0_.Id as Id1_,staff0_.Id as Id0_0_,staff0_.FirstName as FirstName0_0_,staff0_.LastName as LastName0_0_,staff0_.Store_id as Store4_0_0_ FROM [Employee] staff0_ WHERE staff0_.Store_id=@p0 ; @ p0 = 1 [类型:Int32(0)] NHibernate:UPDATE [Employee] SET Store_id = null WHERE Store_id = @ p0; @ p0 = 1 [Type:Int32(0)] NHibernate:DELETE FROM [Employee] WHERE Id = @ p0; @ p0 = 1 [Type:Int32(0)] NHibernate:DELETE FROM [Store] WHERE Id = @ p0; @ p0 = 1 [Type:Int32(0)]
加载:stores = StoreRepository.GetAll()
NHibernate:选择this_.Id为Id3_0_,this_.Name为Name3_0_ FROM [Store] this_
(仍然没有结果,数据库是空的)
程序窗口
所选商店的商店集合和子集合绑定到BindingSource / DataGridView / BindingNavigator,如下所示:
EDIT2
private static ISessionFactory CreateSessionFactory() { if (sessionFactory == null) { return Fluently.Configure() .Database(MsSqlConfiguration.MsSql2008 .ConnectionString(Properties.Settings.Default.FnhDbString) .Cache(c => c .UseQueryCache()).ShowSql()) .Mappings(m => m.FluentMappings.AddFromAssemblyOf() .Conventions.Add(FluentNHibernate.Conventions.Helpers.DefaultLazy.Never()) .Conventions.Add(FluentNHibernate.Conventions.Helpers.DefaultCascade.All()) .ExportTo("D:/VB/")) .ExposeConfiguration(c => cfg = c) .BuildSessionFactory(); } return sessionFactory; }
EDIT3
我现在尝试了以下所有不同的映射(1-6)。 如果没有级联约定,我会对所有替代方案都有例外。 我是否被迫手动删除引用 ? 我认为不应该要求它。
//对于所有替代方案,配置不指定级联约定。 // HasMany(x => x.Staff); // 1.添加商店,保存,加载,添加员工, // save:TransientObjectException; 雇员 HasMany(x => x.Staff).Inverse(); // 2.作为1 // HasMany(x => x.Staff).Cascade.All(); // 3.添加商店,保存,加载,添加员工,保存,加载, //删除Employee:ObjectDeletedException // HasMany(x => x.Staff).Inverse()。Cascade.All(); // 4.作为3 // HasMany(x => x.Staff).Inverse()。Cascade.AllDeleteOrphan(); // 5.作为3/4 // HasMany(x => x.Staff).Cascade.None(); // 6.为1/2 //例外1) //在StoreRepositorySaveOrUpdate(商店)上:TransientObjectException: //对象引用未保存的瞬态实例 - 在刷新之前保存瞬态实例。 //类型:FNHib_Test.Entities.Employee,实体:FNHib_Test.Entities.Employee //例外3) //在EmployeeRepository.Delete(员工)上; 器transaction.commit() // ObjectDeletedException未处理: //已删除的对象将通过级联重新保存 //(从关联中删除已删除的对象)[FNHib_Test.Entities.Employee#1]
EDIT5:
以上exception的结果:
1)存储是聚合根(无反向集)。 由于没有级联:我需要在保存聚合时手动处理添加的子项。 (好)
2)Employee是聚合根(反向集)。 仍然,因为没有级联:我需要手动处理添加的Employee,因为stores集合包含持久性和瞬态实体。 所以1和2的线索就是cascade = none。 逆是无关紧要的。 (好)
3)存储是聚合根(无反向集)。 Cascade = all,它在两个方向都有效,不仅来自聚合根? 因此,如果没有先删除它对父级的引用,我们就无法删除它。 (也许OK)。
4)同样的原因3.反向对级联没有影响。 (也许OK)
5)与3相同的原因。
6)与1相同。
如果这是结论。 然后它意味着我们被迫在删除子项之前删除双向实体之间的引用。 无论Inverse的设置如何。
所以: 我看不出Inverse对双向关系有任何影响。 ?
EDIT6:
(呼吸..)甚至设置emp.Store = null; 它仍然提供ObjectDeletedException :已删除的对象将通过级联重新保存(从关联中删除已删除的对象)[FNHib_Test.Entities.Employee#1]
这是映射; HasMany(x => x.Staff).Cascade.All();
public static void Delete(Employee employee) { using (ISession session = FNH_Manager.OpenSession()) { using (ITransaction transaction = session.BeginTransaction()) { employee.Store = null; if (employee.Id != 0) { // var emp = session.Get(typeof(Employee), employee.Id); Employee emp = session.Get( employee.Id); if (emp != null) { emp.Store = null; session.Delete(emp); transaction.Commit(); } } } } }
我想知道在保存瞬态实例时是否存在与实体-Id未被设置相关的问题。 这就是我在每次保存后加载的原因。 但我不知道为什么他们没有定。 正如我在这里描述的那样: NHibernate:在保存瞬态实例时如何更新身份ID?
不要在映射中使用逆映射。 没有逆,它应该没问题。
“我用常规Cascade.All配置”是什么意思? 您使用的是Fluent NHibernate惯例吗? 我在您的映射中看不到任何会导致Employee删除级联到附加商店的内容。 我也没有看到为什么加载Employee会触发加载Store及其Staff and Products集合的任何原因。
对Delete方法的这种更改可能会解决问题,但它没有解决根本原因:
if (emp != null) { emp.Store = null; session.Delete(emp); transaction.Commit(); }
好的..最后..我发现至少有一个可行的解决方案。 不确定这是否应该如何解决:
所需要的是从父列表中删除实例。 孩子仍然可以参考父母,但不能反过来。 所以,如下图所示:
emp.Store.Staff.Remove(EMP);
这看起来有点尴尬。 这只是一个正常的父子关系,我们试图删除孩子。 但也许其他人可以用适当的知识来阐述……
顺便说一下,这是映射: HasMany(x => x.Staff).Cascade.All();
public static void Delete(Employee employee) { using (ISession session = FNH_Manager.OpenSession()) { using (ITransaction transaction = session.BeginTransaction()) { if (employee.Id != 0) { Employee emp = session.Get(employee.Id); if (emp != null) { emp.Store.Staff.Remove(emp); session.Delete(emp); transaction.Commit(); } } } } }
- 即使关闭WPF跟踪设置,奇怪的资源字典警告也会出现在输出窗口中
- 嵌入式(ASP.NET)Web服务器
- Microsoft.Office.Interop.Excel或EPPlus用于读取大量(或不是)Excel文件
- .NET是否可以在combobox的项目上设置上下文菜单?
- IronPython脚本调试
- 自定义GroupBox,带有自定义TextColor,BorderColor和Transparent BackColor
- WPF ListBox在用户滚动时生成项目
- 无法加载文件或程序集’System,Version = 4.0.0.0,Culture = neutral,PublicKeyToken = b77a5c561934e089’或其依赖项之一
- 通过函数指针在C#中调用C函数