Fluent NHibernate (con automapping) non risparmiando uniscono i valori della tabella in molti-a-molti
-
19-09-2019 - |
Domanda
Io non sono esattamente un esperto di NHibernate, quindi questo potrebbe essere una mancanza di comprensione in quel reparto. Ho due entità semplici con una relazione molti-a-molti
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; }
}
e
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; }
}
La mia configurazione è
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();
}
Così ho iniziato senza le sostituzioni (li aggiunto più tardi, dopo un po 'googling suggerito), e sto generando il mio schema con ExportSchema. ExportSchema conosce la relazione molti a molti (anche senza le sostituzioni), perché ha generato una tabella di join (ProductsToCategories
).
ho voluto aggiungere alcuni dati di test semplici, così ho fatto in questo modo
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);
}
}
Questo consente di risparmiare prodotti e categorie correttamente, ma non salva nessuno dei rapporti nella tabella join. Guardando lo sql una chiamata a category.Products genera, in realtà è alla ricerca in tale tabella unirsi per i prodotti (e cant trovare qualsiasi, dal momento che è vuoto)
Qualsiasi aiuto sarebbe molto apprezzato: -)
Modifica : Rimosso un inverso, problema ancora accade: (
EDIT2 :
Qui ci sono le file di mapping per il catalogo e di prodotto (nota: il nome è una specie di sciocco, ma questo è solo un rapido prototipo-ish tipo di accordo)
<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>
Soluzione 2
@ Matt Briggs, ho capito. È necessario impostare una relazione come Inverso () e salvare l'altra entità, aggiungere riferimenti a entrambe le entità (prod.Categories.Add (+) cat.Products.Add ()) e il commit della transazione. A meno che non si impegnano non verrà generato la query per la tabella join. - HeavyWave 13 maggio alle 12:43
Altri suggerimenti
potrei sbagliarmi, ma sembra che hai segnato il rapporto come "inversa" per entrambe le parti.
Si dovrebbe fare questo per un solo lato della relazione, e assicuratevi di chiamare Save
sull'altro.
Inoltre, credo che il rapporto deve essere valido su entrambi i lati prima di salvare (aggiungere cat
a prod.Categories
, in aggiunta a quello che si sta già facendo).
La mia comprensione di questo tipo di relazioni è certamente confuso, ma questo è ciò che la mia ricerca nel corso dei mesi ha messo a punto (e risolto alcuni dei miei problemi).
Hai inversa sul lato sbagliato dell'associazione. Inverse significa "l'altro lato gestisce questa associazione".
Quello che vi serve è o rimuovere Inverse su Categoria e metterlo sul prodotto, o il cambiamento del prodotto ad essere l'entità 'possedere', in questo modo:
//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
Che dire se si dovesse applicare le mappature FLUENT per prodotto e Categoria prima, prima della automapping. Ad esempio,
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();
}
con la configurazione,
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();
}