代码之家  ›  专栏  ›  技术社区  ›  Matt Briggs

Fluent NHibernate(带自动映射)不以多对多方式保存联接表值

  •  4
  • Matt Briggs  · 技术社区  · 15 年前

    我并不完全是一名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语句,它实际上是在该联接表中查找产品(并且找不到任何产品,因为它是空的)

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

    :已删除反向,问题仍在发生:(

    :

    以下是目录和产品的映射文件(注意:名称有点傻,但这只是一个简单的交易)

    <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>
    
    4 回复  |  直到 13 年前
        1
  •  2
  •   Sapph    15 年前

    你应该只为关系的一方这样做,并确保你打电话给对方 Save 另一方面。

    cat prod.Categories ,除了你已经在做的事情之外)。

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

        2
  •  2
  •   Matt Briggs    14 年前

    @马特·布里格斯,我知道了。您必须将一个关系设置为Inverse()并保存另一个实体,添加对两个实体的引用(prod.Categories.add()+cat.Products.add())并提交事务。除非您提交,否则将不会生成联接表的查询。5月13日12时43分的巨浪

        3
  •  0
  •   Ben Scheirman    15 年前

    你在协会的错误方面有相反的看法。反向表示“另一方管理此关联”。

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

    //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
  •  0
  •   Ajw    14 年前

    如果在自动映射之前,首先为产品和类别应用fluent映射,会怎么样。例如

    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();
    }