为什么删除子(员工) 时删除了 父(商店)? Add parent: Add store to empty collection stores Save: StoreRepository.SaveOrUpdate(stores) Load: stores = StoreRepository.GetAll() Add child: to empty child collection, for selected store Save: StoreRepository.SaveOrUpdate(stores) Load: stores = StoreRepository.GetAll() Delete child: (Delete Employee for selected store) EmployeeRepository.Delete(employee) Load: stores = StoreRepository.GetAll() (no result, database is empty) EDIT1: SQL WITHOUT Inverse Program startup Load: Stores stores = StoreRepository.GetAll() Add parent: Add store to empty collection stores Save: StoreRepository.SaveOrUpdate(stores) Load: stores = StoreRepository.GetAll() Add child: to empty child collection, for selected store Save: StoreRepository.SaveOrUpdate(stores) Load: stores = StoreRepository.GetAll() Delete child: (Delete Employee for selected store) EmployeeRepository.Delete(employee) Load: stores = StoreRepository.GetAll() (Still; no result, database is empty) Program window Store collection and child collection of selected store is bound to BindingSource/DataGridView/BindingNavigator like this: EDIT2 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();
我使用 约定 Cascade.All 进行配置。
用户输入序列非常简单:
这可能是一个基本的映射错误,因为这是我第一次使用 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”:
程序启动
加载:商店商店 = StoreRepository.GetAll()NHibernate: SELECT this_.Id as Id3_0_, this_.Name as Name3_0_ FROM [Store] this_
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)]
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)]
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)]
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)]
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)]
NHibernate: SELECT this_.Id as Id3_0_, this_.Name as Name3_0_ FROM [Store] this_
NHibernate: SELECT this_.Id as Id3_0_, this_.Name as Name3_0_ FROM [Store] this_
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)]
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)]
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)]
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)]
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)]
NHibernate: SELECT this_.Id as Id3_0_, this_.Name as Name3_0_ FROM [Store] this_
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]
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?
最佳答案
不要在映射中使用 inverse 为您的情况。没有逆应该没问题。
关于c# - NHibernate;删除子项会删除父项?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/4890123/