Entity Frameweork 4 - Apresentando os recursos da CTP5 - Code First


Esta prevista para o primeiro semestre deste ano uma nova versão do Entity Framework 4 com muitos ajustes com o objetivo de tornar a ferramenta cada vez melhor.

Enquanto a versão final não sai, vamos falar um pouco sobre uma das suas últimas atualizações, a Entity Framework Feature Community Technology Preview - CTP5, mais precisamente da funcionalidade Code-First.

Obs: Baixe a versão CTP5 aqui : http://tinyurl.com/5rg52tg

Nota: A versão anterior, a CTP4, já permitia usar o recurso do First-Code com um banco de dados existente mas exigia mais intervenção do desenvolvedor com a CTP5 tudo ficou mais simples.

A CTP5 da Entity Framewor 4 inclui atualizações para o recurso Code First e uma interface simplificada da API (DbContext)

O Entity Framework é uma ferramenta OR/M que realiza o mapeamento objeto relacional gerando entidades e mapeando-as para as tabelas do banco de dados.

Na primeira versão do Entity Framework praticamente não havia o que é conhecido como Code-First (Código Primeiro), ou seja, a possibilidade de gerar o modelo de negócios e suas entidades sem ter que primeiro criar o banco de dados antes.

Para quem trabalha usando o paradigma da orientação a objetos deveria ser natural iniciar o desenvolvimento pelo modelo de entidades, onde as classes são definidas para representar o domínio do negócio. Posteriormente, e a partir destas classes, seria gerado o banco de dados usado pela aplicação para realizar a persistência das informações.

Deveria ser assim, mas a primeira versão do EF não permitia esse recurso.

Tentando contornar este problema eram feitos certos malabarismos, mas ainda assim, havia uma dependência muito forte do mapeamento gerado pelo EF4 através do Entity Data Model, o que incomodava muita gente; até que a Microsoft liberou atualizações que permitem atualmente uma maior independência do Framework através da criação de classes POCO que não herdam das classes base geradas pelo EF.

Quando decidimos usar o Code-First não precisamos começar nossa aplicação criando o banco de dados ou definindo um esquema mas podemos iniciar escrevendo classes .NET para definir o modelo de objetos do nosso domínio sem ter que misturar a lógica de persistência de dados com as classes.

Neste artigo eu vou mostrar como usar o Code-First com um banco de dados existente, mais precisamente, o banco de dados Northwind.mdf e usar os recursos da CTP5 para usar as classes POCO do nosso domínio sem ter que gerar um Entity Data Model a partir do banco de dados.

Essa é outra possibilidade, já temos um banco de dados, e vamos ainda sim usar o Code-First.

Mas por que eu iria querer fazer uma coisa dessas ????

Primeiro porque eu não quero passar por todo aquele processo de ter que criar um Entity Data Model nem ter que definir um arquivo de mapeamento XML, e também porque eu quero ter um código mais limpo e conciso.

Com esse objetivo em mente vamos ao trabalho...

O que você vai precisar ter instalado na sua máquina local ?

Eu vou criar uma aplicação Web Forms usando o Visual Web Developer 2010 Express Edition.

Abra o VWD 2010 Express e no menu File selecione New Project;

A seguir escolha Visual Basic -> Web -> ASP .NET Web Application, informe o nome EF4_CodeFirst_BD e clique em OK;

Vamos incluir uma referência ao assembly da CTP5 em nosso projeto.

No menu Project clique em Add Reference;

Selecione a guia Browse e localize a dll EntityFramework na pasta onde foi instalada a CTP5 (c:\Arquivos Programas)

Os recursos da EF Code First CTP5 tem um nome de assembly atualizado e um novo namespace .NET:

Agora vamos definir as classes POCO para a nossa camada de modelo:

No menu Project clique em Add Class e informe o nome Model.vb;

A seguir digite o código abaixo para criar as classe Product, Category e Northwind:

Imports System.Data.Entity

    Public Class Product
        Public Property ProductID() As Integer
            Get
                Return m_ProductID
            End Get
            Set(ByVal value As Integer)
                m_ProductID = value
            End Set
        End Property
        Private m_ProductID As Integer
        Public Property CategoryID() As Integer
            Get
                Return m_CategoryID
            End Get
            Set(ByVal value As Integer)
                m_CategoryID = value
            End Set
        End Property
        Private m_CategoryID As Integer
        Public Property ProductName() As String
            Get
                Return m_ProductName
            End Get
            Set(ByVal value As String)
                m_ProductName = value
            End Set
        End Property
        Private m_ProductName As String
        Public Property UnitPrice() As System.Nullable(Of [Decimal])
            Get
                Return m_UnitPrice
            End Get
            Set(ByVal value As System.Nullable(Of [Decimal]))
                m_UnitPrice = value
            End Set
        End Property
        Private m_UnitPrice As System.Nullable(Of [Decimal])
        Public Property Discontinued() As Boolean
            Get
                Return m_Discontinued
            End Get
            Set(ByVal value As Boolean)
                m_Discontinued = value
            End Set
        End Property
        Private m_Discontinued As Boolean

        Public Overridable Property Category() As Category
            Get
                Return m_Category
            End Get
            Set(ByVal value As Category)
                m_Category = value
            End Set
        End Property
        Private m_Category As Category
    End Class

   
Public Class Category
        Public Property CategoryID() As Integer
            Get
                Return m_CategoryID
            End Get
            Set(ByVal value As Integer)
                m_CategoryID = value
            End Set
        End Property
        Private m_CategoryID As Integer
        Public Property CategoryName() As String
            Get
                Return m_CategoryName
            End Get
            Set(ByVal value As String)
                m_CategoryName = value
            End Set
        End Property
        Private m_CategoryName As String
        Public Property Description() As String
            Get
                Return m_Description
            End Get
            Set(ByVal value As String)
                m_Description = value
            End Set
        End Property
        Private m_Description As String
        Public Property Picutre() As Byte()
            Get
                Return m_Picutre
            End Get
            Set(ByVal value As Byte())
                m_Picutre = value
            End Set
        End Property
        Private m_Picutre As Byte()

        Public Overridable Property Products() As ICollection(Of Product)
            Get
                Return m_Products
            End Get
            Set(ByVal value As ICollection(Of Product))
                m_Products = value
            End Set
        End Property
        Private m_Products As ICollection(Of Product)
    End Class


  
 
Public Class Northwind
        Inherits DbContext

        Public Property Products() As DbSet(Of Product)
            Get
                Return m_Products
            End Get
            Set(ByVal value As DbSet(Of Product))
                m_Products = value
            End Set
        End Property
        Private m_Products As DbSet(Of Product)
        Public Property Categories() As DbSet(Of Category)
            Get
                Return m_Categories
            End Get
            Set(ByVal value As DbSet(Of Category))
                m_Categories = value
            End Set
        End Property
        Private m_Categories As DbSet(Of Category)
    End Class

Note que a classe Northwind herda de DbContext.

Note também que não geramos o Entity Data Model pois vamos usar a classe DbContext.

O código acima é tudo que precisamos para criar nosso modelo e camada de acesso a dados! As CTPs anteriores a EF Code First exigiam um passo adicional para funcionarem em bancos de dados existentes (uma chamada para Database.Initializer<Northwind>(null) para que a EF Code First não criasse o banco de dados) - este passo não é mais necessário com o lançamento da CTP5. 

A EF Code First permite a utilização de "POCO" - Objetos padrão da CLR - para representar as entidades dentro de um banco de dados. Isso significa que você não precisa derivar classes do modelo a partir de uma classe base, nem implementar nenhuma interface ou atributos de persistência de dados neles. Isso permite que as classes do modelo sejam mantidas limpas, facilmente testáveis, e "ignorantes quanto à persistência".

A EF Code First permite que você conecte facilmente suas classes POCO do modelo em um banco de dados através da criação de uma classe "DbContext" que expõe propriedades públicas que mapeiam para tabelas do banco de dados. A classe Northwind acima ilustra como isso pode ser feito. Ela está mapeando nossas classes Product e Category para as tabelas "Products" e "Categories" no banco de dados. As propriedades dentro das classes Product e Category por sua vez mapeiam para colunas nas tabelas Products e Categories - e cada instância de um objeto Product/Category mapeia para uma linha dentro das tabelas.

Configurando o banco de dados

A última tarefa a realizar para poder usar o recurso da CTP5 é configurar uma string de conexão que conecte com o nosso banco de dados,  no caso o Nortwhind.mdf. Para isso definimos no arquivo Web.Config (ou App.Config) a seguinte string de conexão:

<configuration>
  <connectionStrings>
      <add name="Northwind"
        connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=Northwind;Integrated Security=True"
        providerName="System.Data.SqlClient" />
  </connectionStrings>

A EF Code First usa uma convenção onde as classes DbContext por padrão procuram a string de conexão que possui o mesmo nome que a classe de contexto.  Como nossa classe de contexto DbContext é chamada "Northwind" ela vai procurar a string de conexão nomeada como Northwind para usar que foi definida no arquivo Web.Config para acessar em uma instância do SQL Server o banco de dados Northwind.

Usando a camada de modelo Nortwhind

Agora podemos facilmente consultar e atualizar o nosso banco de dados usando uma camada de modelo fortemente tipada que foi construída com o CTP5 Code First.

Abra o arquivo Default.aspx e inclua nesta página um controle GridView com o nome gdvProducts e no evento Load da página inclua o seguinte código:

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Dim northwind As New Northwind()
        Dim produtos = From p In northwind.Products
                              Where p.Category.CategoryName = "Beverages"
                              Select p
        gdvProducts.DataSource = produtos.ToList
        gdvProducts.DataBind()
    End Sub

Executando o projeto iremos obter a relação de produtos exibida no GridView conforme abaixo:

Vamos agora mostrar que podemos atualizar as informações de um produto.

Altere o código do evento Load conforme abaixo onde alteramos o preço de um produto e persistimos as alterações:

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

        Dim northwind As New Northwind()

        'localiza o produto de código igual a 1 (Chai)
        Dim prod As Product = northwind.Products.Find(1)

        'atualiza o preço do produto , seu nome e o campo discontiued
        prod.UnitPrice = 9.99D
        prod.ProductName = "Chai XXXX"
        prod.Discontinued = True

        'persiste as alterações
        northwind.SaveChanges()

        'cria uma consulta e exibe os produtos
        Dim produtos = From p In northwind.Products
                       Where p.Category.CategoryName = "Beverages"
                       Select p

        gdvProducts.DataSource = produtos.ToList
        gdvProducts.DataBind()

    End Sub

Executando o projeto iremos obter novamente a relação de produtos onde podemos verificar as alterações realizadas:

 Dessa forma vimos que o CTP5 EF Code First trata todas as alterações de monitoramente e trabalho e da persistência dos dados para nós de uma maneira simples (lembra que não precisamos geraro Entity Data Model ?)  liberando assim o desenvolvedor para se concentrar na aplicação e na lógica do negócio ao invés de ter que se preocupar com a estrutura de acesso a dados.

Na continuação desse artigo eu irei abordar o modelo de validação embutido na CTP5 Code First. Aguarde...

Pegue o projeto completo aqui:  EF4_CodeFirst_BD.zip

Referências:

José Carlos Macoratti