2011-03-28 17 views
2

为了让自己清楚,我创建了一个最基本的案例来描述我的问题。假设我有3个表格:NHibernate实体复合标识保存问题

CREATE TABLE [dbo].[Product](
    [ProductID] [int] IDENTITY(1,1) NOT NULL, 
    [ProductName] [varchar](50) NOT NULL, 
CONSTRAINT [PK_Product] PRIMARY KEY CLUSTERED ([ProductID] ASC) 
) ON [PRIMARY] 

CREATE TABLE [dbo].[OrderHeader](
    [HeaderID] [int] IDENTITY(1,1) NOT NULL, 
    [Comment] [varchar](100) NULL, 
CONSTRAINT [PK_OrderHeader] PRIMARY KEY CLUSTERED ([HeaderID] ASC) 
) ON [PRIMARY] 

CREATE TABLE [dbo].[OrderDetail](
    [HeaderID] [int] NOT NULL, /* FK to OrderHeader table */ 
    [ProductID] [int] NOT NULL, /* FK to Product table */ 
    [CreatedOn] [datetime] NOT NULL, 
CONSTRAINT [PK_OrderDetail] PRIMARY KEY CLUSTERED 
(
    [HeaderID] ASC, 
    [ProductID] ASC 
) 
) ON [PRIMARY] 

而且我创建了相应的实体类和映射类。

public class Product { 
    public virtual int? Id { get; set; } 

    public virtual string Name { get; set; } 
} 

public class ProductMap : ClassMap<Product> { 
    public ProductMap() { 
     Table("Product"); 

     Id(x => x.Id, "ProductID").GeneratedBy.Identity(); 
     Map(x => x.Name, "ProductName"); 
    } 
} 

public class OrderHeader { 
    public virtual int? Id { get; set; } 

    public virtual string Comment { get; set; } 

    public virtual IList<OrderDetail> Details { get; set; } 
} 

public class OrderHeaderMap : ClassMap<OrderHeader> { 
    public OrderHeaderMap() { 
     Table("OrderHeader"); 

     Id(x => x.Id, "HeaderID").GeneratedBy.Identity(); 
     Map(x => x.Comment, "Comment"); 

     HasMany<OrderDetail>(x => x.Details) 
      .KeyColumn("HeaderID") 
      .Inverse() 
      .Cascade 
      .All(); 
    } 
} 

public class OrderDetail { 
    public virtual OrderHeader OrderHeader { get; set; } 

    public virtual Product Product { get; set; } 

    public virtual DateTime? CreatedOn { get; set; } 

    public override bool Equals(object obj) { 
     OrderDetail other = obj as OrderDetail; 
     if (other == null) { 
      return false; 
     } else { 
      return this.Product.Id == other.Product.Id && this.OrderHeader.Id == other.OrderHeader.Id; 
     } 
    } 

    public override int GetHashCode() { 
     return (OrderHeader.Id.ToString() + "|" + Product.Id.ToString()).GetHashCode(); 
    } 
} 

public class OrderDetailMap : ClassMap<OrderDetail> { 
    public OrderDetailMap() { 
     Table("OrderDetail"); 

     CompositeId() 
      .KeyReference(x => x.Product, "ProductID") 
      .KeyReference(x => x.OrderHeader, "HeaderID"); 

     References<OrderHeader>(x => x.OrderHeader, "HeaderID").ForeignKey().Not.Nullable().Fetch.Join(); 
     References<Product>(x => x.Product, "ProductID").ForeignKey().Not.Nullable(); 

     Version(x => x.CreatedOn).Column("CreatedOn").Generated.Always(); 
    } 
} 

我也创建NH会话提供

public class NHibernateSessionProvider { 
    private static ISessionFactory sessionFactory; 

    public static ISessionFactory SessionFactory { 
     get { 
      if (sessionFactory == null) { 
       sessionFactory = createSessionFactory(); 
      } 
      return sessionFactory; 
     } 
    } 

    private static ISessionFactory createSessionFactory() { 
     return Fluently.Configure() 
      .Database(MsSqlConfiguration.MsSql2008.ShowSql() 
      .ConnectionString(c => c.FromConnectionStringWithKey("TestDB"))) 
      .Mappings(m => m.FluentMappings.AddFromAssemblyOf<OrderHeaderMap>()) 
      .BuildSessionFactory(); 
    } 
} 

而且一NH仓储类还创建

public class NHibernateRepository<T, TId> { 
     protected ISession session = null; 
     protected ITransaction transaction = null; 

     public NHibernateRepository() { 
      this.session = NHibernateSessionProvider.SessionFactory.OpenSession(); 
     } 

     public void Save(T entity) { 
      session.SaveOrUpdate(entity); 
     } 

     public void AddNew(T entity) { 
      session.Save(entity); 
     } 

     public void BeginTransaction() { 
      transaction = session.BeginTransaction(); 
     } 

     public void CommitTransaction() { 
      transaction.Commit(); 
      closeTransaction(); 
     } 

     public void RollbackTransaction() { 
      transaction.Rollback(); 
      closeTransaction(); 
      closeSession(); 
     } 

     private void closeTransaction() { 
      transaction.Dispose(); 
      transaction = null; 
     } 

     private void closeSession() { 
      session.Close(); 
      session.Dispose(); 
      session = null; 
     } 

     public void Dispose() { 
      if (transaction != null) { 
       CommitTransaction(); 
      } 

      if (session != null) { 
       session.Flush(); 
       closeSession(); 
      } 
     } 
    } 

在我的代码,我已经创建了两种不同的方式来保存这个主/复合标识的详细结构。

private static void method1() { 
     NHibernateRepository<Product, int?> repoProduct = new NHibernateRepository<Product, int?>(); 
     NHibernateRepository<OrderHeader, int?> repo = new NHibernateRepository<OrderHeader, int?>(); 
     OrderHeader oh = new OrderHeader(); 
     oh.Comment = "Test Comment " + DateTime.Now.ToString(); 
     oh.Details = new List<OrderDetail>(); 
     for (int i = 0; i < 2; i++) { 
      oh.Details.Add(new OrderDetail 
      { 
       OrderHeader = oh, 
       Product = repoProduct.GetById(i + 3) 
      }); 
     } 

     repo.AddNew(oh); 
    } 

    private static void method2() { 
     NHibernateRepository<OrderHeader, int?> repoHeader = new NHibernateRepository<OrderHeader, int?>(); 
     OrderHeader oh = new OrderHeader(); 
     oh.Comment = "Test Comment " + DateTime.Now.ToString(); 
     repoHeader.Save(oh); 

     NHibernateRepository<OrderDetail, int?> repoDetail = new NHibernateRepository<OrderDetail, int?>(); 
     for (int i = 0; i < 2; i++) { 
      OrderDetail od = new OrderDetail 
      { 
       OrderHeaderId = oh.Id, 
       OrderHeader = oh, 
       ProductId = i + 3, 
       Product = new Product 
       { 
        Id = i + 3 
       }, 
      }; 

      repoDetail.AddNew(od); 
     } 
    } 

但是,对于这两种方法,OrderDetail表从不保存。我已经打开ShowSql()来查看在控制台上执行的SQL语句,没有生成SQL来保存OrderDetail表。

我到处搜索了很多搜索,无法得出清楚的结论。

任何人都有一些线索,我需要做什么来保存一个实体复合ID?

感谢

哈迪

回答

2

模型和映射都不正确。

删除OrderHeaderId产品编号的OrderDetail

然后,将复合标识应包括OrderHeader产品作为参考(我想用流利它KeyReference而不是KeyProperty;在XML它是key-propertykey-many-to-one代替)

然后,添加适当的Cascade设置,正如科尔所建议的那样。

使用范例:

using (var session = GetSessionFromSomewhere()) 
using (var tx = session.BeginTransaction()) 
{ 
    var orderHeader = new OrderHeader(); 
    ... 
    orderHeader.Details.Add(new OrderDetail 
          { 
           OrderHeader = orderHeader; 
           Product = session.Load<Product>(someProductId); 
          }); 
    session.Save(orderHeader); 
    tx.Commit(); 
} 

一切都在该块是需要

+0

我试过了,它仍然不会针对我的OrderDetail表生成插入语句。 – hardywang 2011-03-29 13:44:30

+0

@hardywang:查看添加的示例。你的代码也有几个问题。 – 2011-03-29 14:30:19

+0

@Diego,我已经更新了原始帖子中的代码来结合您的建议,我仍然无法获得保存的OrderDetail,根本没有生成SQL语句。 – hardywang 2011-03-29 17:32:09

2

我不认为复合-ID是什么原因造成你的问题。我认为这是您将OrderDetails映射到OrderHeader映射中的方式。

我想应该是这样的,而不是:

HasMany<OrderDetail>(x => x.Details).KeyColumn("HeaderID").Inverse().Cascade.AllDeleteOrphan(); 

编辑:

你应该听下面迭戈和改变你的映射:

public class OrderDetailMap : ClassMap<OrderDetail> { 
    public OrderDetailMap() { 
     Table("OrderDetail"); 

     CompositeId() 
      .KeyReference(x => x.Product, "ProductID") 
      .KeyReference(x => x.OrderHeader, "HeaderID"); 

     Version(x => x.CreatedOn).Column("CreatedOn").Generated.Always(); 
    } 
} 

代码你在你的上面的OrderDetails映射是什么导致你的错误“无效索引2这个SqlParameterCollection与Co UNT = 2“。

References<OrderHeader>(x => x.OrderHeader, "HeaderID").ForeignKey().Not.Nullable().Fetch.Join(); 
References<Product>(x => x.Product, "ProductID").ForeignKey().Not.Nullable(); 
+0

插入我试过了,它仍然不会针对我的OrderDetail表生成插入语句。 – hardywang 2011-03-29 13:43:17

+0

你说得对,我在OrderDetail映射中删除了addtional引用,我的代码又向前移动了一步。 NH生成的SQL语句如下:UPDATE OrderDetail SET WHERE ProductID = @ p0 AND HeaderID = @ p1 AND CreatedOn = @ p2,它有语法错误。在我重新从OrderDetail的版本映射后,现在一切正常。只是不知道为什么在这里的版本映射会导致有趣的事情。 – hardywang 2011-03-29 22:45:41

1

首先,您的OrderDetail映射错误:您可能无法多次映射一列。在这里你可以将它分配给composite-id,也可以有多对一的分配。你的composite-id可以(也应该)有2个多对一的属性,而不仅仅是值属性。

这是迭戈的回答你的最后的评论明显,也看到IndexOutOfRangeException Deep in the bowels of NHibernate

其次,你是在OrderHeader.Details集合,如果我还记得其中设置一个inverse正确表示方法1不会造成对的OrderDetail

+0

是不是反向()用于让另一侧来控制创建? – hardywang 2011-03-29 22:49:10