2013-04-24 67 views
1

我与功能NHibernate奋力从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 pri或去例外....

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

有你'WarehouseMap'类的错误。不应该把表(“库存”)变成像表(“仓库”)那样的东西? – mickfold 2013-04-24 21:13:31

+0

@Penfold我根据你的评论更新了这个问题我认为create table语句显示了我在这种情况下应该期待的(我认为) – 0909EM 2013-04-24 21:21:58

回答

4

问题的原因是NHibernate试图在Warehouse记录之前插入Inventory记录。这是因为插入顺序由调用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记录和外键引发错误。

最后,为了获得下面列出的三条插入语句,必须在ProductMap映射类中重新设置Inverse()。否则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)] 
+0

你在这里说的是非常合理的。问题是Nhibernate(或者更加流利的nhibernate)需要在插入到链接表(它有一个到每个相邻表的外键链接)之前确保键就位。 问题是,流利的NHibernate *应该*计算出这些表需要填充的顺序 - 即在链接表之前。在我的情况下,链接表具有描述实体之间关系的附加数据。我相对高兴地接受你的答案,但我认为它是次优的(最好的答案我得到了)! – 0909EM 2013-04-26 20:39:24

+0

正如我在我的答案中所说的NHibernate没有按照我的预期完全工作。看起来你已经碰到了一个边缘情况。 NHibernate非常好,但它并不完美。对不起,我不能更有帮助。 – mickfold 2013-04-26 20:47:18

+0

不,你的答案很棒 - 反映了我的想法(如果你喜欢阅读,请参阅下文)。也许我已经错过了一些东西,**也许**这是安静地记录在某个地方。我会继续寻找。感谢您的意见。 – 0909EM 2013-04-26 21:03:18

3

对于可能跟随我的脚步......任何人

我的问题是,我想存储有关链接表实体之间的关系的一些信息。通常数据库术语中的多对多关系具有域对象之间的链接表。

使用问题中概述的结构,需要在插入库存项目之前插入仓库和产品。库存项目必须最后插入,以便在保存发生前两个外键约束都已就位。

Insert into Product 

Insert into Warehouse 

Insert into Inventory (Note this happens **after** the primary keys are 
inserted in Warehouse and Product!) 

但是我的工作我的映射功能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(product); 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); 

任何人都可以改进,并给我我真正想要的映射,这样我可以保存仓库或产品和流利的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! 
相关问题