【问题标题】:Fluent NHibernate (with automapping) not saving join table values in many-to-manyFluent NHibernate(带自动映射)不以多对多保存连接表值
【发布时间】:2011-11-10 07:21:02
【问题描述】:

我不完全是 NHibernate 专家,所以这可能是对该部门缺乏了解。我有两个具有多对多关系的简单实体

public class Category
{
    public virtual int Id { get; private set; }
    public virtual string Name { get; set; }
    public virtual string Description { get; set; }
    public virtual bool Visible { get; set; }

    public virtual IList<Product> Products { get; set; }
}

public class Product
{
    public virtual int Id { get; private set; }
    public virtual string Name { get; set; }
    public virtual string Description { get; set; }
    public virtual decimal Price { get; set; }
    public virtual bool Visible { get; set; }
    public virtual IList<Category> Categories { get; set; }
}

我的配置是

    public static void BuildFactory()
    {
        _factory = Fluently.Configure()
            .Database(
                MsSqlConfiguration
                    .MsSql2008
                    .ConnectionString(_connectionString)
                    .ShowSql()
            )
            .Mappings(m =>
                m.AutoMappings.Add(
                    AutoMap.AssemblyOf<Database>()
                        .Where(t => t.Namespace == "FancyStore.Models.Catalog")
                        .Override<Product>(k => 
                            k.HasManyToMany(x => x.Categories)
                                .Table("ProductsToCategories")
                                    .ParentKeyColumn("Product_id")
                                    .ChildKeyColumn("Category_id")
                                .Cascade.AllDeleteOrphan())
                        .Override<Category>(k =>
                            k.HasManyToMany(x => x.Products)
                                .Table("ProductsToCategories")
                                    .ParentKeyColumn("Category_id")
                                    .ChildKeyColumn("Product_id")
                                .Cascade.AllDeleteOrphan().Inverse())
                )
            )
            .ExposeConfiguration(SetConfiguration)
            .BuildConfiguration()
            .BuildSessionFactory();
    }

所以我开始时没有覆盖(在一些谷歌搜索建议后添加它们),并使用 ExportSchema 生成我的架构。 ExportSchema 知道多对多关系(即使没有覆盖),因为它生成了一个连接表 (ProductsToCategories)。

我想添加一些简单的测试数据,所以我这样做了

        using (var db = new Repository<Category>())
        {
            for (int i = 0; i < 6; i++)
            {
                var cat = new Category
                {
                    Name = "Things" + i,
                    Description = "Things that are things",
                    Visible = true,
                    Products = new List<Product>()
                };

                for (int k = 0; k < 6; k++)
                {
                    var prod = new Product
                    {
                        Name = "Product" + k,
                        Description = "Product for " + cat.Name,
                        Visible = true,
                        Categories = new List<Category>(new[] { cat })
                    };
                    cat.Products.Add(prod);
                }
                db.Save(cat);
            }
        }

这会正确保存产品和类别,但不会保存连接表中的任何关系。查看对 category.Products 的调用生成的 sql,它实际上是在该连接表中查找产品(并且找不到任何产品,因为它是空的)

任何帮助将不胜感激:-)

编辑:删除了逆向,问题仍然存在:(

EDIT2

这里是目录和产品的映射文件(注意:这个名字有点傻,但这只是一种快速的原型交易)

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true">
  <class xmlns="urn:nhibernate-mapping-2.2" name="FancyStore.Models.Catalog.Product, FancyStore, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="`Product`">
    <id name="Id" type="System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Id" />
      <generator class="identity" />
    </id>
    <property name="Name" type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Name" />
    </property>
    <property name="Description" type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Description" />
    </property>
    <property name="Price" type="System.Decimal, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Price" />
    </property>
    <property name="Visible" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Visible" />
    </property>
    <bag cascade="all-delete-orphan" inverse="true" name="Categories" table="ProductsToCategories">
      <key>
        <column name="Product_id" />
      </key>
      <many-to-many class="FancyStore.Models.Catalog.Category, FancyStore, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
        <column name="Category_id" />
      </many-to-many>
    </bag>
  </class>
</hibernate-mapping>

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true">
  <class xmlns="urn:nhibernate-mapping-2.2" name="FancyStore.Models.Catalog.Category, FancyStore, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="`Category`">
    <id name="Id" type="System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Id" />
      <generator class="identity" />
    </id>
    <property name="Name" type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Name" />
    </property>
    <property name="Description" type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Description" />
    </property>
    <property name="Visible" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Visible" />
    </property>
    <bag cascade="all-delete-orphan" name="Products" table="ProductsToCategories">
      <key>
        <column name="Category_id" />
      </key>
      <many-to-many class="FancyStore.Models.Catalog.Product, FancyStore, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
        <column name="Product_id" />
      </many-to-many>
    </bag>
  </class>
</hibernate-mapping>

【问题讨论】:

  • 省略的SetConfiguration 方法有什么作用?此外,为了调试它可能有助于查看 Fluent NHibernate 生成的映射。添加这个:".Mappings(m => m.AutoMappings.Add(...).ExportTo(@"C:\mappings"))" 让它生成映射文件。
  • SetConfiguration 只是获取对配置对象的引用,我在其他地方使用它来使用一些 NH DDL 的东西。我会导出映射并在我有时间时发布它
  • 你解决问题了吗?
  • @HeavyWave:不,最后只是放弃了 fluent-nh。有点丢人,不过你打算怎么办……
  • 我想通了。您必须将一个关系设置为 Inverse() 并保存另一个实体,添加对两个实体的引用 (prod.Categories.Add() + cat.Products.Add()) 并提交事务。除非您提交对连接表的查询,否则不会生成。

标签: c# nhibernate fluent-nhibernate automapping


【解决方案1】:

我可能弄错了,但您似乎将双方的关系都标记为“反向”。

您应该只为关系的一方这样做,并确保在另一方调用Save

另外,我认为在您保存之前,双方的关系都必须有效(除了您已经在做的事情之外,还要将cat 添加到prod.Categories)。

诚然,我对这类关系的理解很模糊,但这是我几个月来的研究得出的结论(并解决了我自己的一些问题)。

【讨论】:

  • 我在我的产品初始化程序中得到了 Categories = new List(new[] { cat }),这是“创建新列表”的简写,并添加了一个新的类别数组用一个元素(猫)给它”。我尝试删除两个逆,并将其添加到一侧或另一侧,但没有任何效果。 :( 不过感谢您的回答,我很确定我想反转类别,所以我只调用保存产品。(一旦我解决了这个问题)
  • 糟糕,很抱歉,应该更仔细地阅读您的代码(错过了初始化程序)。不过,切线,您也可以只执行 new List&lt;Category&gt;() { cat } 并跳过数组部分。
【解决方案2】:

@Matt Briggs,我想通了。您必须将一个关系设置为 Inverse() 并保存另一个实体,添加对两个实体的引用 (prod.Categories.Add() + cat.Products.Add()) 并提交事务。除非您提交连接表的查询,否则不会生成。 – HeavyWave 5 月 13 日 12:43

【讨论】:

    【解决方案3】:

    您在关联的错误一侧有逆。逆的意思是“对方管理这个关联”。

    您需要删除类别上的 Inverse 并将其放在 Product 上,或者将 Product 更改为“拥有”实体,如下所示:

    //make sure to add it to BOTH sides of the association
    product.Categories.Add(cat);
    cat.Products.Add(product);
    
    db.Save(cat);  //if Product has Inverse
    /* or */
    db.Save(prod); //if Category has Inverse
    

    【讨论】:

      【解决方案4】:

      如果您在自动映射之前先应用 Product 和 Category 的流畅映射会怎样。例如,

      public class Product
      {
          public virtual int Id { get; private set; }
          public virtual string Name { get; set; }
          public virtual string Description { get; set; }
          public virtual decimal Price { get; set; }
          public virtual bool Visible { get; set; }
          public virtual IList<Category> Categories { get; set; }
      
          public Product()
          {
              Categories = new List<Category>();
          }
      }
      
      public class ProductMap : ClassMap<Product>
      {
          Id(x => x.Id);
          Map(x => x.Name);
          Map(x => x.Description);
          Map(x => x.Price);
          Map(x => x.Visible);
          HasManyToMany(x => x.Categories)
            .Table("ProductsToCategories")
            .ParentKeyColumn("Product_id")
            .ChildKeyColumn("Category_id")
            .Cascade.AllDeleteOrphan();
      }
      
      public class Category
      {
          public virtual int Id { get; private set; }
          public virtual string Name { get; set; }
          public virtual string Description { get; set; }
          public virtual bool Visible { get; set; }
          public virtual IList<Product> Products { get; set; }
      
          public Category()
          {
              Products = new List<Product>();
          }
      }
      
      public class CategoryMap : ClassMap<Category>
      {
          Id(x => x.Id);
          Map(x => x.Name);
          Map(x => x.Description);
          Map(x => x.Visible);
          HasManyToMany(x => x.Products)
            .Table("ProductsToCategories")
            .ParentKeyColumn("Category_id")
            .ChildKeyColumn("Product_id")
            .Cascade.AllDeleteOrphan().Inverse();
      }
      

      有配置,

      public static void BuildFactory()
      {
          _factory = Fluently.Configure()
              .Database(
                  MsSqlConfiguration
                      .MsSql2008
                      .ConnectionString(_connectionString)
                      .ShowSql()
              )
              .Mappings(m => {
                  m.FluentMappings.AddFromAssemblyOf<Database>();
                  m.AutoMappings.Add(
                    AutoMap.AssemblyOf<Database>()
                    .Where(t => t.Namespace == "FancyStore.Models.Catalog"));
              })
              .ExposeConfiguration(SetConfiguration)
              .BuildConfiguration()
              .BuildSessionFactory();
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-01-29
        • 1970-01-01
        • 2010-12-10
        • 2012-03-03
        • 1970-01-01
        相关资源
        最近更新 更多