Procedura: eseguire il mapping di relazioni tra database
Qualsiasi relazione tra i dati, che rimane prevedibilmente sempre la stessa, può essere codificata come riferimenti alla proprietà nella classe di entità. Nel database di esempio Northwind, ad esempio, poiché in genere i clienti effettuano ordini, nel modello è sempre presente una relazione tra clienti e ordini.
LINQ to SQL definisce un attributo AssociationAttribute per rappresentare tali relazioni. Questo attributo viene utilizzato insieme ai tipi EntitySet<TEntity> e EntityRef<TEntity> per rappresentare quello che in un database sarebbe una relazione di chiave esterna. Per altre informazioni, vedere la sezione relativa all'attributo Association di Mapping basato su attributi.
Nota
I valori delle proprietà di archiviazione AssociationAttribute e ColumnAttribute rispettano la distinzione tra maiuscole e minuscole. Verificare, ad esempio, che per i valori dell'attributo della proprietà AssociationAttribute.Storage venga usata la stessa combinazione di maiuscole e minuscole adoperata per i nomi di proprietà corrispondenti usati in altri punti del codice. Ciò si applica a tutti i linguaggi di programmazione .NET, anche a quelli che in genere non distinguono tra maiuscole e minuscole, tra cui Visual Basic. Per altre informazioni sulla proprietà di archiviazione, vedere DataAttribute.Storage.
La maggior parte delle relazioni è di tipo uno-a-molti, come nell'esempio riportato più avanti in questo argomento. È anche possibile rappresentare relazioni uno-a-uno e molti-a-molti come segue:
Uno-a-uno: per rappresentare questo tipo di relazione includere EntitySet<TEntity> su entrambi i lati.
Ad esempio, si consideri una relazione
Customer
-SecurityCode
creata in modo che il codice di sicurezza del cliente non venga trovato nella tabellaCustomer
e a cui solo le persone autorizzate possano accedere.Molti-a-molti: in questo tipo di relazioni la chiave primaria della tabella di collegamento è spesso formata da una combinazione di chiavi esterne delle altre due tabelle.
Ad esempio, si consideri una relazione molti-a-molti
Employee
-Project
creata usando la classeEmployeeProject
della tabella di collegamento. In LINQ to SQL è necessario che tale relazione venga modellata usando tre classi:Employee
,Project
eEmployeeProject
. In questo caso la modifica della relazione traEmployee
eProject
può apparentemente richiedere un aggiornamento della chiave primaria diEmployeeProject
. In questa situazione, tuttavia, è preferibile modellare la relazione eliminando una classeEmployeeProject
esistente e creando una nuova classeEmployeeProject
.Nota
Le relazioni nei database relazionali vengono in genere modellate come valori di chiave esterna che fanno riferimento a chiavi primarie in altre tabelle. Per spostarsi tra di esse, associare in modo esplicito le due tabelle usando un'operazione di join relazionale.
Gli oggetti in LINQ to SQL, d'altra parte, fanno riferimento reciproco usando riferimenti alle proprietà o raccolte di riferimenti in cui è possibile spostarsi usando la notazione del punto.
Esempio 1
Nell'esempio uno-a-molti seguente, alla classe Customer
è associata una proprietà che dichiara la relazione tra clienti e ordini. La proprietà Orders
è di tipo EntitySet<TEntity>. Questo tipo significa che si tratta di una relazione uno-a-molti (un cliente a molti ordini). La proprietà OtherKey viene usata per descrivere come viene eseguita questa associazione, ovvero specificando il nome della proprietà nella classe correlata che dovrà essere confrontata con questa. In questo esempio viene confrontata la proprietà CustomerID
, esattamente come verrebbe confrontato il valore di tale colonna con un'operazione join in un database.
Nota
Se si usa Visual Studio, è possibile usare Object Relational Designer per creare un'associazione tra classi.
[Table(Name = "Customers")]
public partial class Customer
{
[Column(IsPrimaryKey = true)]
public string CustomerID;
// ...
private EntitySet<Order> _Orders;
[Association(Storage = "_Orders", OtherKey = "CustomerID")]
public EntitySet<Order> Orders
{
get { return this._Orders; }
set { this._Orders.Assign(value); }
}
}
<Table(Name:="Customers")> _
Public Class Customer
<Column(IsPrimaryKey:=True)> _
Public CustomerID As String
' ...
Private _Orders As EntitySet(Of Order)
<Association(Storage:="_Orders", OtherKey:="CustomerID")> _
Public Property Orders() As EntitySet(Of Order)
Get
Return Me._Orders
End Get
Set(ByVal value As EntitySet(Of Order))
Me._Orders.Assign(value)
End Set
End Property
End Class
Esempio 2
È inoltre possibile invertire la situazione. Anziché usare la classe Customer
per descrivere l'associazione tra clienti e ordini, è possibile usare la classe Order
. La classe Order
usa il tipo EntityRef<TEntity> per descrivere la relazione con la classe Customer, come nell'esempio di codice seguente.
Nota
La classe EntityRef<TEntity> supporta il caricamento posticipato. Per altre informazioni, vedere Caricamento posticipato e immediato.
[Table(Name = "Orders")]
public class Order
{
[Column(IsPrimaryKey = true)]
public int OrderID;
[Column]
public string CustomerID;
private EntityRef<Customer> _Customer;
[Association(Storage = "_Customer", ThisKey = "CustomerID")]
public Customer Customer
{
get { return this._Customer.Entity; }
set { this._Customer.Entity = value; }
}
}
<Table(Name:="Orders")> _
Public Class Order
<Column(IsPrimaryKey:=True)> _
Public OrderID As Integer
<Column()> _
Public CustomerID As String
Private _Customer As EntityRef(Of Customer)
<Association(Storage:="Customer", ThisKey:="CustomerID")> _
Public Property Customer() As Customer
Get
Return Me._Customer.Entity
End Get
Set(ByVal value As Customer)
Me._Customer.Entity = value
End Set
End Property
End Class