TechEd 2005

Dias 29, 30 de Novembro e 1 de Dezembro acontecerá em São Paulo – Brasil o maior evento realizado pela Microsoft Brasil, chamado de TechEd 2005. Neste haverão palestras para desenvolvedores e IT Pros, onde as sessões para desenvolvedores, haverá bastante palestras à respeito dos novos produtos e tecnologias, como por exemplo Windows Vista, Windows Workflow, Windows Communication Foundation, mas não vi nada sobre Windows Presentation Foundation (Avalon) até o momento.

De qualquer forma, já defini algumas das palestras que pretendo assisitir, sendo elas:

  • Novidades do Windows Forms 2.0 – Alexandre Tarifa [ 30/11 – Sala 1 ]
  • Fundamentos de desenvolvimento para Windows Vista – Fabio Galuppo [ 30/11 – Sala 1 ]
  • Dominando as ferramentas SQL Server 2005 – Cristiano Matheus [ 30/11 – Sala 1 ]
  • Composite UI Application Block – Marcelo Azuma [ 30/11 – Sala 5 ]
  • Windows Workflow Foundation – Marcelo Uemura [ 01/12 – Sala 1 ]
  • Windows Communication Foundation – Leonardo Bruno Lima [ 01/12 – Sala 1 ]

Vale lembrar que haverá um espaço para comunidades, ou melhor, uma espécie de “Ask the Experts” e, estarei por lá em algum desses “cantinhos”. Para aqueles que quiserem ir até mim bater um papo e trocar algumas idéias, saiba serão muito bem vindos. A idéia será falar de ASP.NET 1.x e 2.0 e, se houver alguma coisa que eu não souber responder, anoto a dúvida, faço uma consulta à alguns gurus no assunto e, prometo dar um feedback à todos. 😉

Criando objetos SQL-CLR

Como sabemos, utilizamos o T-SQL (Transact Structure Query Language) para acessar e manipular dados em um Banco de Dados SQL Server. Como o T-SQL é uma linguagem de Banco, ela é bastante limitada além de procedural; muitas vezes precisamos fazer algo dentro da Base de Dados onde ficamos impossibilitados, tendo que trazer os dados para a aplicação e assim fazer as devidas manipulações e/ou consistências ali.

Nesta nova versão do SQL Server e do Visual Studio .NET (SQL Server 2005 e Visual Studio 2005, respectivamente), a Microsoft integrou o CLR (Common Language Runtime) do .NET com o SQL Server 2005, podendo assim desenvolver Stored Procedures, Triggers, User-Defined Functions, User-Defined Types utilizando uma linguagem .NET como, por exemplo, Visual Basic .NET ou Visual C# .NET, ou seja, em código gerenciado (managed code).

A integração com o Common Language Runtime (CLR) traz uma série de benefícios, pois agora os desenvolvedores não estarão mais limitados a utilizar o T-SQL quando quiserem fazer interações com o SQL Server, onde podem escrever códigos em linguagens .NET e ter toda a riqueza que estas nos oferecem, como por exemplo: tratamento de erros estruturados, arrays, coleções fortemente tipadas, laços For…Next e For Each e até mesmo utilizarmos uma Regular Expression para validar um determinado campo. Podemos também usufruir do CLR, indicando nos em compile-time erros de sintaxe até mesmo buffer overflows que possam vir a acontecer. O mais interessante ainda é que, para termos boa performance, o runtime do .NET é lazy loading para o usuário do SQL Server, ou seja, somente carregará quando for realmente necessário, portanto, quando invocar pela primeira vez uma Stored Procedure ou qualquer outro objeto que lá dentro se encontra.

Vamos ver no decorrer deste artigo como fazer para criar esses tipos de objetos utilizando uma linguagem .NET. O artigo se baseia na versão Beta 2 do SQL Server 2005 e versão Beta 1 do Visual Studio .NET 2005. Vale lembrar que, pelo fato destes softwares estarem ainda em suas versões betas, é possível que até a sua versão final alguma característica possa vir a mudar.

No Visual Studio .NET foi criado uma série de novos Templates para projetos. Um deles é o SQL Server Project , que é justamente para esta finalidade: criar objetos para o SQL Server. Para isso, ao iniciar o Visual Studio .NET 2005, basta criar um novo projeto e, ao selecionar a linguagem desejada, terá os templates para projetos para Base de Dados (Database). A Figura 1 ilustra este processo.

Quando o projeto é criado, é apresentada uma caixa de diálogo para informar o servidor de Banco de Dados pelo qual queremos criar os objetos. Neste momento temos que informar os dados para acesso, como: Nome do Servidor, Login e Password e a Base de Dados em si.

Criado o projeto e configurado a conexão com a Base de Dados, podemos já iniciar a construção dos objetos. Mas antes disso, vamos entender um pouco mais sobre cada um destes objetos:

Objeto  Descrição
Stored Procedures  Uma Stored Procedure (ou Procedimento Armazenado) é uma coleção de instruções T-SQL que é armazenada no servidor de Banco de Dados. A Stored Procedure nada mais é que um método qual encapsula a lógica de uma tarefa repetitiva, fornecendo suporte à declaração de variáveis, condicionais, laços e outros recursos de programação.
Triggers  Uma Trigger é um Procedimento Armazenado que é executado quando os dados de uma tabela específica são modificados. É bastante utilizado para impor integridade referencial ou consistência entre dados relacionados (claro, em tabelas diferentes).
User-Defined Functions Com o SQL Server você pode criar suas próprias funções para estender as funcionalidades do mesmo. Ele pode conter um ou mais parâmetros de entrada, podendo retornar um valor escalar ou uma tabela.
User-Defined Types O SQL Server fornece vários tipos de dados de sistema, mas não se limita a eles. Você pode criar tipos de dados específicos para a sua aplicação, baseando-se obrigatóriamente nestes tipos de dados do sistema. 

Criando objetos no .NET – Stored Procedures

Depois de entendermos o que é cada um desses objetos veremos como criá-los no Visual Studio .NET 2005. Para criar um novo objeto do tipo Stored Procedure dentro do projeto SQL Server devemos clicar com o botão direito em cima do Projeto no Solution Explorer >> Add >> Strored Procedure. Um arquivo é criado com a extensão *.vb (ou *.cs se o projeto tiver como linguagem o Visual C# .NET). Depois de adicionado, podemos ver que a IDE criou uma Partial Class, chamada StoredProcedures. Na medida que você for criando objetos deste tipo, outras Partial Class são também criadas e, quando compilado o projeto, estas, por sua vez são mescladas (merge) em uma única classe chamada StoredProcedures. Ao adicionar um novo objeto do tipo Stored Procedure, terá o código semelhante ao abaixo:

1
2
3
4
5
6
7
8
9
10
11
 
Imports System
Imports System.Data
Imports System.Data.Sql
Imports System.Data.SqlServer
Imports System.Data.SqlTypes
 
Partial Public Class StoredProcedures
    <SqlProcedure()> Public Shared Sub StoredProcedure1()
        ‘ Add your code here
    End Sub
End Class
 

Código 1 – Código gerado pela IDE quando solicitamos uma nova Stored Procedure.

O Atributo <SqlProcedure()> determina que o procedimento é uma Stored Procedure. O Visual Studio .NET usa esta determinação para criar a Stored Procedure dentro do SQL Server quando o Deployment é feito. Como já vimos acima, uma Partial Class chamada StoredProcedures é criada e dentro dela colocamos uma ou mais Stored Procedures. O interessante é colocar uma Stored Procedure por arquivo para facilitar a manutenção, mas isso não é uma regra obrigatória.

Vale ressaltar também que, independentemente se criarmos as Stored Procedures em um mesmo arquivo ou em arquivos separados, ao compilar, as Partial Class são mescladas e, com isso, as Stored Procedures ficam todas dentro de uma mesma classe, como já mencionamos acima. Para certificarmos que isso realmente acontece, podemos visualizar através do Class View do Visual Studio .NET, como vemos na Figura 4 abaixo:

 

Figura 4 – Class View do Visual Studio .NET 2005.

Depois de entendido a estrutura de como isso funciona, vamos então ao código que mostrará realmente a construção da Stored Procedure. O nosso cenário consiste em três tabelas, onde na primeira são armazenados os Fabricantes de Veículos. Já a segunda se encarrega de armazenar os Veículos dos respectivos Fabricantes e, por fim, temos uma tabela chamada Log que guardará os Logs de inserção, deleção e atualização efetuados na tabela Veiculo. A nossa tabela já está pré-configurada com os fabricantes. Um “select” na mesma nos retornará aos seguintes dados:

1
2
3
4
5
6
7
8
9
10
11
 
SELECT * FROM Fabricante
 
FabricanteID Nome
————– ————————————————–
1                 Audi
2                 Fiat
3                 Ford
4                 Volkswagen
5                 Chevrolet
 
(5 row(s) affected)
 

Código 2 – Dados pré-configurados na Tabela Fabricante para os exemplos do artigo.

Para complementar temos ainda a tabela de Veiculos, qual contém os veículos relacionados com os seus respectivos fabricantes, qual também já temos uma pré-carga com alguns dados:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 
SELECT
  Fabricante.Nome As Fabricante
  , Veiculo.Nome As Veiculo
FROM Veiculo
INNER JOIN Fabricante ON
  Fabricante.FabricanteID = Veiculo.FabricanteID
 
Fabricante             Veiculo
——————-   ————————————————–
Audi                     A3
Fiat                      Palio
Ford                     Fiesta
Volkswagen           Golf
Chevrolet              Corsa
 
(5 row(s) affected)
 

Código 3 – Dados pré-configurados na Tabela Veiculo para os exemplos do artigo.

Portanto, agora vamos transpor a última query, que retorna os Veículos e seus respectivos Fabricantes para uma Stored Procedure em .NET. A mesma vai chamar-se RetornaVeiculos. O código abaixo exemplifica isso:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 
Imports System
Imports System.Data
Imports System.Data.Sql
Imports System.Data.SqlServer
Imports System.Data.SqlTypes
 
Partial Public Class StoredProcedures
    <SqlProcedure()> Public Shared Sub RetornaVeiculos()
        Dim cmd As SqlCommand = SqlContext.GetCommand()
        Dim pipe As SqlPipe = SqlContext.GetPipe()
        
        Dim query As String = “SELECT “
        query &= “Fabricante.Nome, Veiculo.Nome “
        query &= “FROM Veiculo “
        query &= “INNER JOIN Fabricante ON “
        query &= “Fabricante.FabricanteID = Veiculo.FabricanteID”
        
        cmd.CommandText = query
        pipe.Send(cmd.ExecuteReader())
    End Sub
End Class
 

Código 4 – Stored Procedure RetornaVeiculos().

Analisando o código acima, temos algums novos objetos. No caso do exemplo do código 4 nos é apresentado o SqlContext e o SqlPipe. Veremos a funcionalidade de cada um desses objetos logo abaixo:

  • SqlContext: Através da classe SqlContext você consegue interagir com o Banco de Dados daquele contexto/conexão do ambiente em que está executando-o, resgatando as informações e objetos necessários para a execução de um determinado comando no Banco de Dados como, por exemplo, GetConnection(), GetCommand(), GetTransaction() , etc.. 

  • SqlPipe:  Este por sua vez envia mensagens, dados tabulares ou até mesmo erros que possam vir a ocorrer durante a execução para o cliente. O conceito deste objeto é bastante similar ao objeto HttpResponse (Response) do ASP.NET, que envia informações para quem o está requisitando (Browser).

O método GetPipe() resgata o canal de comunicação entre o objeto e a aplicação cliente que o chama, ou seja, é a comunicação entre o cliente e o servidor. Através do método Send deste objeto você envia as informações para o cliente. Este mesmo método tem vários overloads (sobrecargas) que recebe diversos tipos de objetos para retornar ao cliente. Já a classe SqlContext representa o contexto corrente e, com isso, elimina a necessidade de abrir outra conexão com a Base de Dados. Com exceção dessas novidades, o resto do código é bastante parecido com o que temos hoje ao desenvolvermos através de código ADO.NET.

Adicionando o Assembly no SQL Server 2005

Depois de criado a(s) Stored Procedure(s), ou qualquer outro objeto, é necessário catalogar o Assembly gerado pelo Visual Studio .NET dentro do SQL Server 2005. Este processo consiste em dois passos: no primeiro deles você deve catalogar o Assembly, ou seja, “inserí-lo” dentro do SQL Server. No segundo passo você precisa mapear os objetos (Stored Procedures, Triggers, etc) para os métodos que estão dentro do Assembly que, nesta altura, já estará catalogado. Veremos como funciona o processo de catálogo de Assemblies através da DDL (Data Definition Language):

1
2
3
4
 
USE BetaTester
  CREATE ASSEMBLY  ObjetosSQL 
    FROM ‘C:SQLCLR.NETObjetosSQLbinObjetosSQL.dll’
    WITH PERMISSION_SET = SAFE
 

Código 5 – Catalogando o Assembly dentro do SQL Server 2005.

No SQL Server 2005 temos agora uma nova instrução dentro da linguagem DDL que chamamos de “CREATE ASSEMBLY”. Esta instrução é utilizada para adicionarmos o Assembly dentro da Base de Dados. Como podemos ver, não existem muitos segredos: informamos um nome qualquer que identificará o Assembly e, na cláusula FROM, informarmos o path completo até o arquivo DLL gerado pelo Visual Studio .NET. Já a cláusula PERMISSION_SET permite especificar o nível de segurança em que seu código será executado/acessado. Existem três opções para esta cláusula, como veremos abaixo:

Tipo de Permissão  Descrição
SAFE É o default. Neste modo o Assembly somente poderá rodar no contexto local, mas não através do SqlClient. Previne também o acesso através de recursos externos e de código não gerenciado.
EXTERNAL_ACCESS  É o mesmo que SAFE, somente habilitando o acesso aos recursos externos. 
UNSAFE  Acesso irrestrito, desde que o Assembly seja assinado e catalogado por um usuário que seja membro do grupo sql_admins.

Depois de catalogado o Assembly dentro do nosso Banco de Dados, o que temos que realizar agora é a definição da Stored Procedure, mapeando o método RetornaVeiculos() que está dentro do Assembly. Para isso, utilizamos o código semelhante ao que se utiliza atualmente, ou seja, utilizando a linguagem DDL. Exemplo:

1
2
3
4
 
USE BetaTester
  CREATE PROCEDURE RetornaVeiculos 
    AS EXTERNAL NAME
    ObjetosSQL.[ObjetosSQL.StoredProcedures].RetornaVeiculos
 

Código 6 – Mapeando os métodos/objetos dentro do SQL Server 2005.

Uma observação importante é que, se a Stored Procedure tiver parâmetros, você deverá também específicá-los quando for criá-la dentro do SQL Server. Depois de mapead, podemos executar a Stored Procedure normalmente. Se tudo ocorrer corretamente o resultado será o mesmo que está sendo exibido no código 3 deste artigo. Para executar a Stored Procedure pode-se fazer como já era feito nas versões anteriores do SQL Server:

1
2
 
USE BetaTester
  EXECUTE RetornaVeiculos 
 

Código 7 – Executando a Stored Procedure.

Se analisarmos agora o Object Browser que encontra-se dentro do SQL Server Management Studio, veremos lá a Stored Procedure criada e o Assembly também já catalogado. Através da tabela sys.assembly_files você pode recuperar os Assemblies que estão catalogados dentro de um determinado Banco de Dados. Além dessa forma que foi explicado acima para catalogar o Assembly no SQL Server, pode ser feito automaticamente pelo Visual Studio .NET onde, depois de compilar o projeto, faz o Deployment do mesmo e com isso, os passos que foram efetuados acima ele faz automaticamente, incluindo todos os objetos dentro da Base de Dados em questão. Para realizar isso dentro do Visual Studio .NET 2005 vá até o menu Build e clique na opção “Build <Project>”.

Triggers

Como já sabemos, Triggers são disparadas quando uma ação de INSERT, UPDATE ou DELETE é executada em uma determinada tabela e as utilizamos quando necessitamos criar integridade ou mesmo fazer consistências dos dados da Base de Dados. Mas os eventos (ações, como são tratados em .NET) não se restringem somente à estas opções, tendo inclusive ações que detectam, por exemplo, a criação de tabelas dentro da Base de Dados. Para o uso das CLR Triggers, quando você cria uma Trigger no Visual Studio, o nome da Partial Class é Triggers.

No restante é bem semelhante, mesmo a construção das Stored Procedures como já vimos acima. A principal diferença está na recuperação do Contexto onde o comando corrente é executado, ou seja, dentro da Trigger você recupera o contexto através do método GetTriggerContext do objeto SqlContext. Este método fornece as mesmas informações do anterior, incluindo o acesso às tabelas virtuais que são criadas durante a execução da Trigger, tabelas quais armazenam os dados que causaram o disparo da mesma.

Outra diferença também é que o atributo para o método também muda. Agora temos que utilizar o seguinte atributo: <SqlTrigger(…)>, o qual veremos detalhadamente mais abaixo. No cenário dos nossos testes criaremos uma Trigger vinculada à tabela Veiculo onde, a cada Inserção, Atualização ou Deleção um Log deve ser gerado na tabela Log. Abaixo o código da mesma para analisarmos:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 
Imports System
Imports System.Data
Imports System.Data.Sql
Imports System.Data.SqlServer
Imports System.Data.SqlTypes
 
Partial Public Class Triggers
     <SqlTrigger(Event:=”FOR INSERT, UPDATE, DELETE”, Name:=”GeraLog”, Target:=”Veiculo”)> _
     Public Shared Sub GeraLog()
        Dim context As SqlTriggerContext = SqlContext.GetTriggerContext()
        Dim cmd As SqlCommand = SqlContext.GetCommand()
        
        Select Case context.TriggerAction
            Case TriggerAction.Insert
                cmd.CommandText = “INSERT INTO [Log] (Descricao) VALUES (‘INSERÇÃO.’)”
            Case TriggerAction.Delete
                cmd.CommandText = “INSERT INTO [Log] (Descricao) VALUES (‘DELEÇÃO.’)”
            Case TriggerAction.Update
                cmd.CommandText = “INSERT INTO [Log] (Descricao) VALUES (‘ATUALIZAÇÃO.’)”
        End Select
        cmd.ExecuteNonQuery()
    End Sub
End Class
 

Código 8 – Trigger GeraLog().

Analisando o código acima vemos que o construtor do atributo SqlTrigger têm alguns parâmetros:

  • Event:  É o evento em que a Trigger vai ser disparada caso o mesmo aconteça. Se quisermos que a Trigger seja disparada toda vez em que uma inserção de um novo registro seja efetuada na Tabela, definimos como “FOR INSERT”. O mesmo acontece para atualização e deleção de registros. É importante lembrar que a cláusula “FOR” não é necessária para cada uma dessas ocasiões. 

  • Name:  O nome em si da Trigger.

  • Target: A tabela que será o “alvo”, ou melhor, a tabela em que a Trigger será anexada. A tabela em que é detectada alguma ação e esta estiver sendo capturada.

Vale lembrar que é perfeitamente possível acessar as tabelas virtuais “INSERTED” e “DELETED”, que são criadas pela Triggers quando a mesma é executada. Recuperando o contexto da Trigger através do método GetTriggerContext e através da ação da Trigger (verificado com o enumerador System.Data.Sql.TriggerAction) executamos um código específico para aquele processo. No caso do exemplo que é exibido no Código 8 inserimos uma nova linha na tabela Log informando a ação que foi executada.

O processo de criação dentro do SQL Server funciona da mesma forma que é feito para a Stored Procedure, ou seja, utilizando a linguagem DDL (Data Definition Language). O que muda é apenas o nome, agora sendo TRIGGER e tendo que informar em qual evento o mesmo será disparado. O exemplo abaixo exemplifica como a criação é realizada:

1
2
3
4
5
6
 
USE BetaTester
  CREATE TRIGGER GeraLog 
    ON Veiculo
    FOR INSERT, UPDATE, DELETE
    AS EXTERNAL NAME
    ObjetosSQL.[ObjetosSQL.StoredProcedures].GeraLog
 

Código 9 – Criando a Trigger dentro do SQL Server 2005.

User-Defined Functions (UDFs)

A construção e deploymet de User-Defined Functions é da mesma forma que as explicadas anteriormente. Como é necessário, o atributo para o método agora é o <SqlFunction(…)>. Vamos ver abaixo o código para termos um exemplo de uma UDF criada pela linguagem .NET:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 
Imports System
Imports System.Data
Imports System.Data.Sql
Imports System.Data.SqlServer
Imports System.Data.SqlTypes
 
Partial Public Class UserDefinedFunctions
     <SqlFunction(DataAccess:=DataAccessKind.Read)> _
     Public Shared Function RetornaQtdeVeiculos() As Integer
        Dim cmd As SqlCommand = SqlContext.GetCommand()
        cmd.CommandText = “SELECT COUNT(*) FROM Veiculo”
        Return Convert.ToInt32(cmd.ExecuteScalar())
    End Function
End Class
 

Código 10 – UDF – User-Defined Function RetornaQtdeVeiculos().

Como vemos, agora o procedimento é do tipo Function, justamente porque um valor é retornado. Utilizando a função COUNT(*) para retornar a quantidade de registros e através do método ExecuteScalar(), recuperamos este valor da Base de Dados e utilizando o Return, retornamos o valor ao cliente.

T-SQL vs. Managed Code

Uma das grandes dúvidas, com este novo recurso que será disponibilizado nas próximas versões do Visual Studio .NET e SQL Server 2005, é saber quando utilizar código gerenciado e quando utilizar o T-SQL. Com esta questão temos que analisar alguns fatores para a escolha. O T-SQL é interessante utilizar onde o código executará em sua maior parte diretamente acessando os dados, sem nenhuma lógica complexa ou procedural; já a utilização do código gerenciado facilita quando você necessita tirar proveito ao máximo das capacidades de linguagens de programação como, por exemplo, Visual Basic .NET ou Visual C# .NET, inclusive conceitos de orientação à objetos, integrando assim com recursos que dificilmente conseguimos em uma linguagem de Banco de Dados, que é muito limitada em relação à estas linguagens genuínas. Temos, além disso, a vantagem de usufruirmos da biblioteca do .NET Framework para trabalharmos.


Conclusão
: Neste artigo foram apresentadas algumas das novas features do novo SQL Server. Para que não ficasse muito extenso, inúmeros outros recursos não foram abordados neste artigo como, por exemplo, suporte à chamadas assíncronas, métodos para paginação de dados que retornam SqlDataReaders. Inclusive uma nova característica muito interessante, pois não precisamos mais de várias conexões com a base de dados para múltiplos comandos, podendo compartilhar a mesma conexão entre os comandos, fornecendo um grande ganho de performance e escalabilidade. E, como vimos no decorrer do artigo, o código é muito semelhante ao que já é utilizado atualmente em aplicações .NET que fazem acesso aos dados utilizando o ADO.NET. Agora temos mais de uma opção quando quisermos escrever códigos de acesso e manipulação de dados, ou seja, podemos escolher .NET (managed code) ou ainda continuar utilizando o T-SQL.

SQLCLR.NET.zip (208.69 kb)

Guia Inicial do NHibernate

O que é o NHibernate

NHibernate é uma biblioteca (Framework) baseada em .NET para persistir os objetos para bases de dados relacionais. Baseado em uma ferramenta de persistência de dados do Java, chamado Hibernate, o NHibernate tem a finalidade de persistir os objetos .NET em uma base de dados relacional subjacente. Isso facilita muito ao invés de escrever códigos SQL dentro e fora da base de dados, pois o NHibernate gera o código SQL necessário, certificando-se que os tipos e o valores são corretamente criados.

Por que este Guia

Todas as pessoas que já trabalharam com o Hibernate, não sentirão grandes dificuldades em utilizar o NHibernate no mundo .NET. Como os projetos são bastante similares, poderá utilizar a documentação do Hibernate e basear-se nela para aplicar na utilização do NHibernate.

Este artigo é tem a finalidade de mostrar os primeiros passos para a utilização do NHibernate. Cobrirá como persistir um objeto simples em uma tabela.

O Processo de Desenvolvimento

Brevemente o NHibernate terá ferramentas para ajudar na geração do schema da tabela, gerando e atualizando assim os arquivos de mapeamentos. Entretanto, estamos assumindo que não temos estas ferramentas e sendo assim, teremos que criar o arquivo de mapeamento (XML) entre a Classe .NET e a Tabela da Base de Dados manualmente. Abaixo os passos para o desenvolvimento:

  • 1. Criar a Tabela na Base de Dados.
  • 2. Criar a Classe .NET.
  • 3. Criar o Arquivo de Mapeamento.
  • 4. Criar o Arquivo de Configuração do NHibernate.
  • 5. Usar a API do NHibernate.

Passo 1: Escrevendo o SQL

O exemplo é algo bem simples. Considere que estamos desenvolvendo um subsistema básico de gerenciamento de usuários para um Web Site qualquer. O tabela chamará usuários (users) e estaremos assumindo que a Base de Dados a ser utilizada chama-se NHibernate:

1
2
3
4
5
6
7
8
9
10
11
12
 
USE NHibernate
GO
 
    CREATE TABLE users (
        LogonID nvarchar(20) NOT NULL default ‘0’,
        Name nvarchar(40) default NULL,
        Password nvarchar(20) default NULL,
        EmailAddress nvarchar(40) default NULL,
        LastLogon datetime default NULL,
        PRIMARY KEY (LogonID)
GO
)
 

Passo 2: Criando a Classe .NET

Necessitamos criar uma classe com as propriedades que desejamos persistir na Base de Dados. NHibernate utiliza Reflection para resgatar e atribuir os valores as propriedades dos objetos que queremos persistir. Veja abaixo uma classe simples que pode ser persistida utilizando NHibernate:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
 
using System;
 
namespace CS
{
    public class User
    {
        private string id;
        private string userName;
        private string password;
        private string emailAddress;
        private DateTime lastLogon;
 
        public User(){}
 
        private string Id
        {
            get { return id; }
            set { id = value; }
        }
 
        private string UserName
        {
            get { return userName; }
            set { userName = value; }
        }
 
        private string Password
        {
            get { return password; }
            set { password = value; }
        }
 
        private string EmailAddress
        {
            get { return emailAddress; }
            set { emailAddress = value; }
        }
 
        private DateTime LastLogon
        {
            get { return lastLogon; }
            set { lastLogon = value; }
        }
    }
}
 

No exemplo acima, as propriedades e o construtor é definido como públicos, mas isso não é uma exigência para o NHibernate, podendo utilizar propriedades definidas como public, protected, internal ou mesmo private para persistir os dados.

Passo 3: Escrevendo o Arquivo de Mapeamento

Com a tabela criada na Base de Dados e a nossa Classe .NET também escrita, ainda necessitamos uma maneira de dizer ao NHibernate quais as propriedades da Classe .NET que receberá as informações das colunas da tabela da Base de Dados. Isto é realizado através de um arquivo de mapeamento, escrito em formato XML, tendo cada classe seu respectivo arquivo XML de mapeamento.

Para que o sistema funcione corretamente, você deve nomear o arquivo de mapeamento com o mesmo nome da classe, ficando da seguinte forma: “Objeto.hbm.xml”, e no nosso caso: “User.hbm.xml”. Além disso, é necessário colocá-lo no mesmo diretório da classe, pois com isso o NHibernate fará as coisas com mais facilidade. Abaixo um exemplo do arquivo de mapeamento:

1
2
3
4
5
6
7
8
9
10
11
12
 
<?xml version=”1.0″ encoding=”utf-8″ ?>
<hibernate-mapping xmlns=”urn:nhibernate-mapping-2.0″>
    <class name=”CS.User, CS” table=”users”>
        <id name=”Id” column=”LogonId” type=”String” length=”20″>
            <generator class=”assigned” />
        </id>
        <property name=”UserName” column= “Name” type=”String” length=”40″/>
        <property name=”Password” type=”String” length=”20″/>
        <property name=”EmailAddress” type=”String” length=”40″/>
        <property name=”LastLogon” type=”DateTime”/>
    </class>
</hibernate-mapping>
 

Analisando o código acima, o que mais nos chama a atenção são as tags class e table, onde definimos o nome da Classe e o nome da Tabela da Base de Dados de onde será recuperado e/ou inserido os dados. No caso de nosso exemplo, estamos mapeando os dados da tabela “users” para a classe “User”.

Logo mais abaixo temos as propriedades que compõem nosso objeto. Internamento o NHibernate varre este arquivo recuperando os atributos “name”, que correspondem as propriedades da nossa Classe User. É recuperado também o atributo “column”, que se refere a coluna correspondente na Base de Dados, lembrando que este atributo é opcional, e quando ocultado o NHibernate assume que o nome da coluna na Base de Dados é o mesmo da Propriedade.

A tag Id é a responsável por ser a chave identificadora do objeto/registro, onde mapeamos a propriedade (name) para a coluna da Base de Dados (column). No nosso exemplo, geramos as nossas próprias chaves, não necessitando que a Base de Dados gere automaticamente, mas isso é perfeitamente possível. Para saber um pouco mais sobre chaves, poderá consultar a documentação do NHibernate.

Observação: Se você estiver utilizando Visual Studio .NET, certifique-se que ao compilar o projeto a propriedade Build Action dos arquivos XML de mapeamento – que no nosso caso chama-se “User.hbm.xml” – está definido como “Embedded Resource”, para que o arquivo XML faça parte do Assembly como Resource. Isso facilita porque não há a necessidade de distribuir os arquivos XML de mapeamento juntos com a aplicação, mas claro, a desvantagem é que se mudar o arquivo de mapeamento, a aplicação deverá ser recompilada.

Passo 4: Criando o Arquivo de de Configuração para a Base de Dados

Ainda falta dizermos ao NHibernate onde ele deverá ir buscar e persistir os dados. A maneira mais fácil e simples é criando uma seção de configuração dentro do arquivo de configuração da aplicação (Web.Config ou App.Config). Isto é equivalente a usar as propriedades de configuração do NHibernate. Abaixo veremos como criar a seção de configuração dentro do arquivo App.Config:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 
<?xml version=”1.0″ encoding=”utf-8″ ?>
<configuration>
    <configSections>
        <section
             name=”nhibernate”
             type=”System.Configuration.NameValueSectionHandler, System,
             Version=1.0.3300.0,Culture=neutral, PublicKeyToken=b77a5c561934e089″ />
    </configSections>
 
    <nhibernate>
    <add
        key=”hibernate.connection.provider”
        value=”NHibernate.Connection.DriverConnectionProvider”
    />
    <add
        key=”hibernate.dialect”
        value=”NHibernate.Dialect.MsSql2000Dialect”
    />
    <add
        key=”hibernate.connection.driver_class”
        value=”NHibernate.Driver.SqlClientDriver”
    />
    <add
        key=”hibernate.connection.connection_string”
        value=”CONNECTION_STRING”
    />
    </nhibernate>
</configuration>
 

No código acima, definimos o SqlClient como Provider para acesso à Base de Dados SQL Server 2000. A ConnectionString foi ocultada apenas para não confundir o leitor, tendo que ser definida com os dados de acesso do servidor de Banco de Dados que estará servindo a aplicação.

Veja não existe nenhuma informação sobre a configuração do log4net. NHibernate usa log4net para registrar o que está acontecendo internamente. Em uma aplicação em produção é recomendável configurarar o log4net e ajustar NHibernate ao nível apropriado do registro para seu cenário.

Passo 5: Utilizando o NHibernate

Até agora fizemos o trabalho árduo. Se tudo ocorreu como esperamos, até o momento temos:

  • Arquivo User.cs – Classe .NET do nosso objeto.
  • Arquivo User.hbm.xml – Arquivo XML de mapeamento.
  • app.Config – Arquivo de Configuração da aplicação.
  • SQL Server – A Tabela “users” criada.

Abaixo os passos que devemos seguir para utilizar o NHibernate para recuperar e inserir os dados na Base de Dados:

  • 1. – Criar o Objeto de Configuração.
  • 2. – Informar ao Objeto de Configuração os tipos de Objeto que deseja persistir.
  • 3. – Criar uma sessão com a Base de Dados.
  • 4. – Carregar, Salvar e Consultar seus objetos.
  • 5. – Fechar ou Flush() na sessão com a Base de Dados.

Abaixo veremos como utilizar o NHibernate:

Inicialmente devemos criar o objeto de configuração do NHibernate que é responsável e tem o conhecimento sobre todos os mapeamentos que existem entre os objetos do nosso sistema e a Base de Dados.

1
2
 
Configuration cfg = new Configuration();
cfg.AddAssembly(“CS”);
 

O objeto de Configuração recupera através do Assembly todos os arquivos que terminam com “.hbm.xml” qual o utilizará para efetuar as manipulações. Existem outras formas de definir e carregar os arquivos de mapeamentos, mas esta é a forma mais simples.

O objeto de ISession representa uma conexão com a sua Base de Dados e o ITransaction representa uma transação controlada também pelo NHibernate.

1
2
3
 
ISessionFactory factory = cfg.BuildSessionFactory();
ISession session = factory.OpenSession();
ITransaction transaction = session.BeginTransaction();
 

Depois das configurações realizadas, nos resta definitivamente inserir, carregar e consultar a Base de Dados através do NHibernate:

1
2
3
4
5
6
7
8
9
10
11
 
User newUser = new User();
newUser.Id = “joe_cool”;
newUser.UserName = “Joseph Cool”;
newUser.Password = “abc123”;
newUser.EmailAddress = “joe@cool.com”;
newUser.LastLogon = DateTime.Now;
 
session.Save(newUser);
 
transaction.Commit();
session.Close();
 

Como podemos ver no código acima, um novo objeto do tipo User é criado, definimos as suas propriedades e depois o passamos para o método Save(…) do NHibernate, que baseando-se no arquivo XML de mapeamento correspondente, faz a persistência do objeto de forma “mágica” na nossa Base de Dados.

Para recuperar um objeto da Base de Dados, utilize o método Load, passando o ID do usuário para carregarmos o objeto e assim manipulá-lo. Exemplo:

1
2
3
4
5
6
 
session = factory.OpenSession();
 
User joeCool = (User)session.Load(typeof(User), “joe_cool”);
 
joeCool.EmailAddress = “tt@tt.com”;
session.Flush();
 

Depois de recuperado o Usuario com o Id “joe_cool” da Base de Dados, alteramos o E-Mail do mesmo para “tt@tt.com” e invocamos o método Flush() da Session para persistir/devolver os dados atualizados na Base de Dados.

Podemos também recuperar uma lista de objetos da nossa Base de Dados através do NHibernate. Utilizamos a Interface IList para isto, qual encontra-se dentro do Namespace System.Collections. Um exemplo simples seria algo como:

1
2
3
4
5
6
 
session = factory.OpenSession();
 
IList userList = session.CreateCriteria(typeof(User)).List();
 
foreach(User user in userList)
    Console.WriteLine(user.Id + ” último login em ” + user.LastLogon);
 

CONCLUSÃO: Bem, como vimos neste artigo, a utilização de um OR/M (Object Relational/Mapping) como o NHibernate diminui e muito o grande espaço que existe entre as tabelas de um Banco de Dados relacional e os objetos de uma aplicação. É ótimo para quando se trabalha com uma aplicação fortemente Orientada à Objetos.

Agradecimentos: Gostaria de agradecer ao meu amigo Mike Doerfler, que gentilmente cedeu o artigo (A Quick Start Guide to NHibernate), para que o conteúdo do artigo escrito por ele, seja adaptado e traduzido para a língua portuguesa.

Referências:

Middleware

Acabo de ler um artigo interessante chamado “Escolhendo entre WebServices, Enterprise Services e Remoting”, onde o autor, Otavio Coelho, mostra os prós e contras de cada uma dessas tecnologias para serem ou não utilizadas em uma middleware.

.NET vs. JAVA

Estou acompanhando uma daquelas intermináveis discussões sobre a Plataforma .NET vs. JAVA em um determinada lista de desenvolvimento de software. Achei muito interessante a citação de um dos integrantes do grupo que em sua opinião sobre a “briga”, onde ele disse:

“De minha parte, entre Java e .NET, fico com arquitetura de software, MDA e Governança de TI. Na guerra entre as partes, prefiro vender a munição. E aproveitem, que está com desconto!!!”.

Legal 🙂

TTT – SP

Na última sexta-feira (10/12), aconteceu na Microsoft em São Paulo o quarto TTT. Vários MVPs, RDs, Líderes de Grupos de Usuários entre outras pessoas estiveram presente prestigiando o evento.

Aconteceram várias palestras voltadas para desenvolvimento e infraestrutura. Deu também para ter uma visão dos planos e das diretivas da Microsoft e seus produtos, além de como entender como abordar a comunidade com essas novas ferramentas e inovações que viram por aí.

A palestra que mais gostei foi uma apresentada qual falava sobre algo mais gerencial, como Software Livre, GPL, TCO, Custos de Desenvolvimento, Manutenção, etc, coisas que para mim eram obscuras e agora consegui ter uma visão bem superficial do que é isso.

Com relação a parte técnica foi falado sobre o SQL Server 2005, VS.NET 2005 e LongHorn, mas nada que não já tinha visto no MSDN, mas claro, para muitos, foi um grande aprendizado/novidade. O mais importante mesmo, foi rever os amigos que muitas vezes nos falamos apenas por MSN.

Piratas da Informática

Tive a oportunidade de assistir a um filme chamado “Piratas da Informática”, conhecido também como “Piratas do Sillicon Valey”. Este filme mostra como a Microsoft e Apple surgiram, mas claro, não vou contar detalhes, pois talvez alguém queira assistir ao mesmo. 😉

Mas o legal, é que vemos grandes nomes, tais como: Bill Gates, Steve Ballmer, Paul Allen, Steve Jobs, Microsoft, Apple e IBM.