Esiste un modo migliore per modellare una relazione molti-a-molti sulla stessa tabella con Entity Framework?

4

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");
            });
    }
}
    
posta Kristof Claes 03.04.2013 - 12:10
fonte

2 risposte

5

Segui una buona progettazione del database e i problemi inizieranno a diventare più semplici, non pensare al framework delle entità quando progetti le strutture del tuo database. Crea la tabella dei prodotti e crea la tabella dei prodotti correlata con il prodotto di origine e gli ID dei prodotti correlati. L'obbligo per il collegamento di essere bidirezionali è solo una regola aziendale da gestire con l'applicazione sulla creazione di prodotti correlati per inserire sempre due righe nella tabella di mapping. Tutto rimane bello e semplice in questo modo.

    
risposta data 03.04.2013 - 14:24
fonte
0

Mi sono sempre sentito strano creare entità esplicite per le mie tabelle many-to-many. EF cerca di associare le tabelle many-to-many con alcune tabelle parent. Tuttavia, questo di solito non era "abbastanza buono". O non aveva senso che le proprietà apparissero nell'entità genitore o le mie tabelle molti-a-molti fossero entità a sé stanti.

Ero costantemente in lotta con EF, dovendo rimuovere le associazioni ogni volta che aggiornavo il mio modello in base al database. Tuttavia, penso che ne valesse la pena.

Questo potrebbe essere il caso in cui la tabella ProductaMapping è un'entità esplicita con un buon design.

    
risposta data 03.04.2013 - 23:04
fonte

Leggi altre domande sui tag