Compartilhar via


Novidades do Entity Framework 7: armazenamento de dados em memória

O objetivo deste artigo é descrever o mecanismo de armazenamento em memória do Entity Framework 7, um dos recursos que integram a nova versão desta solução de ORM.

Introdução

Bancos relacionais são presença praticamente certa em uma maioria esmagadora de aplicações. A utilização em larga escala de estruturas deste tipo revolucionou a forma como as organizações manipulam suas informações, trazendo agilidade e flexibilidade aos mais variados processos de negócio.

O suporte à interação com bases de dados relacionais está presente nas principais plataformas de desenvolvimento da atualidade. Linguagens compatíveis com o paradigma de Orientação a Objetos costumam fazer uso de soluções de ORM (sigla do inglês “Object-relational mapping”), de forma a simplificar a representação de elementos de um banco a partir de construções como classes.

No caso específico do .NET, o Entity Framework (EF) corresponde a uma das alternativas de ORM mais utilizadas em aplicações criadas nesta plataforma. Concebido pela própria Microsoft, o Entity Framework é atualmente um projeto open source cuja versão mais recente é a 7 (ainda em desenvolvimento).

Dentre as novidades que englobam este novo release estão:

  • A possibilidade de utilização do EF7 em aplicações criadas para a Windows Store, Windows Phone e Universal Apps. Anteriormente a utilização deste framework estava restrita a projetos baseados em tecnologias como ASP.NET, Windows Forms e WPF (Windows Presentation Foundation);
  • Além do tradicional suporte ao SQL Server, na versão 7 também está prevista a compatibilidade nativa com outras fontes de dados como SQLite, SQL Server Compact e PostgreSQL. O novo Entity Framework conta ainda com um mecanismo de armazenamento em memória, dispensando assim a obrigatoriedade de uso de um banco de dados convencional;
  • Com o EF7 está disponível apenas o desenvolvimento através do modo conhecido como Code First, que envolve a codificação das classes que representam as estruturas de uma base a serem mapeadas. A modelagem visual empregando arquivos EDMX não estará mais disponível nesta versão.

Este artigo tem por intenção demonstrar o uso do novo mecanismo de armazenamento em memória do Entity Framework 7. A fim de cumprir tal objetivo será implementado um exemplo prático, cujos detalhes constam nas próximas seções.

Implementando a aplicação de exemplo

Para implementar o projeto apresentado neste artigo foram utilizados os seguintes recursos:

  • O Microsoft Visual Studio Community 2015 Update 1 como IDE de desenvolvimento;
  • A versão 4.6.1 do .NET Framework;
  • O Microsoft ASP.NET MVC 5;
  • O Release Candidate 1 do Entity Framework 7 InMemory.

A solução descrita neste artigo foi disponibilizada no Technet Gallery, podendo ser baixada a partir do link:

https://gallery.technet.microsoft.com/Novidades-do-Entity-dca9cba9

Para o exemplo aqui proposto criar um projeto do tipo “ASP.NET Web Application” chamado “TesteEF7InMemory”:

Selecionar na sequência o template “MVC”:

Será necessário adicionar ainda o package do EntityFramework.InMemory via NuGet, como indicado na próxima imagem:



O próximo passo agora será a criação de uma nova classe chamada “Capital”, a qual será utilizada na exibição de dados descrevendo as diferentes capitais brasileiras. A definição deste tipo pode ser observada na listagem a seguir:

namespace TesteEF7InMemory.Models
{
    public class  Capital
    {
        public string  Estado { get; set; }
        public string  NomeCidade { get; set; }
        public string  Regiao { get; set; }
    }
}

A interação entre o Entity Framework e um repositório de dados (como um banco relacional ou, até mesmo, a própria memória como no caso desta nova versão) acontece por meio de estruturas conhecidas como contextos. Na próxima listagem encontra-se um exemplo desta construção:

  • A classe ExemploContext será responsável por controlar as informações acessadas pela aplicação de testes, herdando do tipo básico DbContext (namespace Microsoft.Data.Entity);
  • O construtor de ExemploContext recebe como parâmetro uma instância da classe DbContextOptions (namespace Microsoft.Data.Entity.Infrastructure), a qual contém configurações utilizadas pelas estruturas básicas do Entity Framework. Esta referência será repassada então ao construtor da superclasse DbContext;
  • A propriedade Capitais faz uso do tipo genérico DbSet<T> (namespace Microsoft.Data.Entity). Este elemento servirá de base para o acesso a uma coleção de instâncias da classe Capital, sendo preenchido automaticamente a partir do tipo ExemploContext;
  • Foi implementada também uma versão sobrecarregada do método OnModelCreating. Esta operação recebe como parâmetro uma instância do tipo ModelBuilder (namespace Microsoft.Data.Entity); a partir deste último objeto será acessado o método genérico Entity, de forma a se configurar a chave primária da classe Capital através da operação HasKey.
using Microsoft.Data.Entity;
using Microsoft.Data.Entity.Infrastructure;
using TesteEF7InMemory.Models;
 
namespace TesteEF7InMemory.Data
{
    public class  ExemploContext : DbContext
    {
        public ExemploContext(DbContextOptions options)
            : base(options)
        {
        }
 
        public DbSet<Capital> Capitais { get; set; }
 
        protected override  void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Capital>().HasKey(c => c.NomeCidade);
        }
    }
}

Já na listagem a seguir está o código que define a classe CapitalRepository. Este tipo estático será utilizado na manipulação de informações sobre capitais, contando para isto com as seguintes construções:

  • O atributo estático “_configEF7” armazenará uma instância da classe genérica DbContextOptionsBuilder<T>, com T apontando para o tipo ExemploContext. Neste objeto constarão configurações para uso do Entity Framework 7;
  • O construtor estático de CapitalRepository será acionado uma única vez pelo runtime do .NET Framework, de forma a proceder com a configuração e carregamento dos dados em memória. Para isto será primeiramente criada e associada ao atributo “_configEF7” uma instância do tipo DbContextOptionsBuilder, através da qual será acionado o método UseInMemoryDatabase. Esta referência servirá de base para a criação de uma nova instância da classe ExemploContext que, por sua vez, será usada pela operação IncluirCapital na geração de novas instâncias do tipo Capital;
  • O método privado IncluirCapital será utilizado na inclusão de informações sobre capitais, fazendo uso para tanto de uma instância da classe ExemploContext. Instâncias criadas por meio desta operação serão armazenadas em memória, com isto acontecendo a partir da chamada do método Add do objeto vinculado à propriedade Capitais;
  • Quanto ao método ListarCapitais, esta operação criará uma nova instância de ExemploContext com base no objeto do tipo DbContextOptionsBuilder gerado anteriormente. Para se retornarem as cidades cadastradas a propriedade Capitais é acionada, além dos métodos OrderBy e ToList.
using System.Collections.Generic;
using System.Linq;
using Microsoft.Data.Entity;
using TesteEF7InMemory.Models;
 
namespace TesteEF7InMemory.Data
{
    public static  class CapitalRepository
    {
        private static  DbContextOptionsBuilder<ExemploContext> _configEF7;
 
        static CapitalRepository()
        {
            _configEF7 = new  DbContextOptionsBuilder<ExemploContext>();
            _configEF7.UseInMemoryDatabase();
 
            using (var context = new ExemploContext(_configEF7.Options))
            {
                IncluirCapital(context, "Acre",  "Rio Branco",
                    "Norte");
                IncluirCapital(context, "Alagoas",  "Maceió",
                    "Nordeste");
                IncluirCapital(context, "Amapá",  "Macapá",
                    "Norte");
                IncluirCapital(context, "Amazonas",  "Manaus",
                    "Norte");
                IncluirCapital(context, "Bahia",  "Salvador",
                    "Nordeste");
                IncluirCapital(context, "Ceará",  "Fortaleza",
                    "Nordeste");
                IncluirCapital(context, "Distrito Federal", "Brasília",
                    "Centro-Oeste");
                IncluirCapital(context, "Espírito Santo", "Vitória",
                    "Sudeste");
                IncluirCapital(context, "Goiás",  "Goiânia",
                     "Centro-Oeste");
                IncluirCapital(context, "Maranhão",  "São Luís",
                     "Nordeste");
                IncluirCapital(context, "Mato Grosso", "Cuiabá",
                     "Centro-Oeste");
                IncluirCapital(context, "Mato Grosso do Sul", "Campo Grande",
                     "Centro-Oeste");
                IncluirCapital(context, "Minas Gerais", "Belo Horizonte",
                     "Sudeste");
                IncluirCapital(context, "Pará",  "Belém",
                     "Norte");
                IncluirCapital(context, "Paraíba",  "João Pessoa",
                     "Nordeste");
                IncluirCapital(context, "Paraná",  "Curitiba",
                     "Sul");
                IncluirCapital(context, "Pernambuco",  "Recife",
                     "Nordeste");
                IncluirCapital(context, "Piauí",  "Teresina",
                     "Nordeste");
                IncluirCapital(context, "Rio de Janeiro", "Rio de Janeiro",
                     "Sudeste");
                IncluirCapital(context, "Rio Grande do Norte", "Natal",
                     "Nordeste");
                IncluirCapital(context, "Rio Grande do Sul", "Porto Alegre",
                     "Sul");
                IncluirCapital(context, "Rondônia",  "Porto Velho",
                     "Norte");
                IncluirCapital(context, "Roraima",  "Boa Vista",
                     "Norte");
                IncluirCapital(context, "Santa Catarina", "Florianópolis",
                     "Sul");
                IncluirCapital(context, "São Paulo",  "São Paulo",
                     "Sudeste");
                IncluirCapital(context, "Sergipe",  "Aracaju",
                     "Nordeste");
                IncluirCapital(context, "Tocantins",  "Palmas",
                     "Norte");
 
                context.SaveChanges();
            }
        }
 
        private static  void IncluirCapital(
            ExemploContext context,
            string estado, string cidade, string regiao)
        {
            context.Capitais.Add(
                new Capital()
                {
                    Estado = estado,
                    NomeCidade = cidade,
                    Regiao = regiao
                });
        }
 
        public static  List<Capital> ListarCapitais()
        {
            using (var context = new ExemploContext(_configEF7.Options))
            {
                return context.Capitais.OrderBy(c => c.Estado).ToList();
            }
        }
    }
}

Para a visualização das informações sobre capitais um novo Controller também será implementado, a partir da seleção do template “MVC 5 Controller - Empty”:

Definir “CapitaisController” como nome desta estrutura:

Na listagem a seguir está a implementação da classe CapitaisController:

  • A Action Index será utilizada para exibir uma listagem com as diferentes capitais brasileiras;
  • Tal visualização será gerada a partir de uma chamada ao método ListarCapitais do tipo CapitalRepository;
  • O retorno da Action Index será uma instância do tipo PartialViewResult (System.Web.Mvc) gerada por meio de uma chamada ao método básico PartialView (herdado da superclasse Controller). Esta característica indica que o resultado de Index será uma Partial View, um tipo de estrutura cujo conteúdo pode ser combinado a outras Views para a geração de uma página HTML.
using System.Web.Mvc;
using TesteEF7InMemory.Data;
 
namespace TesteEF7InMemory.Controllers
{
    public class  CapitaisController : Controller
    {
        public PartialViewResult Index()
        {
            return PartialView(CapitalRepository.ListarCapitais());
        }
    }
}

Por fim, será preciso criar a Partial View para a exibição de informações sobre as capitais, bem como proceder com ajustes na View que conterá tais estruturas (a qual está relacionada à Action Index de HomeController).

No caso da classe CapitaisController, a geração da nova View pode ser feita clicando com o botão direito do mouse sobre o método correspondente; no menu de atalho apresentado selecionar a opção “Add View...”:

Na janela “Add View” selecionar no campo “Model class” o tipo “Capital (TesteEF7InMemory.Models)” e em “Template” o valor “List”. Certificar-se ainda de que foram marcadas as opções “Create as a partial view” e “Reference script libraries”:

Ao se acionar o botão Add da janela “Add View” será criado o arquivo Index.cshtml (cujo conteúdo pode ser observado na próxima listagem). Trata-se de uma View "fortemente tipada", ou seja, através da propriedade Model desta estrutura se dá o acesso a uma coleção de objetos de um tipo específico (no caso, a classe Capital).

@model IEnumerable<TesteEF7InMemory.Models.Capital>
 
<div class="table-responsive">
    <table class="table table-hover">
        <tr>
            <th>Estado</th>
            <th>Capital</th>
            <th>Região</th>
        </tr>
 
        @foreach (var item in Model)
        {
            <tr>
                <td>
                    @item.Estado
                </td>
                <td>
                    @item.NomeCidade
                </td>
                <td>
                    @item.Regiao
                </td>
            </tr>
        }
 
    </table>
</div>

Ajustes precisarão ser realizados também na View Index vinculada à Action de mesmo nome na classe HomeController (este Controller foi gerado automaticamente durante a criação do projeto TesteEF7InMemory), de maneira que a mesma exiba as diferentes capitais através de uma chamada a CapitaisController (via Html.Action). Importante ressaltar que essa estrutura será exibida como página inicial ao se executar a aplicação por meio do Visual Studio.

<div class="jumbotron">
    <h1>ASP.NET x Entity Framework 7</h1>
    <p class="lead">Exemplo de utilização do mecanismo
    InMemory do Entity Framework 7.</p>
</div>
 
@Html.Action("Index", "Capitais")

OBSERVAÇÃO: por questões de simplificação, o código da classe HomeController foi omitido, já que no mesmo não existem instruções complexas que justifiquem uma discussão mais aprofundada (o arquivo correspondente pode ser obtido através do download da solução aqui implementada).

Testes

Será necessário agora executar a aplicação TesteEF7InMemory a partir do Visual Studio. Na imagem a seguir está a tela inicial desta aplicação, na qual é possível visualizar as diferentes capitais brasileiras:

Conclusão

Este artigo procurou apresentar, em linhas gerais, o novo recurso de armazenamento em memória que integra o Entity Framework 7. Conforme demonstrado no exemplo prático, a grande vantagem desta funcionalidade está na possibilidade de desvincular a utilização deste mecanismo de ORM da obrigatoriedade de acesso a uma base relacional.

Devido a tal característica, o Entity Framework se torna uma opção bastante interessante em aplicações criadas como provas de conceito (já que dispensa os desenvolvedores da obrigatoriedade de instalação de um SGBD para testes). Projetos que dependam do armazenamento temporário de informações para posterior persistência também podem se beneficiar deste comportamento.

Referências

Entity Framework Documentation
http://ef.readthedocs.org/en/latest/

Entity Framework - GitHub
https://github.com/aspnet/EntityFramework

What is EF7 all about
https://github.com/aspnet/EntityFramework/wiki/What-is-EF7-all-about