Il contesto
Stiamo costruendo un'applicazione Web utilizzando Entity Framework 5.0. Uno dei requisiti è che dovrebbe essere possibile per gli amministratori collegare i prodotti correlati in modo che quando qualcuno naviga su un prodotto possiamo rendere un elenco " Potrebbero piacerti anche questi prodotti: ".
Poiché un prodotto può essere collegato a molti prodotti, abbiamo bisogno di una relazione molti-a-molti per questo.
La tabella nel database potrebbe essere simile a questa:
_________________________
| LinkedProducts |
|-------------------------|
| ProductId1 | ProductId2 |
|------------|------------|
| 1 | 2 |
|------------|------------|
| 1 | 3 |
|------------|------------|
| 2 | 4 |
|------------|------------|
Un ulteriore requisito è che il collegamento sia bidirezionale. Ciò significa che con i dati di esempio della tabella sopra, quando si passa al prodotto 2, è necessario ottenere i prodotti 1 e 4 nell'elenco. Quindi, per un dato ProductId
, dovresti essere in grado di ottenere i suoi prodotti collegati dalla colonna ProductId1
e ProductId2
.
Cosa ho scoperto
L'entità prodotto:
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public virtual ICollection<Product> References { get; set; }
public virtual ICollection<Product> ReferencedBy { get; set; }
public Product()
{
References = new List<Product>();
ReferencedBy = new List<Product>();
}
}
La mappatura del prodotto:
public class ProductMapping : EntityTypeConfiguration<Product>
{
public ProductMapping()
{
HasKey(t => t.ProductId);
Property(t => t.Name).IsRequired().HasMaxLength(150);
ToTable("Products");
Property(t => t.ProductId).HasColumnName("ProductId");
Property(t => t.Name).HasColumnName("Name");
HasMany(x => x.References).WithMany(x => x.ReferencedBy).Map(map =>
{
map.ToTable("LinkedProducts");
map.MapLeftKey("ProductId1");
map.MapRightKey("ProductId2");
});
}
}
Tutto funziona. Per visualizzare l'elenco di prodotti collegati per un determinato prodotto, posso semplicemente ottenere l'unione di References
e ReferencedBy
:
var product = db.Find(2);
var linkedProducts = product.References.Union(product.ReferencedBy);
Il problema
Come ho detto, funziona, ma non mi piace e mi stavo chiedendo se c'è un modo migliore per gestire una situazione come questa quando si lavora con Entity Framework.
La soluzione
Mi è piaciuto il suggerimento di Ryathal di aggiungere due record al database durante il collegamento dei prodotti. Quindi, quando collego il prodotto 1 al prodotto 2, ora inserisco due record: { 1, 2 }
e { 2, 1 }
.
Questo mi consente anche di rimuovere la raccolta ReferencedBy
nella classe Product
in modo che rimanga una sola collezione: References
.
Per collegare ai prodotti:
product1.References.Add(product2);
product2.References.Add(product1);
Per rimuovere il collegamento tra i prodotti:
product1.References.Remove(product2);
product2.References.Remove(product1);
Ecco la mia nuova classe Product
con la mappatura:
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public virtual ICollection<Product> References { get; set; }
public Product()
{
References = new List<Product>();
}
}
La mappatura del prodotto:
public class ProductMapping : EntityTypeConfiguration<Product>
{
public ProductMapping()
{
HasKey(t => t.ProductId);
Property(t => t.Name).IsRequired().HasMaxLength(150);
ToTable("Products");
Property(t => t.ProductId).HasColumnName("ProductId");
Property(t => t.Name).HasColumnName("Name");
HasMany(x => x.References).WithMany().Map(map =>
{
map.ToTable("LinkedProducts");
map.MapLeftKey("ProductId1");
map.MapRightKey("ProductId2");
});
}
}