Entity Framework 5 - Apresentando e usando Spatial Data


Hoje eu vou escrever sobre um novo recurso do Entity Framework, disponível a partir da versão 5, (a versão 6.0 já esta em beta) conhecido como Spatial Data ou Dados geográficos.

A versão 5 do Entity Framework, a partir de agora citada apenas como EF5, além de um  novo  modelo de mapeamento Code First (Sem Designer nem XML), nova APi DBContext e  melhorias no desempenho trouxe as seguintes novidades:

Outro detalhe importante: Agora o Entity Framework é Open Source, e, tem o código aberto e esta esperando pela nossa contribuição... (ASP .NET MVC, ASP .NET Web API e Razor também são projetos Open Source...)

Obs: Instalar o Entity Framework agora é bem simples. Abra o Package Manger Console  digite : Install-Package EntityFramework e pronto!

Os dados espaciais representam informações sobre o local físico e a forma de objetos geométricos. Esses objetos podem ser locais de pontos ou objetos mais complexos como países, estradas ou lagos.

O SQL Server suporta dois tipos de dados espaciais: o tipo de dados geometry e o tipo de dados geography.

O Entity Framework suporta trabalhar com dados espaciais/geográficos através das classes DbGeography e DbGeometry.  Essas classes se baseiam em funcionalidades especificas do banco de dados fornecidas pelo Entity Framework provider.  Nem todos os provedores dão suporte aos dados espaciais e aqueles que dão suporte podem ter pré-requisitos adicionais.

Neste artigo eu vou mostrar um exemplo simples e prático de utilização dos dados geográficos com Code-First em uma aplicação VB .NET usando o Visual Studio Express 2012 for Windows Desktop e o Entity Framework com Code-First. (O banco de dados usado será o LocalDB)

Iremos criar uma aplicação onde iremos gerar um banco de dados com informações de alguns hotéis como nome, cidade e localização geográfica usando as coordenadas de latitude e longitude (obtidas do Google Maps).

Usando Spatial Data com Code-First

Agora vamos instalar o Entity Framework 5 via Nuget.

No menu TOOLS clique em Library Package Manager e a seguir em Package Manager Console;

No Package Manager Console digite : Install-Package EntityFramework e tecle ENTER;

O Entity framework 5 deverá ser instalado e adicionado ao nosso projeto conforme mostra a figura acima.

Definindo o modelo usando Code-First

Ao utilizar o modelo de desenvolvimento Code-First geralmente começamos definindo as classes do nosso modelo conceitual ou seja do nosso domínio. Para o exemplo do artigo irei definir a classe Hotel com as propriedades : HotelID, Nome, Cidade e Localizacao :

Clique no menu PROJECT e a seguir em Add Class e informe o nome Hotel.vb a seguir defina o código abaixo para a classe Hotel:

Imports System.Data.Spatial
 

Public Class Hotel

   Public Property HotelID As Integer

    Public Property Nome As String

    Public Property Cidade As String

    Public Property Localizacao As DbGeography

End Class

 

A propriedade Localizacao e do tipo DbGeography e para poder esse tipo temos que incluir o namespace System.Data.Spatial.

Agora temos que definir a classe que deriva de DbContext e expõe propriedades DbSet(Of TEntity)

As propriedades DbSet(Of TEntity) permitem que o contexto reconheça quais tipos desejamos incluir em nosso modelo.

Uma instância do tipo derivado DbContext gerencia os objetos de entidade durante o tempo de execução, o qual inclui popular os objetos com dados de um banco de dados, controlar as alterações, e persistir os dados no banco de dados.

Os tipos DbContext e DbSet são definidos no assembly EntityFramework que foi adicionado e referenciando quando instalamos o EF via Nuget.

Clique no menu PROJECT e a seguir em Add Class e informe o nome HotelContext.vb a seguir defina o código abaixo para a classe HotelContext:

Imports System.Data.Entity
 

Public Class HotelContext

     Inherits DbContext
 

    Public Property Hoteis() As DbSet(Of Hotel)
 

End Class

 

Observe a referência ao namespace System.Data.Entity e que a classe HotelContext herda de DbContext.

Definindo a interface

Vamos agora definir uma interface bem simples em nosso projeto usando o formulário form1.vb e incluindo os seguintes componentes:

Disponha os controles conforme o leiaute exibido na figura abaixo:

Defina o seguinte namespace no início do formulário form1.vb:

Imports System.Data.Spatial

Agora vamos definir o código do evento Click do botão de comando - Criar Dados com o EF5- conforme abaixo:

Private Sub btnCriarDados_Click(sender As Object, e As EventArgs) Handles btnCriarDados.Click

   CriarDados()

End Sub

 

O código da rotina CriarDados() é visto a seguir:

Private Sub CriarDados()
        Using contexto = New HotelContext()
            contexto.Hoteis.Add(New Hotel() With { _
                .Nome = "Hotel Arpoador", _
                .Cidade = "Salvador", _
                .Localizacao = DbGeography.FromText("POINT(-12.983148 -38.521729)") _
            })
            contexto.Hoteis.Add(New Hotel() With { _
                .Nome = "Hotel Sereia do Mar", _
                .Cidade = "Guaruja", _
                .Localizacao = DbGeography.FromText("POINT(-23.926013 -46.300049)") _
            })
            contexto.Hoteis.Add(New Hotel() With { _
                .Nome = "Hotel Nosso Cantinho", _
                .Cidade = "Rio de Janeiro", _
                .Localizacao = DbGeography.FromText("POINT(-22.897683 -43.24585)") _
            })
            contexto.Hoteis.Add(New Hotel() With { _
                .Nome = "Hotel Candeias", _
                .Cidade = "Belo Horizonte", _
                .Localizacao = DbGeography.FromText("POINT(-19.932041 -43.970948)") _
            })
            contexto.Hoteis.Add(New Hotel() With { _
                .Nome = "Hotel America", _
                .Cidade = "Goiânia", _
                .Localizacao = DbGeography.FromText("POINT(-16.678293 -49.288331)") _
            })
            contexto.SaveChanges()
        End Using
    End Sub

 

Neste código estou incluindo informações para 5 hotéis na tabela Hotels. Observe que o tipo de dado Localizacao é do tipo DbGeography e esta sendo informando com as coordenadas, latitude e longitude, que representa a localização do hotel no Google Maps.

O método SaveChanges irá criar o banco de dados e persistir as informações na tabela Hotels.

Agora temos o código do evento Click do botão de comando - Exibir Dados Criados :

Private Sub btnExibirDados_Click(sender As Object, e As EventArgs) Handles btnExibirDados.Click

    ExibirDados()

End Sub

O código da rotina ExibirDados() é dado a seguir:

  Private Sub ExibirDados()

        Using contexto = New HotelContext()

            Dim locais = From loc In contexto.Hoteis
                             Select loc
            lsbDados.Items.Clear()
            For Each hot In locais
                lsbDados.Items.Add(hot.Nome + " - " + hot.Cidade + " - " + hot.Localizacao.ToString())
            Next
        End Using
    End Sub

 

No código acima estou selecionando os dados da entidade Hotels exibindo-as em um ListBox usando uma consulta LINQ.

Para localizar um hotel mais próximo da minha localização com base nas coordenadas (latitude/longitude) temos o código abaixo no evento Click do botão : Encontrar Hotel mais perto:

 Private Sub btnLocalizar_Click(sender As Object, e As EventArgs) Handles btnLocalizar.Click

        Using contexto = New HotelContext
            Dim coordenadas As String = txtLocalizacao.Text
            Dim minhaLocalizacao = DbGeography.FromText("POINT(" + coordenadas + ")") 'Brasilia
            Dim hotel = (From ht In contexto.Hoteis Order By
                              ht.Localizacao.Distance(minhaLocalizacao) _
                              Select ht).FirstOrDefault
            lblResposta.Text = "O hotel mais perto desta localização é : " + vbCrLf + hotel.Nome + vbCrLf + hotel.Cidade + vbCrLf + 
hotel.Localizacao.Latitude.ToString + "-" + hotel.Localizacao.Longitude.ToString
        End Using
    End Sub

 

Note que estamos o método Distance da classe DbGeography para calcular a distância entre os pontos mais próximos das coordenadas da minha localização. No resultado estou usando as propriedades Latitude e Longitude da mesma classe para exibir os respectivos valores das coordenadas.

Nota: Lembre que a classe DbGeography representa dados em um sistema de coordenadas geodésicas

No menu VIEW clique em DataBase Explorer e a seguir clique com o botão direito sobre Data Connections e selecione Add Connection;

Informe o nome do servidor de banco de dados como LocalDB\v11.0 e abra a caixa dropdownlist em Select ou enter a database name;

Perceba que entre os bancos de dados listados podemos identificar um contendo o nome da nossa solução e do nosso contexto. Esse é o banco de dados que foi criado automaticamente pelo Code-First:

Observe que o banco de dados foi criado com o nome EF5_SpatialData.HotelContext que é o formato padrão de nomes usado : Nome da Solucao+Nome do Contexto

Selecionando o banco de dados e expandindo os objetos Tables vemos a tabela Hotels; clicando bom o botão direito do mouse sobre ela e selecionando Show Table Data vermos os dados que foram persistidos pela Code-First.

Conferimos desta a forma a utilização do novo recurso Spatial Data no Entity Framework 5. Pense nas possibilidades...

Pegue o projeto completo aqui: EF5_SpatialData.zip (sem a referência ao EF5)

Mar 1:5 E saíam a ter com ele toda a terra da Judéia, e todos os moradores de Jerusalém; e eram por ele batizados no rio Jordão, confessando os seus pecados.

Mar 1:6 Ora, João usava uma veste de pelos de camelo, e um cinto de couro em torno de seus lombos, e comia gafanhotos e mel silvestre.

Mar 1:7 E pregava, dizendo: Após mim vem aquele que é mais poderoso do que eu, de quem não sou digno de, inclinando-me, desatar a correia das alparcas.

Referência:


José Carlos Macoratti