我正在努力与Fluent Nhibernate Many-to-Many mapping with extra column
的流利nhibernate我已经复制了映射并编写了我能够编写的最小程序......但它不会保存......是否有人能够提供一些见解?
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public IList<Inventory> Inventory { get; set; }
public Product()
{
Inventory = new List<Inventory>();
}
}
public class Warehouse
{
public int Id { get; set; }
public string Name { get; set; }
public IList<Inventory> Inventory { get; set; }
public Warehouse()
{
Inventory = new List<Inventory>();
}
}
public class Inventory
{
public Product Product { get; set; }
public Warehouse Warehouse { get; set; }
public bool StockInHand { get; set; }
// override object.Equals
public override bool Equals(object obj)
{
if (obj == null || GetType() != obj.GetType())
{
return false;
}
var i = obj as Inventory;
return ((i.Product.Id == this.Product.Id)
&& (i.Warehouse.Id == this.Warehouse.Id));
}
// override object.GetHashCode
public override int GetHashCode()
{
return 9999;
}
}
public class ProductMap : ClassMap<Product>
{
public ProductMap()
{
Not.LazyLoad();
Table("Product");
Id(x => x.Id).GeneratedBy.Assigned();
Map(x => x.Name);
HasMany(x => x.Inventory).AsBag()
.Cascade.All()
//.Inverse()
.Table("Inventory");
}
}
public class WarehouseMap : ClassMap<Warehouse>
{
public WarehouseMap()
{
Not.LazyLoad();
Table("Warehouse");
Id(x => x.Id).GeneratedBy.Assigned();
Map(x => x.Name);
HasMany(x => x.Inventory).AsBag()
.Cascade.All()
.Inverse()
.Table("Inventory");
}
}
public class InventoryMap : ClassMap<Inventory>
{
public InventoryMap()
{
Not.LazyLoad();
Table("Inventory");
CompositeId()
.KeyReference(x => x.Product, "Product_id")
.KeyReference(x => x.Warehouse, "Warehouse_id");
Map(x => x.StockInHand);
}
}
程序......
using (var session = sessionFactory.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
Product p = new Product() { Id = 1, Name="product" };
Inventory i = new Inventory() { StockInHand = true };
i.Product = p;
p.Inventory.Add(i);
Warehouse w = new Warehouse() { Id = 1, Name = "warehouse" };
i.Warehouse = w;
w.Inventory.Add(i);
session.SaveOrUpdate(p);
session.Flush();
transaction.Commit();
}
}
我得到的例外是
constraint failed\r\nforeign key constraint failed
我还输出了对我来说正确的创建语句......
create table Inventory (
Product_id INT not null,
Warehouse_id INT not null,
StockInHand BOOL,
primary key (Product_id, Warehouse_id),
constraint FK2B4C61665C5B845 foreign key (Product_id) references Product,
constraint FK2B4C616A6DE7382 foreign key (Warehouse_id) references Warehouse)
create table Product (
Id INT not null,
Name TEXT,
primary key (Id)
)
create table Warehouse (
Id INT not null,
Name TEXT,
primary key (Id)
)
在异常之前运行的SQL ....
NHibernate:
INSERT
INTO
Warehouse
(Name, Id)
VALUES
(@p0, @p1);
@p0 = 'warehouse' [Type: String (0)], @p1 = 1 [Type: Int32 (0)]
NHibernate:
INSERT
INTO
Inventory
(StockInHand, Product_id, Warehouse_id)
VALUES
(@p0, @p1, @p2);
@p0 = True [Type: Boolean (0)], @p1 = 1 [Type: Int32 (0)], @p2 = 1 [Type: Int32 (0)]
那么这应该如何正常工作?!?
答案 0 :(得分:4)
问题的原因是NHibernate试图在Inventory
记录之前插入Warehouse
记录。这是因为插入的顺序由调用session.Save
的顺序控制。根据这些信息,我尝试了许多代码变体,以防止外键约束错误。我在下面发布了我最好的解决方案。
using (var session = sessionFactory.OpenSession())
using (var transaction = session.BeginTransaction())
{
var warehouse = new Warehouse() { Id = 1, Name = "warehouse" };
session.Save(warehouse);
var product = new Product() {Id = 1, Name = "product"};
var inventory = new Inventory
{ StockInHand = true, Product = product, Warehouse = warehouse};
product.Inventory.Add(inventory);
warehouse.Inventory.Add(inventory);
session.Save(product);
transaction.Commit();
}
我发现的一件让我感到惊讶的事情是,如果你把session.Save(warehouse)
放在warehouse.Inventory.Add(inventory)
之后,那么NHibernate不首先插入Warehouse
记录和外键抛出错误。
最后要注意,要获得下面列出的三个插入语句,必须在Inverse()
映射类中重新设置ProductMap
。否则,NHibernate将发出一个额外的更新语句。
INSERT INTO Warehouse (Name, Id) VALUES (@p0, @p1);@p0 = 'warehouse'
[Type: String (4000)], @p1 = 1 [Type: Int32 (0)]
INSERT INTO Product (Name, Id) VALUES (@p0, @p1);
@p0 = 'product' [Type: String (4000)], @p1 = 1 [Type: Int32 (0)]
INSERT INTO Inventory (StockInHand, Product_id, Warehouse_id) VALUES (@p0, @p1, @p2);
@p0 = True [Type: Boolean (0)], @p1 = 1 [Type: Int32 (0)], @p2 = 1 [Type: Int32 (0)]
答案 1 :(得分:3)
对于任何可能跟随我的脚步的人......
我的问题是我想存储一些关于链接表中实体之间关系的信息。经典地,数据库术语中的多对多关系在域对象之间具有链接表。
使用问题中概述的结构,在插入库存物料之前需要插入仓库和产品。必须最后插入一个库存项目,以便在保存发生之前存在两个外键约束。
Insert into Product
Insert into Warehouse
Insert into Inventory (Note this happens **after** the primary keys are
inserted in Warehouse and Product!)
但是我工作了我的映射Fluent NHibernate生成了以下内容......
Insert into Product
Insert into Inventory (Foreign Key constraint violated, no Warehouse)
..必须是不正确的,因为仓库上没有主键!我理解这个问题,但不是解决方案......我设法制作了两个解决方案,但我认为两者都不是最理想的。
public class ProductMap : ClassMap<Product>
{
public ProductMap()
{
Not.LazyLoad();
Table("Product");
Id(x => x.Id, "Product_id").GeneratedBy.Assigned();
Map(x => x.Name).Column("Name").Length(10);
HasMany(x => x.Inventory)
.Cascade.Delete()
.KeyColumn("Product_id");
}
}
public class WarehouseMap : ClassMap<Warehouse>
{
public WarehouseMap()
{
Not.LazyLoad();
Table("Warehouse");
Id(x => x.Id, "Warehouse_id").GeneratedBy.Assigned();
Map(x => x.Name).Column("Name").Length(10);
HasMany(x => x.Inventory)
.Cascade.All()
.KeyColumn("Warehouse_id");
}
}
public class InventoryMap : ClassMap<Inventory>
{
public InventoryMap()
{
Not.LazyLoad();
Table("Inventory");
CompositeId()
.KeyReference(x => x.Product, "Product_id")
.KeyReference(x => x.Warehouse, "Warehouse_id");
Map(x => x.StockInHand);
}
}
我可以执行保存并且有一半的代码按照我的预期工作,但它预先假定了正常性。也就是说,我必须知道这些对象需要保存的顺序。我还没有测试删除会发生什么,但这确实意味着我必须按照正确的顺序保存这些对象。 / *有效* / session.Save(产品); session.Save(仓库); //还会保存库存
/* would fail */
session.Save(warehouse);
session.Save(product);
或者(我更喜欢这个)我可以告诉Nhibernate我想对所有事情负责......
public class ProductMap : ClassMap<Product>
{
public ProductMap()
{
Not.LazyLoad();
Table("Product");
Id(x => x.Id, "Product_id").GeneratedBy.Assigned();
Map(x => x.Name).Column("Name").Length(10);
HasMany(x => x.Inventory).Inverse();
}
}
public class WarehouseMap : ClassMap<Warehouse>
{
public WarehouseMap()
{
Not.LazyLoad();
Table("Warehouse");
Id(x => x.Id, "Warehouse_id").GeneratedBy.Assigned();
Map(x => x.Name).Column("Name").Length(10);
HasMany(x => x.Inventory).Inverse();
}
}
public class InventoryMap : ClassMap<Inventory>
{
public InventoryMap()
{
Not.LazyLoad();
Table("Inventory");
CompositeId()
.KeyReference(x => x.Product, "Product_id")
.KeyReference(x => x.Warehouse, "Warehouse_id");
Map(x => x.StockInHand);
}
}
现在我得到以下
/* works */
session.save(Product);
session.Save(Warehouse);
session.Save(Inventory);
/* works */
session.Save(Warehouse);
session.Save(Product);
session.Save(Inventory);
/* fails */
session.Save(Inventory);
session.Save(Warehouse);
session.Save(Product);
任何人都可以对此进行改进并给我我真正想要的映射,这样我就可以保存仓库或产品以及Fluent NHibernate,这样可以获得正确的订购! 例如
session.Save(warehouse); // or session.Save(product);
这样就会产生
Insert into Warehouse...
Insert into Product...
Insert into Inventory... // where NHibernate determines this goes last so that primary keys are in place on both previous tables!