NHibernate - 一对多只是不使用 SQLite
NHibernate - one-to-many just not working with SQLite
TL;DR;
NHibernate
反向关系适用于 Azure-SQL
和 MSSQL2012
但不适用于 SQLite
描述:
我目前正在对我的 Asp.Net MVC 应用程序进行单元测试,并在 SQLite 上使用 FluentMigrator
设置我的单元测试。
创建数据库后,我设置了一些我需要的基本条目。
其中之一是产品。
一个 Product
有很多 ProductSuppliers
而一个 ProductSupplier
有很多 ProductSupplierPrices
public class Product
{
public virtual long Id { get; set; }
public virtual string Number { get; set; }
public virtual string Name { get; set; }
public virtual string Description { get; set; }
//more properties
public virtual IList<ProductSupplier> Suppliers { get; set; }
//more properties
}
public class ProductSupplier
{
public virtual long Id { get; set; }
public virtual Product Product { get; set; }
public virtual Supplier Supplier { get; set; }
public virtual IList<ProductSupplierPrice> Prices { get; set; }
}
public class ProductSupplierPrice : IHaveId
{
public virtual long Id { get; set; }
public virtual ProductSupplier ProductSupplier { get; set; }
public virtual decimal FromAmount { get; set; }
public virtual decimal Price { get; set; }
}
设置:
- 创建供应商
- 创建产品
- 创建产品供应商
- 创建 ProductSupplierPrice
测试:
Product product = this.session.Load<Product>((long)1);
ProductSupplier productSupplier = product.Suppliers.First(); //<-- Suppliers are null; therefore throws an exception
如果我单独加载它们以检查关系:
productSupplierPrice.ProductSupplier <--- Correct Supplier
productSupplier.Prices <-- Null
productSupplier.Product <-- Product with Id 1
product.Suppliers <-- Null
所以在我看来,多对一的方向工作正确,但一对多(反向关系)不工作。
问题仅存在于我的单元测试 (SQLite) 中,应用程序本身在 Azure-SQL
上运行并且工作正常。
编辑:
与 FluentnHibernate 的映射
public class ProductMap : ClassMap<Product>
{
public ProductMap()
{
Id(x => x.Id);
HasMany(x => x.Suppliers).Inverse().Cascade.DeleteOrphan().BatchSize(20);
//many more mappings
}
}
public ProductSupplierMap()
{
Id(x => x.Id);
References(x => x.Product);
References(x => x.Supplier);
Map(x => x.IsMainSupplier);
Map(x => x.SupplierProductNumber);
Map(x => x.CopperSurcharge);
HasMany(x => x.Prices).Inverse().Cascade.DeleteOrphan().BatchSize(20);
}
public ProductSupplierPriceMap()
{
Id(x => x.Id);
References(x => x.ProductSupplier);
Map(x => x.FromAmount);
Map(x => x.Price);
}
Edit2 - 创建数据库条目:
Product product = new Product()
{
Type = ProductType.Purchase,
Dispatcher = session.Load<Employee>(employeeId),
Number = "100.10-1000",
Name = "Testproduct",
//Lots of Properties
Suppliers = new List<ProductSupplier>()
};
session.SaveOrUpdate(product);
ProductSupplier productSupplier = new ProductSupplier()
{
Product = product,
Supplier = session.Load<Supplier>((long)1),
IsMainSupplier = true,
SupplierProductNumber = "Artikel123456",
CopperSurcharge = CopperSurchargeType.DEL700,
Prices = new List<ProductSupplierPrice>()
};
session.Save(productSupplier);
ProductSupplierPrice productSupplierPrice = new ProductSupplierPrice()
{
ProductSupplier = productSupplier,
FromAmount = 1,
Price = 5
};
session.Save(productSupplierPrice);
编辑 3.1:
public static ISession InitializeDatabase()
{
NHibernateSessionHolder.CreateSessionFactory();
session = NHibernateSessionHolder.OpenSession();
CreateBaseEntries(); //Creates Employees, Supplier, Customer etc
return session;
}
基于Ayende's article你需要清除insert/update和查询之间的会话:
session.Clear();
似乎是一个会话管理,我不确定为什么会话应该是干净的,但是会话提供了您的原始实例(与您提供的保存实例相同,存储在会话缓存中)而不是代理延迟加载。
private long CreatePurchaseOrder()
{
session.Clear();
var product = this.session.Load<Product>((long)1);
var productSupplier = product.Suppliers.First();
var productSupplierPrice = productSupplier.Prices.First();
return 0;
}
抱歉回复晚了
在您的单元测试中,您使用相同的会话来创建和获取实体。这是不正确的,因为随后从一级缓存中提取 returns 个实体,它们的图形设置不正确。
所以....使用不同的会话 或 作为快速修复,我在 [=27 的方法 "InitializeDatabase()" 中添加了 "session.Clear()" =].清除会话会清除一级缓存并强制 NH 再次从数据库中获取数据,并且生成的实体会正确设置其图表。
public static ISession InitializeDatabase()
{
NHibernateSessionHolder.CreateSessionFactory();
session = NHibernateSessionHolder.OpenSession();
CreateBaseEntries();
session.Clear(); // notice this!!! this clears first level cache of session, thus forcing fetching of data from DB
return session;
}
注意:我的快速修复不是最终解决方案,它只是展示会话的行为方式。在正确的解决方案中,您必须使用不同的会话。
TL;DR;
NHibernate
反向关系适用于 Azure-SQL
和 MSSQL2012
但不适用于 SQLite
描述:
我目前正在对我的 Asp.Net MVC 应用程序进行单元测试,并在 SQLite 上使用 FluentMigrator
设置我的单元测试。
创建数据库后,我设置了一些我需要的基本条目。
其中之一是产品。
一个 Product
有很多 ProductSuppliers
而一个 ProductSupplier
有很多 ProductSupplierPrices
public class Product
{
public virtual long Id { get; set; }
public virtual string Number { get; set; }
public virtual string Name { get; set; }
public virtual string Description { get; set; }
//more properties
public virtual IList<ProductSupplier> Suppliers { get; set; }
//more properties
}
public class ProductSupplier
{
public virtual long Id { get; set; }
public virtual Product Product { get; set; }
public virtual Supplier Supplier { get; set; }
public virtual IList<ProductSupplierPrice> Prices { get; set; }
}
public class ProductSupplierPrice : IHaveId
{
public virtual long Id { get; set; }
public virtual ProductSupplier ProductSupplier { get; set; }
public virtual decimal FromAmount { get; set; }
public virtual decimal Price { get; set; }
}
设置:
- 创建供应商
- 创建产品
- 创建产品供应商
- 创建 ProductSupplierPrice
测试:
Product product = this.session.Load<Product>((long)1);
ProductSupplier productSupplier = product.Suppliers.First(); //<-- Suppliers are null; therefore throws an exception
如果我单独加载它们以检查关系:
productSupplierPrice.ProductSupplier <--- Correct Supplier
productSupplier.Prices <-- Null
productSupplier.Product <-- Product with Id 1
product.Suppliers <-- Null
所以在我看来,多对一的方向工作正确,但一对多(反向关系)不工作。
问题仅存在于我的单元测试 (SQLite) 中,应用程序本身在 Azure-SQL
上运行并且工作正常。
编辑:
与 FluentnHibernate 的映射
public class ProductMap : ClassMap<Product>
{
public ProductMap()
{
Id(x => x.Id);
HasMany(x => x.Suppliers).Inverse().Cascade.DeleteOrphan().BatchSize(20);
//many more mappings
}
}
public ProductSupplierMap()
{
Id(x => x.Id);
References(x => x.Product);
References(x => x.Supplier);
Map(x => x.IsMainSupplier);
Map(x => x.SupplierProductNumber);
Map(x => x.CopperSurcharge);
HasMany(x => x.Prices).Inverse().Cascade.DeleteOrphan().BatchSize(20);
}
public ProductSupplierPriceMap()
{
Id(x => x.Id);
References(x => x.ProductSupplier);
Map(x => x.FromAmount);
Map(x => x.Price);
}
Edit2 - 创建数据库条目:
Product product = new Product()
{
Type = ProductType.Purchase,
Dispatcher = session.Load<Employee>(employeeId),
Number = "100.10-1000",
Name = "Testproduct",
//Lots of Properties
Suppliers = new List<ProductSupplier>()
};
session.SaveOrUpdate(product);
ProductSupplier productSupplier = new ProductSupplier()
{
Product = product,
Supplier = session.Load<Supplier>((long)1),
IsMainSupplier = true,
SupplierProductNumber = "Artikel123456",
CopperSurcharge = CopperSurchargeType.DEL700,
Prices = new List<ProductSupplierPrice>()
};
session.Save(productSupplier);
ProductSupplierPrice productSupplierPrice = new ProductSupplierPrice()
{
ProductSupplier = productSupplier,
FromAmount = 1,
Price = 5
};
session.Save(productSupplierPrice);
编辑 3.1:
public static ISession InitializeDatabase()
{
NHibernateSessionHolder.CreateSessionFactory();
session = NHibernateSessionHolder.OpenSession();
CreateBaseEntries(); //Creates Employees, Supplier, Customer etc
return session;
}
基于Ayende's article你需要清除insert/update和查询之间的会话:
session.Clear();
似乎是一个会话管理,我不确定为什么会话应该是干净的,但是会话提供了您的原始实例(与您提供的保存实例相同,存储在会话缓存中)而不是代理延迟加载。
private long CreatePurchaseOrder()
{
session.Clear();
var product = this.session.Load<Product>((long)1);
var productSupplier = product.Suppliers.First();
var productSupplierPrice = productSupplier.Prices.First();
return 0;
}
抱歉回复晚了
在您的单元测试中,您使用相同的会话来创建和获取实体。这是不正确的,因为随后从一级缓存中提取 returns 个实体,它们的图形设置不正确。
所以....使用不同的会话 或 作为快速修复,我在 [=27 的方法 "InitializeDatabase()" 中添加了 "session.Clear()" =].清除会话会清除一级缓存并强制 NH 再次从数据库中获取数据,并且生成的实体会正确设置其图表。
public static ISession InitializeDatabase()
{
NHibernateSessionHolder.CreateSessionFactory();
session = NHibernateSessionHolder.OpenSession();
CreateBaseEntries();
session.Clear(); // notice this!!! this clears first level cache of session, thus forcing fetching of data from DB
return session;
}
注意:我的快速修复不是最终解决方案,它只是展示会话的行为方式。在正确的解决方案中,您必须使用不同的会话。