Introduction
In many systems, it’s common modeling tables that have many-to-many relationships, this structure usually has three tables, two have primary keys and the third has two columns to relate these primary keys. The columns in the third table are, at the same time, primary key and foreign key. When mapping in Entity Framework designer, three tables becomes two entities.
Using the Code
The project has two layers to maintain as simple as possible:
Model
: Layer that connects with database UI.Web
: ASP.NET WebForm
project that references to Model
and presents one aspx page with controls to demonstrate operations in database
Model and Mapping
I use just three tables in the example, Supplier
, Product
and ProductSupplier
. In that structure, one supplier can supply one or many products and one product can be supplied by one or many suppliers.
The database diagram has three tables:
But when mapping to EntityFramework
, only two entities are present.
Mapping entities add navigation property to represent that relation.
namespace ManyToMany.Model
{
using System;
using System.Collections.Generic;
public partial class Product
{
public Product()
{
this.Supplier = new HashSet<supplier>();
}
public long ProductID { get; set; }
public string ProductName { get; set; }
public virtual ICollection<supplier> Supplier { get; set; }
}
}
namespace ManyToMany.Model
{
using System;
using System.Collections.Generic;
public partial class Supplier
{
public Supplier()
{
this.Product = new HashSet<product>();
}
public long SupplierID { get; set; }
public string SupplierName { get; set; }
public virtual ICollection<product> Product { get; set; }
}
}
In the next sections, I will explain how to insert
, delete
, update
and select
this association table using LINQ to Entities.
Insert
There are two situations to insert data in tables that has a relationship many-to-many.
First, when data do not exist in tables, add instances to context, add an instance to navigation property and call SaveChanges
method from context. That is possible because Entity Framework, at the time of insert
, puts primary key value (if Identity
, AutoIncrement
) in correspondent entity’s property inserted.
public void InsertWithoutData(Product prod, Supplier sup)
{
using (ManyToManyEntities conn = new ManyToManyEntities())
{
conn.Product.Add(prod);
conn.Supplier.Add(sup);
prod.Supplier.Add(sup);
conn.SaveChanges();
}
}
Second case, data already exists in tables and it's necessary to relate them, pass the primary key to two tables/entity, add and attach to context object, add instance to entity navigation property and finally call SaveChanges
method.
public void InsertWithData(long productID, long supplierID)
{
using (ManyToManyEntities conn = new ManyToManyEntities())
{
Product p = new Product { ProductID = productID };
conn.Product.Add(p);
conn.Product.Attach(p);
Supplier s = new Supplier { SupplierID = supplierID };
conn.Supplier.Add(s);
conn.Supplier.Attach(s);
p.Supplier.Add(s);
conn.SaveChanges();
}
}
Delete
To delete relationship, instead of call Remove
from context, we need to call it from navigation property.
public void DeleteRelationship(long productID, long supplierID)
{
using (ManyToManyEntities conn = new ManyToManyEntities())
{
var product = conn.Product.FirstOrDefault(p => p.ProductID == productID);
var supplier = conn.Supplier.FirstOrDefault(s => s.SupplierID == supplierID);
product.Supplier.Remove(supplier);
conn.SaveChanges();
}
}
Update
The update
statement compounds two statements, delete
and insert
, call delete
and insert
methods then.
public void UpdateRelationship(long oldProductID, long oldSupplierID,
long newProductID, long newSupplierID)
{
DeleteRelationship(oldProductID, oldSupplierID);
InsertWithData(newProductID, newSupplierID);
}
Select
The select
statement is a little bit different than normal, at first from
use context to returns the first entity, then entity instance accesses the navigation property at the second from
, this identifies that data comes from relationship table, but only ids do not bring useful information in this case, it’s necessary join
to return useful data, or product or supplier.
DTOGenericObject
is just an object to transport data from database to objects in webforms.
public List<dtogenericobject> GetProductBySupplier(long supplierID)
{
using (ManyToManyEntities conn = new ManyToManyEntities())
{
var result = (
from a in conn.Supplier
from b in a.Product
join c in conn.Product on b.ProductID equals c.ProductID
where a.SupplierID == supplierID
select new DTOGenericObject
{
ID = c.ProductID,
Name = c.ProductName
}).ToList();
return result;
}
}
Points of Interest
When mapping association tables using Entity Framework, three tables involved become two entities. All operations should be with navigation property that was added in entities.