Quick Dev: Behind LINQ to SQL

Olá pessoal!

Você que já utiliza o LINQ to SQL já deve ter tido a curiosidade de saber como as suas consultas LINQ são transformadas em comandos SQL e principalmente, qual a estrutura destes comandos SQL. Como o LINQ to SQL gera um comando SQL para atualizar apenas um registro em uma tabela? Ele utiliza sua chave primária na clausula WHERE? Acredito que estas sejam questões muito importantes para nos embasarmos quando tivermos que decidir pela sua utilização ou estabelecermos os cenários em que podemos aplicá-lo. No artigo anterior dei um overview sobre o LINQ. Neste artigo conceituo o provider LINQ to SQL e demonstro como suas consultas LINQ são transformadas em comandos SQL. Na imagem a seguir temos a tela da aplicação Windows Forms que será utilizada como exemplo. Nesta aplicação estou utilizando a tabela Customers da base de Northwind, disponível para download no site da Microsoft.

Aplicação de exemplo LINQ to SQL

Os objetos que nos permitem trabalhar com LINQ to SQL estão disponíveis dentro do namespace System.Data.Linq. Dentro deste namespace, basicamente, dois objetos nos permitirão fazer consultas e atualizarmos registros de uma tabela, o DataContext e o Table(of t). O DataContext, com o próprio nome diz, representa um contexto de dados. Através dele especificamos a string de conexão com a base de dados e estabelecemos uma ligação entre a base de dados do contexto e o objeto Table(of t). Este, por sua vez, representa uma tabela na base de dados. Para ligarmos um objeto Table(of t) a uma tabela, precisamos fazer um mapeamento entre suas propriedades e os campos da tabela. Este mapeamento é provido através de atributos disponíveis no namespace System.Data.Linq.Mapping. Veja um exemplo deste mapeamento na listagem a seguir.

Imports System.Data.Linq.Mapping

 _
Public Class Customer
    Private _CustomerID As String
     _
    Public Property CustomerID() As String
        Get
            Return _CustomerID
        End Get
        Set(ByVal value As String)
            _CustomerID = value
        End Set
    End Property
    Private _CompanyName As String
     _
    Public Property CompanyName() As String
        Get
            Return _CompanyName
        End Get
        Set(ByVal value As String)
            _CompanyName = value
        End Set
    End Property
    Private _Country As String
     _
    Public Property Country() As String
        Get
            Return _Country
        End Get
        Set(ByVal value As String)
            _Country = value
        End Set
    End Property
End Class

Note que neste exemplo estou mapeando campos da tabela Customers da base de dados Northwind. O nome da tabela é definido dentro do atributo Table. Em cada propriedade da classe utilizo o atributo Column para definir que ela representa um campo da tabela de mesmo nome da propriedade. Neste atributo posso definir, por exemplo, se a coluna é uma chave primária. Mapeada a tabela, agora precisamos definir um objeto do tipo DataContext, um objeto do tipo Table(of Customer)  – esta classe que acabamos de criar – e fazermos uma consulta LINQ sobre ele. Veja no exemplo a seguir como isso é feito de forma simples, no código da aplicação Windows Forms que vimos no inicio do artigo. Note como é fácil salvar as edições feitas diretamente no DataGridView, através do método SubmitChanges do DataContext.

Imports System.Data.Linq

Public Class frmLTSqlSample

    'Contexto de dados inicializado com a connection string
    Private dtaContext As New DataContext(My.Settings.cnnStr)

    'Objeto do tipo Table, que mapeia os campos da tabela Customers
    Private customersTable As Table(Of Customer)

    Private Sub frmLTSqlSample_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) _
            Handles MyBase.Load
        'Ao carregar o form, carrego o GridView com todos os registros
        'da tabela Customers
        CarregarCustomers()
    End Sub

    Private Sub CarregarCustomers()
        'Obtém a ligação entre a tabela com o contexto de dados
        customersTable = dtaContext.GetTable(Of Customer)()

        'Define a consulta sobre a tabela Customers
        Dim qry = _
            From ctr In customersTable _
            Order By ctr.CompanyName _
            Select ctr

        'Neste ponto, a conexão é aberta e a consulta é executada
        Me.dtvCustomers.DataSource = qry
    End Sub

    Private Sub btnSalvar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
            Handles btnSalvar.Click
        'Todas as alterações realizadas no DataGridView são submetidas 
        'à base de dados
        dtaContext.SubmitChanges()
    End Sub

End Class

Agora que vimos como é simples utilizarmos o LINQ to SQL vamos ver o que está por de trás dele, no que é transformada a consulta LINQ que fizemos e qual comando é enviado à base quando invocamos o método SubmitChanges. Pois bem, existe uma ferramenta que pode nos dar esta resposta! O Profiler do SQL Server. Para quem não conhece, resumidamente, o Profiler é uma ferramenta do SQL Server que permite visualizar e filtrar todos os comandos que estão sendo enviados para a base de dados. Uma vez ativado, basta acionarmos a aplicação e analisamos os resultados. Veja a seguir o resultado para uma aplicação semelhante a esta que exemplifiquei.

Exemplo LINQ to SQL - Profiler

No Comando 1 a aplicação está fazendo uma operação de consulta LINQ, onde nenhum campo foi especificado. Na transformação da consulta LINQ para o comando SQL podemos observar que o provider utilizou um alias para a tabela e ao invés de fazer um select *, mencionou todos os campos, o que é uma boa prática. O Comando 2 é então executado após alteramos um registro no DataGridView e darmos um SubmitChanges no DataContext. Note que o comando de Update é executado através da procedure sp_executesql e que mesmo a tabela tendo uma chave primária, o provider especificou todos os campos na clausula where, utilizando como critério de busca todos os valores atuais de cada campo do registro.

E assim fechamos mais um Quick Dev. Até a próxima!

Deixe um comentário

O seu endereço de e-mail não será publicado.