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.
Mapeando através do dbml, cria-se a classe Abacaxi no sistema, com as seguintes propriedades: id, responsavel, tarefa e datalimite.
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.


Pingback: seiti.eti.br » Gerando boletos bancários em Asp.NET - Parte I
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?
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.
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
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.