gpt4 book ai didi

c# - N休眠;删除 child 会删除 parent 吗?

转载 作者:太空狗 更新时间:2023-10-30 01:56:47 27 4
gpt4 key购买 nike

为什么删除子项(员工)时父项(商店)也被删除

我使用 convention Cascade.All 配置。

用户输入序列非常简单:

  • 从空数据库开始
  • 添加家长
  • 保存、加载(加载 = 重新加载完整对象图)
  • 添加一个 child
  • 保存、加载
  • 删除 child
  • 结果:空数据库。 (父删除)

这可能是一个基本的映射错误,因为这是我第一次使用 NHibernate。我希望 Store 成为聚合根,并认为通过 在 Store.Staff 属性上设置 Inverse,Store 表将负责保存,因此聚合根。这是一种误解吗?实际上,无论我是否使用 Inverse,我仍然会得到相同的结果。所以也许那不是问题,但我也想了解这一点。

并且故意不使用更宽的 session 范围,因为我想学习如何使用分离的和 transient 的实体。

员工删除方法:

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<Store>
{
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<Employee>
{

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<Product>
{
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<Product> Products { get; set; }
public IList<Employee> Staff { get; set; }

public Store()
{
Products = new List<Product>();
Staff = new List<Employee>();
}


// 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”:

程序启动

加载:商店stores = StoreRepository.GetAll()

NHibernate: SELECT this_.Id as Id3_0_, this_.Name as Name3_0_ FROM [Store] this_

Add parent: Add store to empty collection stores

Save: StoreRepository.SaveOrUpdate(stores)

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); select SCOPE_IDENTITY();@p0 = NULL [Type: String (4000)]

Load: stores = StoreRepository.GetAll()

NHibernate: SELECT this_.Id as Id3_0_, this_.Name as 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 [Type: 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 [Type: Int32 (0)]

Add child: to empty child collection, for selected store

Save: StoreRepository.SaveOrUpdate(stores)

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 [Type: 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 [Type: 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)]

Load: stores = StoreRepository.GetAll()

NHibernate: SELECT this_.Id as Id3_0_, this_.Name as 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 [Type: 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 [Type: Int32 (0)]

Delete child: (Delete Employee for selected store) EmployeeRepository.Delete(employee)

NHibernate: SELECT employee0_.Id as Id0_1_, employee0_.FirstName as FirstName0_1_, employee0_.LastName as LastName0_1_, employee0_.Store_id as Store4_0_1_, store1_.Id as Id3_0_, store1_.Name as Name3_0_ FROM [Employee] employee0_ left outer join [Store] store1_ on employee0_.Store_id=store1_.Id WHERE employee0_.Id=@p0;@p0 = 35 [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 [Type: 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 [Type: 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)]

Load: stores = StoreRepository.GetAll()

NHibernate: SELECT this_.Id as Id3_0_, this_.Name as Name3_0_ FROM [Store] this_

(no result, database is empty)


EDIT1:

SQL WITHOUT Inverse

Program startup

Load: Stores stores = StoreRepository.GetAll()

NHibernate: SELECT this_.Id as Id3_0_, this_.Name as Name3_0_ FROM [Store] this_

Add parent: Add store to empty collection stores

Save: StoreRepository.SaveOrUpdate(stores)

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); select SCOPE_IDENTITY();@p0 = NULL [Type: String (4000)]

Load: stores = StoreRepository.GetAll()

NHibernate: SELECT this_.Id as Id3_0_, this_.Name as 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 [Type: 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 [Type: Int32 (0)]

Add child: to empty child collection, for selected store

Save: StoreRepository.SaveOrUpdate(stores)

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 [Type: 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 [Type: 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)]

Load: stores = StoreRepository.GetAll()

NHibernate: SELECT this_.Id as Id3_0_, this_.Name as 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 [Type: 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 [Type: Int32 (0)]

Delete child: (Delete Employee for selected store) EmployeeRepository.Delete(employee)

NHibernate: SELECT employee0_.Id as Id0_1_, employee0_.FirstName as FirstName0_1_, employee0_.LastName as LastName0_1_, employee0_.Store_id as Store4_0_1_, store1_.Id as Id3_0_, store1_.Name as Name3_0_ FROM [Employee] employee0_ left outer join [Store] store1_ on employee0_.Store_id=store1_.Id WHERE employee0_.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 [Type: 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 [Type: 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)]

Load: stores = StoreRepository.GetAll()

NHibernate: SELECT this_.Id as Id3_0_, this_.Name as Name3_0_ FROM [Store] this_

(Still; no result, database is empty)

Program window

Store collection and child collection of selected store is bound to BindingSource/DataGridView/BindingNavigator like this:

enter image description here


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<EmployeeMap>()
.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)。没有级联约定,我对所有替代方案都有异常(exception)。我是否被迫手动删除引用?我认为不应该要求它。

// For all alternatives, configuration does not specify cascade-convention.//  HasMany(x => x.Staff);   // 1. add store, save, load, add employee,                              // save: TransientObjectException; Employee    HasMany(x => x.Staff).Inverse();       // 2. As 1//  HasMany(x => x.Staff).Cascade.All();   // 3. Add store, Save, Load, Add Employee, Save, Load,                                            // Delete Employee: ObjectDeletedException//  HasMany(x => x.Staff).Inverse().Cascade.All();              // 4. As 3//  HasMany(x => x.Staff).Inverse().Cascade.AllDeleteOrphan();  // 5.  As 3/4//  HasMany(x => x.Staff).Cascade.None();                       // 6. As 1/2// Exception of 1) // On StoreRepositorySaveOrUpdate(stores): TransientObjectException: // object references an unsaved transient instance - save the transient instance before flushing. // Type: FNHib_Test.Entities.Employee, Entity: FNHib_Test.Entities.Employee// Exception of 3) // On EmployeeRepository.Delete(employee);    transaction.Commit()// ObjectDeletedException was unhandled: // deleted object would be re-saved by cascade // (remove deleted object from associations)[FNHib_Test.Entities.Employee#1]

EDIT5:

Findings to above exceptions:

1) Store is aggregate root ( No Inverse set). Since no cascade: I need to handle added children manually on saving aggregate. (OK)

2) Employee is aggregate root (Inverse set). Still, since no cascade: I need to handle added Employee manually, simply because the stores collection contain both persistent and transient entities. So the clue of 1 and 2 is simply that cascade = none. Inverse is irrelevant. (OK)

3) Store is aggregate root (No inverse set). Cascade=all, and it works in both directions, not only from the aggregate root? So we can not delete the child without first removing it's reference to the parent. (Maybe OK).

4) Same reason as 3. Inverse makes no difference on the cascade. (Maybe OK)

5) Same reason as 3.

6) Same as 1.

If this is the conclusion. Then it means we are forced to remove the reference between bidirectional entities before delete of child. No matter the setting of Inverse.

So: I can't see that Inverse has ANY effect on a bidirectional relationship. ?


EDIT6:

(breathe..) Even setting the emp.Store = null; It still gives ObjectDeletedException: deleted object would be re-saved by cascade (remove deleted object from associations)[FNHib_Test.Entities.Employee#1]

This was with mapping; 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>( employee.Id);
if (emp != null)
{
emp.Store = null;
session.Delete(emp);
transaction.Commit();
}
}
}
}
}

我想知道在保存 transient 实例时是否存在与未设置实体 ID 相关的问题。这就是为什么我在每次保存后加载。但我不知道为什么他们没有设置。正如我在这里描述的:NHibernate: How is identity Id updated when saving a transient instance?

最佳答案

不要在您的案例中使用逆映射。 没有逆应该没问题。

关于c# - N休眠;删除 child 会删除 parent 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4890123/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com