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 tabella Customer 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 classe EmployeeProject della tabella di collegamento. In LINQ to SQL è necessario che tale relazione venga modellata usando tre classi: Employee, Project e EmployeeProject. In questo caso la modifica della relazione tra Employee e Project può apparentemente richiedere un aggiornamento della chiave primaria di EmployeeProject. In questa situazione, tuttavia, è preferibile modellare la relazione eliminando una classe EmployeeProject esistente e creando una nuova classe EmployeeProject.

    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

Vedi anche