Como mapear relacionamentos de banco de dados

Você pode codificar como referências de propriedade em sua classe de entidade todas as relações de dados que serão sempre as mesmas. No banco de dados de exemplo Northwind, por exemplo, como os clientes geralmente fazem os pedidos, há sempre uma relação no modelo entre os clientes e seus pedidos.

O LINQ to SQL define um atributo AssociationAttribute para ajudar a representar essas relações. Esse atributo é usado junto com os tipos EntitySet<TEntity> e EntityRef<TEntity> para representar o que seria um relacionamento de chave estrangeira em um banco de dados. Para obter mais informações, consulte a seção Atributo de Associação do Mapeamento baseado em atributo.

Observação

Os valores de propriedade de armazenamento AssociationAttribute e ColumnAttribute diferenciam maiúsculas de minúsculas. Por exemplo, verifique se os valores usados no atributo para a propriedade AssociationAttribute.Storage coincidem maiúsculas e minúsculas para os nomes de propriedades correspondentes usados em outro lugar no código. Isso se aplica a todas as linguagens de programação do .NET, mesmo as que normalmente não diferenciam maiúsculas de minúsculas, incluindo o Visual Basic. Para obter mais informações sobre a propriedade Storage, consulte DataAttribute.Storage.

A maioria da relações são um-para-muitos, como no exemplo mais adiante neste tópico. Você também pode representar relações um-para-um e muitos-para-muitos da seguinte maneira:

  • Um-para-um: representa esse tipo de relação incluindo EntitySet<TEntity> em ambos os lados.

    Por exemplo, considere uma relação Customer-SecurityCode, criada de modo que o código de segurança do cliente não seja encontrado na tabela Customer e possa ser acessado somente por pessoas autorizadas.

  • Muitos-para-muitos: em relações muitos-para-muitos, a chave primária da tabela de vínculo (também chamada de tabela de junção) é formada geralmente por uma composição de chaves estrangeiras das outras duas tabelas.

    Por exemplo, considere uma relação muitos-para-muitos Employee-Project formada com a tabela de vínculo EmployeeProject. O LINQ to SQL requer que esse relacionamento seja modelado usando três classes: Employee, Project e EmployeeProject. Nesse caso, alterar a relação entre um Employee e um Project pode parecer exigir uma atualização da chave primária EmployeeProject. No entanto, essa situação é melhor modelada como excluindo um EmployeeProject existente e criando um novo EmployeeProject.

    Observação

    As relações em bancos de dados relacionais são normalmente modeladas como os valores de chave estrangeira que referenciam as chaves primárias em outras tabelas. Para navegar entre elas, você associa explicitamente as duas tabelas usando uma operação de junção relacional.

    No entanto, objetos no LINQ to SQL fazem referência entre si usando as referências de propriedade ou coleções de referências que você navega usando a notação dot.

Exemplo 1

No exemplo de um-para-muitos a seguir, a classe Customer tem uma propriedade que declara a relação entre os clientes e seus pedidos. A propriedade Orders é do tipo EntitySet<TEntity>. Este tipo significa que essa relação é de um-para-muitos (um cliente para vários pedidos). A propriedade OtherKey é usada para descrever como essa associação é realizada, a saber, especificando o nome da propriedade na classe relacionada a ser comparada com essa. Nesse exemplo, a propriedade CustomerID é comparada, assim como um join de banco de dados compararia esse valor de coluna.

Observação

Se estiver usando o Visual Studio, poderá usar o Object Relational Designer para criar uma associação entre classes.

[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

Exemplo 2

Você também pode reverter a situação. Em vez de usar a classe Customer para descrever a associação entre clientes e pedidos, você pode usar a classe Order. A classe Order usa o tipo EntityRef<TEntity> para descrever a relação de volta para o cliente, como no exemplo de código a seguir.

Observação

A classe EntityRef<TEntity> dá suporte a carregamento adiado. Para obter mais informações, confira Carregamento adiado versus imediato.

[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

Confira também