Usando LINQ To SQL com WCF


Para aqueles que trabalham com LINQ To SQL e querem expor as entidades geradas pela ferramenta via WCF, terá que tomar alguns cuidados. Quando essas entidades não mantém um relacionamento, algo que é bem díficil, você consegue retorná-las a partir das operações que o serviço irá disponibilizar.

O problema começa a aparecer quando você deseja enviar para os clientes, entidades que possuem relacionamentos com outras entidades. Um exemplo clássico e que ilustra bem o cenário, é quando temos categorias e produtos, onde cada produto deverá pertencer à somente uma única categoria. Neste caso, temos uma relação direta entre elas. Mas o ponto que torna isso difícil é que o LINQ To SQL cria uma relação bidirecional entra elas. Isso quer dizer que a entidade Categoria terá uma propriedade chamada Produtos, que como o próprio nome diz, retorna a coleção dos produtos daquela categoria; além disso, a classe Produto expõe uma propriedade chamada Categoria, que retorna a instância de uma categoria em qual o produto está contido.

Quando falamos em orientação à objetos, isso é perfeitamente válido e comum. O problema ocorre quando você tenta serializar essa estrutura a partir do WCF. Como eu comentei neste outro post, o WCF tem um comportamento diferente quando há referências circulares, e não conseguirá fazer a serialização porque ele ficará em uma espécie de loop, pois com há referência bidirecional, ao serializar uma categoria, ela serializa os respectivos produtos, e para cada produto a sua respectiva categoria, e para esta categoria os seus produtos, e por aí vai. O serviço roda sem maiores problemas, mas você terá uma exceção quando quando o cliente tentar acessá-lo.

Analisando a imagem abaixo, podemos visualizar a estrutura das classes que compõem o exemplo, e logo ao lado, você pode reparar nas propriedades do arquivo (superfície) DBML, verá que existe uma propriedade chamada Serialization, que pode receber apenas dois valores: None e Unidirectional. O primeiro deles permite que as propriedades das entidades sejam serializadas de acordo com as regras impostas pelo serializador padrão do WCF, que graças a possibilidade de serializar qualquer propriedade, mesmo que elas não estejam decoradas com os atributos DataContractAttribute e DataMemberAttribute (POCO). A segunda opção, Unidirectional, faz com que ele somente consiga serializar o relacionamento em uma única direção para evitar as referências circulares. No nosso caso, teremos a entidade Categoria com a propriedade Produtos, mas a classe Produto não terá uma propriedade que define a sua categoria.

Alterando a opção de serialização para Unidirectional, a forma que você efetua a consulta também terá que mudar. Isso fará com que o LINQ To SQL não consiga trazer os dados (produtos) relacionados aquela categoria, algo que é transparente quando estamos utilizando o LINQ To SQL diretamente. Para conseguir fazer com que os dados relacionados também sejam carregados, temos que recorrer a classe DataLoadOptions, como é mostrado abaixo:

public Categoria[] RecuperarCategorias()
{
    using (DBContextDataContext ctx = new DBContextDataContext())
    {
        DataLoadOptions opts = new DataLoadOptions();
        opts.LoadWith<Categoria>(c => c.Produtos);
        ctx.LoadOptions = opts;

        return (fromin ctx.Categorias select c).ToArray();
    }
}

Mas como disse acima, isso funcionará mas você perderá a navegação bidirecional. Felizmente, podemos recorrer à propriedade boleana IsReference, que é exposta pelo atributo DataContractAttribute, definindo isso na classe Categoria. Isso permitirá a criação da navegação bidirecional, mas há um trabalho manual a ser feito para que isso funcione. Quando você muda a propriedade Serialization para None, nenhuma das propriedades é decorada com o atributo DataContractAttribute/DataMemberAttribute; já se definir essa propriedade para Unidirectional, as propriedades que são problemáticas, não estarão decoradas com o atributo DataMemberAttribute.

Sendo assim, o exemplo final fica como é mostrado abaixo, conseguindo ter no cliente, a navegação bidirecional. Obviamente que alguns membros foram omitidos por questões de espaço.

[Table(Name = “dbo.Categoria”)]
[DataContract(IsReference = true)]
public partial class Categoria
{
    [DataMember]
    [Column(…)]
    public int CategoriaId

    [DataMember]
    [Column(…)]
    public string Nome

    [DataMember]
    [Association(…)]
    public EntitySet<Produto> Produtos
}

[Table(Name = “dbo.Produto”)]
public partial class Produto
{
    [Column(…)]
    public int ProdutoId

    [Column(…)]
    public int CategoriaId

    [Column(…)]
    public string Nome

    [Association(…)]
    public Categoria Categoria
}

Publicidade

3 comentários sobre “Usando LINQ To SQL com WCF

  1. Excelente artigo Israel, muito esclarecedor.

    Mas ainda fiquei com uma dúvida: e no caso de um método hipotético que retorne o objeto Categoria dado o nome de um produto?

    Exemplo: Categoria ObterCategoriaDoProduto(string nomeProduto) { … };

    Em meus testes, o utilitário ‘wcftestclient’ não retorna nenhum valor quando invoco o serviço.

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s