Boas práticas em Linq To Sql

Estou tentando descobrir uma boa maneira de lidar com o Linq To Sql. Determinar um padrão que seja robusto e flexível no uso desta ferramenta. Mas para se usar uma ferramenta da melhor maneira possível é preciso conhecê-la!

Funcionamento

Não colocarei aqui instruções para uso básico do Linq, mesmo porque já existem excelentes tutoriais por aí (aos interessados na tecnologia, recomendo assinar o feed do Scott Guthrie).

Para o Linq To Sql funcionar devemos criar um arquivo de classes Linq to Sql (dbml). Este XML serve de metacódigo, utilizado para criar dinamicamente várias classes de forma automatizada. Dentre estas classes é criada também um
Unit of Work e um Data gateway em um único objeto: o DataContext.

O DataContext é responsável por guardar/conter/seguir as alterações em todos objetos Linq (Unit of Work) e por conversar com o banco de dados (Data Gateway).

É através do DataContext que conversamos com o banco de dados. E o DataContext gosta de conversar sobre objetos.

Imaginemos uma tabela Abacaxi em nosso banco de dados, com as colunas id, responsavel, tarefa e datalimite.

Tabela Abacaxi

Mapeando através do dbml, cria-se a classe Abacaxi no sistema, com as seguintes propriedades: id, responsavel, tarefa e datalimite.

Abacaxi Linq Object

Simples não? O Linq To Sql te dá o Abacaxi, pronto para descascar.

Problemas

Mesmo após a curva inicial de aprendizado, em que devemos nos acostumar com a sintaxe Linq, e a reaprender a escrever  consultas triviais em SQL, existem outros problemas que permanecem.

Cada objeto Linq To Sql se refere à uma entrada em uma tabela de banco de dados. Como ele serve primariamente para transportar dados, não contendo métodos próprios,  podemos chamá-lo de DTO (Data Transfer Object).

Este DTO é de díficil utilização, quando desconectados. Digo, se você consulta a tabela Abacaxi através do Linq To Sql, você consegue uma monte de objetos Abacaxi’s.

Tome um destes objetos Abacaxis. Vamos batizá-lo de abac. Eu pego o abac e jogo o DataContext que o criou fora. Atualizo o abac. Instancio um novo DataContext, do mesmo tipo que o anterior.

Este abac deve ser conectado a um DataContext de uma maneira muito complicada. Muito mais complicada do que deveria.

Há quem diga que, por esta e outras,  o Linq To Sql não está pronto para uma aplicação em  camadas (N-tier),  situação  atendida plenamente por ferramentas como o Hibernate.

Se mesmo assim você quser utilizar o Linq, já existe uma excelente discussão sobre as possibilidades de se arquitetar seu projeto com o uso do Linq To Sql em mente.

Meu uso

No projeto em que estou trabalhando, utilizamos o Linq To Sql sem muitas dores de cabeça. No momento é esta a arquitetura empregada:

  • Para cada subsistema, um DBML;
  • Para cada tabela, uma classe de negócios;
  • Para cada view no BD, uma classe correspondente (para cada coluna uma propriedade);
  • Cada objeto de negócios, uma instância do DataContext.

A classe de negócios acaba ficando bem parecida com uma DAL, com a importante diferença dela se utilizar do Linq To Sql para intermediar o acesso ao BD.

Segue o esqueleto da classe de negócios:

public class AbacaxiBLL: ITestableCrud, IDisposable
{
    protected ExemploDataContext dc;

    public AbacaxiBLL()
    {
        dc = new ExemploDataContext();
    }

    public Abacaxi Le(int id)
    {
        throw new NotImplementedException();
    }

    public Abacaxi Adiciona(Abacaxi obj)
    {
        throw new NotImplementedException();
    }

    public Abacaxi Atualiza(Abacaxi obj)
    {
        throw new NotImplementedException();
    }

    public void Exclui(int id)
    {
        throw new NotImplementedException();
    }

    public void Dispose()
    {
        throw new NotImplementedException();
    }
}

O corpo do método Adiciona fica assim então:

public Abacaxi Adiciona(Abacaxi obj)
{
    dc.Abacaxis.InsertOnSubmit(obj);
    dc.SubmitChanges();
    return obj;
}

Isto te dá a liberdade de operar com o objeto Abacaxi obj antes de inserí-lo no BD.

E eu sempre tomo o cuidado de implementar a interface IDisposable, o que te obriga a criar o seguinte método:

public void Dispose()
{
    dc.Dispose();
}

No final, o uso deste objeto de negócio é bem simples:

using (AbacaxiBLL bll = new AbacaxiBLL())
{
    Abacaxi a = new Abacaxi();
    a.responsavel = "Seiti Yamashiro";
    a.tarefa = "Criar um post sobre o Linq To Sql no blog seiti.eti.br";
    a.datalimite = new DateTime(2009, 5, 7);

    a = bll.Adiciona(a);
}

Aos DBAs de plantão, meus métodos nas classes de negócios seriam o equivalente às SPROCs no banco de dados, que cuidadriam de toda a manipulação de dados. O ganho é a organização de maior possibilidade de reutilizar e compartilhar código. E fica muito mais fácil de se versionar (Subversion, anyone?) isto!

Conclusão

Não tão robusto quanto um Hibernate, muito melhor que DataSets tipados. Fácil de aprender e usar, difícil de estabelecer uma arquitetura que satisfaça plenamente. Este é o Linq To Sql.

Posts relacionados

This entry was posted in programação and tagged , . Bookmark the permalink.

5 Responses to Boas práticas em Linq To Sql

  1. Pingback: seiti.eti.br » Gerando boletos bancários em Asp.NET - Parte I

  2. Eduardo Mello says:

    Olá,

    estou com essa dificuldade, que aparentemente incomoda diversos blogueiros: como montar uma aplicação em camadas com LINQ to SQL que satisfaça os conceitos básicos? O uso que tu propõe é o que eu sigo. Entretanto, faria AbacaxiBLL realmente parte do Bussiness Layer? Ou, por acessar o dataContext diretamente, ele faz parte do DAL? e se ele faz parte do DAL, o que estaria no BL?

  3. seiti says:

    Bom, não há como ter uma aplicação em camadas com estas mesmas camadas totalmente isoladas uma das outras. Uma camada sempre conversa com as vizinhas.

    Considero DAL o código gerado automaticamente pelo Linq To Sql. O código que escrevo, que efetua consultas lógicas, inclusões condicionais etc e tal, considero parte do business layer.

    O que pode haver, concordo, é uma nova partição do business layer: uma que conversa com a DAL e uma puramente lógica. Isto vai depender da complexidade de suas regras de negócio.

  4. davidzing says:

    Olá,
    Como estou aprendendo C# tenho algumas duvidas.
    A classe AbacaxiBLL esta implementando duas interfaces: ITestableCrud, IDisposable
    Estou entendendo que ITestableCrud possui a declaração dos métodos CRUDs, esta certo ?
    Quanto a interface IDisposable , tudo bem ate entendi, quando você usa o método Dispose ?
    No exemplo de uso voce não teria que haver um bll.Dispose ???
    Eu nao poderia criar uma camada DAL com o seguinte codigo:

    Private contexto As EstoqueDataContext
    Sub New()
    contexto = New EstoqueDataContext
    End Sub
    Public Function GetProdutos() As IEnumerable(Of Produto)
    Using (contexto)
    Dim produtos = From p In contexto.Produtos _
    Select p
    Return (produtos.ToList())
    End Using
    End Function

    depois eu criaria uma BLL (com referencia para DAL) e faria assim:

    Dim dal As DAL.ProdutoDAL

    Public Function getProdutos() As IEnumerable(Of DAL.Produto)
    dal = New DAL.ProdutoDAL
    Return dal.GetProdutos()
    End Function

    E na interface eu usaria:

    bll = New BLL.ProdutoBLL
    gdvProdutos.DataSource = bll.getProdutos

    Minha duvida ao usar este modelo seria o datacontext , ele é fechado automaticamente ou fica aberto ?

    David

  5. seiti says:

    Pelo que andei vendo, é preciso dar um Dispose no datacontext. Mas isto não é tão crítico assim, se você não fizer isto não será o fim do mundo.

    O coletor de lixo dará conta do recado, só vai levar um pouco mais de tempo para liberar os recursos.

    Mas o ideal é sempre chamar Dispose() em todo objeto cuja classe implemente a inteface IDisposable.

Deixe um Comentário

O seu endereço de email não será publicado Campos obrigatórios são marcados *

*

Você pode usar estas tags e atributos de HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>