Outubro de 2016
Volume 31 – Número 10
Pontos de Dados - Execute o EF Core no .NET Framework e no .NET Core
A tecnologia antes conhecida como Entity Framework 7 (EF7) passou a se chamar Entity Framework Core (EF Core) no início de 2016. O EF Core 1.0.0 introduz novos e excelentes recursos, embora, no todo, tenha um conjunto de funcionalidades menor que o EF6. Isso, porém, não significa que o EF seja executado apenas no .NET Core. Você pode usar o EF Core em APIs e aplicativos que requerem o .NET Framework completo e, também, naqueles que se destinam apenas ao .NET Core entre plataformas. Nesta coluna, vou guiar você por dois projetos que exploram essas opções. Meu objetivo é afastar uma preocupação que o moniker “Core” possa causar: a de que o EF Core só é executado no .NET Core. Ao mesmo tempo, vou explicar as etapas envolvidas na criação de cada solução.
EF Core em Projetos para .NET Completos
Vou começar com um projeto destinado ao .NET Framework completo. Não se esqueça de que no Visual Studio 2015, as ferramentas requerem que você tenha o Visual Studio 2015 Atualização 3, além da última versão do Microsoft ASP.NET e do Web Tools. No momento em que esta coluna está sendo escrita (agosto de 2016), o melhor guia para essas instalações é o documento disponível em bit.ly/2bte6Gu.
Para manter meu acesso a dados separado de qualquer aplicativo que o use, vou criá-lo em uma biblioteca de classes própria. A Figura 1 mostra que há um modelo que se destina especificamente a uma biblioteca de classes .NET Core, mas eu vou selecionar Biblioteca de Classes, a opção padrão que está sempre disponível para direcionamento para o .NET. O projeto resultante (Figura 2) também é “normal”. Você pode ver que não há arquivos project.json ou ativos de projeto .NET Core. Tudo está do jeito que sempre esteve.
Figura 1 Como Criar uma Biblioteca de Classes para a API do .NET Completo
Figura 2 A Boa e Velha (e Conhecida) Biblioteca de Classes .NET
Até agora, nada disso está vinculado ao EF. Neste momento, eu poderia escolher tanto o EF6 quanto o EF Core, mas agora vou adicionar o EF Core ao projeto. Como sempre, posso usar o Gerenciador de Pacotes NuGet para encontrar e selecionar o EF Core ou a janela do Console do Gerenciador de Pacotes. Vou usar o console. Lembre-se que o pacote “entityframework” é para o EF6. Para obter o EF Core, você precisa instalar um dos pacotes Microsoft.EntityFrameworkCore. Vou usar o pacote SqlServer, que vai trazer o que o EF precisa para se comunicar com o SqlServer:
install-package Microsoft.EntityFrameworkCore.SqlServer
Como o pacote depende do pacote principal Microsoft.EntityFrameworkCore e do pacote Microsoft.EntityFrameworkCore.Relational, o NuGet vai instalar os dois para mim ao mesmo tempo. E, como o pacote do EF Core depende de outros pacotes, eles também serão instalados. No total, esse processo adiciona os três pacotes EF Core e outros 23 a partir do .NET, mais recente e mais combinável, do qual o EF Core depende. Em vez de poucos pacotes grandes, eu obtenho vários pacotes pequenos, mas apenas aqueles que meu software precisa. Eles vão se entender bem com as bibliotecas .NET padrão que já estão no projeto.
Em seguida, vou adicionar uma classe de domínio simples (Samurai.cs) e um DbContext (SamuraiContext.cs) para permitir que o EF Core persista meus dados em um banco de dados, como mostrado na Figura 3. O EF Core não tem a inferência mágica de cadeia de conexão que o EF6 tem, portanto, é preciso informar qual provedor e qual cadeia de conexão eu estou usando. Para maior simplicidade, vou colar isso diretamente no novo método virtual do DBContext: o OnConfiguring. Também criei uma sobrecarga de construtor para permitir passar adiante o provedor e outros detalhes necessários. Vou tirar vantagem disso em breve.
Figura 3 Classe Samurai e Classe DbContext SamuraiContext
public class Samurai
{
public int Id { get; set; }
public string Name { get; set;}
}
public class SamuraiContext : DbContext
{
public DbSet<Samurai> Samurais { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
{
if (optionsBuilder.IsConfigured == false) {
optionsBuilder.UseSqlServer(
@"Data Source=(localdb)\\mssqllocaldb;Initial Catalog=EFCoreFullNet;
Integrated Security=True;");
}
base.OnConfiguring(optionsBuilder);
}
}
public SamuraiContext(DbContextOptions<SamuraiContext> options)
: base(options) { }
}
Como estou usando o .NET completo, também estou direcionando para o Windows completo, por isso tenho o Windows PowerShell disponível. Isso significa que posso usar os mesmos comandos de migração de sempre: add-migration, update-database e por aí vai. Também existem alguns comandos novos e você pode ler minha coluna de janeiro de 2016 (msdn.com/magazine/mt614250) para aprender tudo sobre os comandos de migração do EF Core. Você se lembra que mencionei que os pacotes são menores e combináveis? Bem, se eu quiser usar migrações, preciso adicionar o pacote que contém esses comandos. No momento em que escrevo, as ferramentas ainda estão em modo de visualização, por isso é preciso usar o parâmetro -pre. Vou adicionar o pacote, depois posso adicionar uma nova migração:
install-package Microsoft.EntityFrameworkCore.Tools –pre
add-migration init
O funcionamento é o de sempre: ele cria uma nova pasta Migrações e o arquivo de migração, como mostrado na Figura 4. O EF Core mudou a forma de armazenamento de instantâneos de modelos. Você também pode ler sobre isso na coluna de janeiro de 2016.
Figura 4 Migrações EF Core em Minha Biblioteca de Classes .NET Completo
Com a migração pronta, o comando update-database cria com êxito o novo banco de dados EFCoreFullNet no localdb do SQL Server.
Por fim, vou adicionar um projeto de teste para a solução no mesmo modelo de Projeto de Teste de Unidade que sempre usei no Visual Studio. Em seguida, adicionarei uma referência à minha biblioteca de classes EFCoreFullNet. Não preciso que meu projeto de teste use o banco de dados para verificar se o EF Core está funcionando, por isso, em vez de instalar o pacote SqlServer, vou executar o comando do NuGET no novo projeto de teste:
install-package microsoft.EntityFrameworkCore.InMemory
O provedor InMemory é uma mão na roda para testes com o EF Core. Ele usa dados em memória para representar o cache do banco de dados e do EF e o EF Core interagirá com o cache basicamente da mesma maneira como trabalha com um banco de dados: adicionando, removendo e atualizando dados
Você se lembra do construtor extra que criei no SamuraiContext? Os testes TestEFCoreFullNet, exibidos na Figura 5, se beneficiam dele. Observe que criei um construtor DbContextOptions para o SamuraiContext no construtor da classe de teste, depois especifiquei que ele deve usar o provedor InMemory. Então, no método, quando instancio o SamuraiContext, passo essas opções adiante. O método OnConfiguring do SamuraiContext foi projetado para fazer uma verificação e ver se as opções já estão configuradas. Se estiverem, ele vai usá-las (neste caso, o provedor InMemory). Caso contrário, seguirá em frente com a configuração para trabalhar com o SqlServer e a cadeia de conexão que codifiquei no método.
Figura 5 Testes com o EFCore
using EFCoreFullNet;
using Microsoft.EntityFrameworkCore;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Linq;
namespace Tests
{
[TestClass]
public class TestEFCoreFullNet
{
private DbContextOptions<SamuraiContext> _options;
public TestEFCoreFullNet() {
var optionsBuilder = new DbContextOptionsBuilder<SamuraiContext>();
optionsBuilder.UseInMemoryDatabase();
_options = optionsBuilder.Options;
}
[TestMethod]
public void CanAddAndUpdateSomeData() {
var samurai = new Samurai { Name = "Julie" };
using (var context = new SamuraiContext(_options)) {
context.Add(samurai);
context.SaveChanges();
}
samurai.Name += "San";
using (var context = new SamuraiContext(_options)) {
context.Samurais.Update(samurai);
context.SaveChanges();
}
using (var context = new SamuraiContext(_options)) {
Assert.AreEqual("JulieSan", context.Samurais.FirstOrDefault().Name);
}
}
}
}
Este método de teste se beneficia de algumas funcionalidades específicas do EF Core que não existem no EF6. Escrevi sobre essas e outras funcionalidades de controle de alterações do EF Core em minha coluna Pontos de Dados de agosto de 2016 (msdn.com/magazine/mt767693). Por exemplo, depois de criar o novo objeto samurai, eu o adicionei ao contexto usando o método DbContext.Add, deixando o EF determinar a que DbSet ele precisava ser vinculado. Depois, salvei tudo no armazenamento de dados, neste caso um tipo de lista em memória que o provedor InMemory está gerenciando. Em seguida, modifiquei o objeto samurai, criei uma nova instância do DbContext e usei o novo comando Atualizar do EF Core para garantir que o SaveChanges atualizará o samurai armazenado em vez de criar um novo. Por fim, consultei o contexto para o samurai e usei um Assert para garantir que o contexto realmente retorne o nome atualizado.
Essas funcionalidades que estou usando não são o xis da questão, no entanto. A questão é que estou fazendo todo esse trabalho com o EF Core em um projeto do “bom e velho .NET” no Windows.
EF Core para CoreCLR: Mesmo Código, Dependências Diferentes
Eu poderia continuar no Windows e no Visual Studio 2015 Atualização 3 para mostrar em seguida como usar as mesmas APIs do EF Core, o mesmo código e os mesmos testes para direcionar para o tempo de execução do CoreCLR, mas isso seria parecido demais com direcionar para o Windows. Por isso, vou para o outro extremo e criar a variação CoreCLR em meu MacBook, explicando as etapas à medida em que vão sendo feitas.
O .NET Core não depende do Windows ou de suas ferramentas. Além do Visual Studio 2015, eu posso usar… bem, imagino que o Emacs era o editor popular para quem não usa Visual Studio. Entretanto, posso escolher alguns IDEs entre plataformas, não só para gravar o código, mas também para obter funcionalidades como depuração e suporte ao Git. Por exemplo, na edição de agosto de 2016 da MSDN Magazine, Alessandro Del Sole detalhou a construção de um site ASP.NET Core usando o Visual Studio Code (msdn.com/magazine/mt767698). Vi pelas capturas de tela que ele estava no Windows, mas, caso não estivesse, a experiência seria basicamente a mesma em um Mac.
Outra opção entre plataformas é o Rider da JetBrains. O Rider foi projetado especificamente para C# e a melhor maneira de descrevê-lo é como o “ReSharper em IDE próprio”.
Já venho usando o Visual Studio Code no Windows e no OS X (não só para C#, mas também para Node.js) e é ele que vou usar para mostrar o EF Core em um aplicativo direcionado para CoreCLR. Na verdade, como estou construindo a solução no OS X, direcionar para o CoreCLR é minha única opção. A matriz de APIs disponíveis para minha biblioteca é mais limitada. No entanto, o EF Core é o mesmo conjunto de APIs que usei na biblioteca do .NET completo no primeiro projeto.
Como você vai ver, a maior dificuldade será configurar projetos e dependências específicos para o direcionamento para o CoreCLR. Posso, no entanto, usar a classe SamuraiContext para definir meu modelo de dados do EF Core e o método de teste CanAddAndUpdateSomeData do projeto anterior para fazer o mesmo trabalho. O código é o mesmo, embora agora eu esteja direcionando para um tempo de execução mais limitado e trabalhando em um ambiente que só pode usar o .NET Core.
Como Criar uma Biblioteca Semelhante à Biblioteca de Classes .NET
Criei uma pasta para conter minha Biblioteca e os projetos Teste com subpastas para cada projeto. Dentro da subpasta Biblioteca, posso chamar dotnet new para criar o projeto da Biblioteca. A Figura 6 mostra o comando juntamente com a confirmação de que o projeto foi criado. A lista do conteúdo da pasta mostra que apenas dois arquivos foram criados. O mais importante, project.json, contém a lista de pacotes NuGet necessários e outros detalhes relevantes do projeto. O arquivo Library.cs é apenas um arquivo de classe vazio que vou excluir.
Figura 6 Como Criar uma Nova Biblioteca CoreCLR com o Comando dotnet
A seguir, vou abrir esse novo projeto de biblioteca no Visual Studio Code. Posso simplesmente digitar “code” no prompt. O Visual Studio Code é aberto com isso como a pasta de destino, reconhece automaticamente os pacotes listados no arquivo json e se oferece para executar a recuperação do dotnet para corrigir as dependências não resolvidas. Aceito a oferta com alegria.
O arquivo project.json se parece com o código da Figura 7.
Figura 7 O Arquivo Project.json
{
"version": "1.0.0-*",
"buildOptions": {
"debugType": "portable"
},
"dependencies": {},
"frameworks": {
"netstandard1.6": {
"dependencies": {
"NETStandard.Library": "1.6.0"
}
}
}
}
Muito simples. As bibliotecas não precisam de toda a parafernália ASP.NET Core que costumo usar, apenas da Biblioteca NETStandard. A Biblioteca Standard .NET. encapsula o que é comum nos vários locais em que o .NET pode ser executado agora. O documento Standard do .NET (bit.ly/2b1JoHJ) diz: “A Biblioteca Padrão do .NET é uma especificação formal das APIs do .NET que devem estar disponíveis em todos os tempos de execução do .NET.” Logo, a biblioteca que estou construindo pode ser usada com aplicativos .NET Core e ASP.NET Core e até mesmo .NET a partir do .NET 4.5. Você pode ver uma grade de compatibilidade na página de documentação.
Meu próximo passo é adicionar o EF Core ao projeto. Não se esqueça que, por estar em um Mac, o SqlServer não é uma opção. Em vez disso, vou usar o provedor PostgreSQL para EF Core, que entra na seção de dependências do project.json, atualmente vazias:
"dependencies": {
"Npgsql.EntityFrameworkCore.PostgreSQL": "1.0.0-*"
},
"tools": {
"Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final"
},
Como antes, pretendo fazer migrações. Normalmente, eu também adicionaria uma dependência para o pacote Microsoft.EntityFrameworkCore.Tools que contém os comandos, como fiz para a versão do .NET completo dessa biblioteca. Porém, por causa de uma limitação atual das ferramentas da Visualização 2, vou adiar isso para uma etapa posterior do processo. No entanto, ainda preciso acessar os comandos na pasta da biblioteca, por isso vou adicionar o pacote em uma seção especial de “ferramentas” do project.json, como mostra o código precedente.
A restauração dos pacotes puxa não só esses dois pacotes, mas também suas dependências. Se examinar o arquivo project.lock.json que foi criado, você verá todos os pacotes, incluindo Microsoft.EntityFrameworkCore e Microsoft.EntityFrameworkCore.Relational. Os mesmos que foram adicionados à solução .NET anterior.
Agora, basta copiar meus arquivos Samurai.cs e SamuraiContext.cs. Preciso alterar a classe OnConfiguring para usar PostgreSQL e sua cadeia de conexão em vez do SQL Server. É esta aparência do trecho de código:
optionsBuilder.UseNpgsql(
"User ID=julie;Password=12345;Host=localhost;Port=5432;Database=EFCoreCoreCLR;
Pooling=true;");
Deveria ser hora de executar as migrações, mas, neste momento, você se deparará com uma limitação conhecida da versão atual da Visualização 2 das ferramentas do EF Core fora do Visual Studio, que exige um projeto executável para encontrar ativos críticos. Então, novamente, dá um certo trabalho na primeira vez, mas não é um esforço extra tão grande. Leia mais sobre isso em bit.ly/2btm4OW.
Criando o Projeto de Teste
Vou seguir em frente e adicionar meu projeto de teste que posso usar, em seguida, como projeto executável para as migrações. De volta à linha de comando, vou até a subpasta Oct2016DataPointsMac/Test criada anteriormente e executo:
dotnet new -t xunittest
No Visual Studio Code, você verá o novo project.json listado na pasta Teste. Como esse projeto será responsável por garantir que as linhas de comando do EF possam ser executadas, é preciso adicionar uma referência aos pacotes de Ferramentas do EF Core nas dependências. Adicionalmente, o projeto de teste precisa de uma referência à Biblioteca, por isso também a adicionei às dependências do project.json. Veja a seção de dependências após as adições:
"dependencies": {
"System.Runtime.Serialization.Primitives": "4.1.1",
"xunit": "2.1.0",
"dotnet-test-xunit": "1.0.0-rc2-192208-24",
"Library": "1.0.0",
"Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final"
},
Agora posso acessar os comandos do EF Core em minha pasta Biblioteca. Observe que, na Figura 8, o comando aponta para o projeto na pasta Teste com o parâmetro --startup-project. Vou usar isso em cada um dos comandos de migrações.
Figura 8 Como Aproveitar um Projeto Executável para Habilitar uma Biblioteca a Usar os Controles de Migrações do EF
Como Executar Migrações no Modelo EF na Biblioteca .NET
Lembre-se que, como mostrei em minha coluna sobre migrações do EF Core, os comandos de migrações dotnet ef parecem diferentes dos comandos do PowerShell, mas levam à mesma lógica na API de migrações.
Em primeiro lugar, vou criar a migração com:
dotnet ef --startup-project ../Test migrations add init
Isso gera o mesmo resultado que a solução .NET: uma nova pasta Migrações com a migração e o instantâneo da migração adicionados.
Agora posso criar o banco de dados com:
dotnet ef --startup-project ../Test database update
Verifiquei então que o banco de dados PostgreSQL, as tabelas e os relacionamentos foram criados. Existem várias ferramentas que você pode usar no OS X para fazer isso. Em meu Mac, uso o DataGrip entre plataformas da JetBrains como IDE de banco de dados.
Como Executar os Testes no CoreCLR
Por fim, copio a classe TestEFCoreFullNet da solução anterior em minha pasta Teste. Mais uma vez, é preciso fazer mudanças na infraestrutura para usar o xUnit em vez do MS Test: mudar algumas coisas no namespace, remover o atributo TestClass, substituir os atributos TestMethod por Fact e substituir Assert.AreEqual por Assert.Equal. Ah, e renomear a classe para TestEFCoreCoreClr, é claro.
O project.json também precisa saber sobre o provedor InMemory, então eu adiciono:
"Microsoft.EntityFrameworkCore.InMemory": "1.0.0"
à seção de dependências também, depois executo “dotnet restore” mais uma vez.
Meu projeto de teste xUnit usa o executor de teste de linha de comando do xUnit. Então, volto à janela do terminal para executar os testes com o comando dotnet test. A Figura 9 mostra o resultado da execução do teste que foi aprovado com louvor, com a exceção de que o executor de teste de linha de comando não fornece o resultado verde satisfatório para testes aprovados.
Figura 9 Resultado de Teste do xUnit do Teste Aprovado
.NET ou CoreCLR: Mesmas APIs, Mesmo Código
Agora você pode ver que o código e os assemblies relacionados ao EF Core são os mesmos, não importa se o software se destina a execução apenas no Windows com o .NET Framework completo à sua disposição ou no CoreCLR em qualquer um dos ambientes com suporte (Linux, OS X, Windows). Eu poderia ter feito ambas as demonstrações no Visual Studio 2015 em meu computador com Windows. Concluí, no entanto, que me concentrar no trabalho do CoreCLR em um ambiente que está completamente indisponível para o .NET Framework completo é uma maneira reveladora de demonstrar que as APIs do EF e meu código relacionado ao EF são absolutamente os mesmos em ambos os locais. As grandes diferenças e todo o trabalho extra estão relacionados apenas às plataformas de destino (.NET vs. CoreCLR). Você pode ver como criei uma API Web do ASP.NET Core completo usando o EF Core 1.0.0 em meu MacBook no vídeo “Primeira Impressão sobre o EF Core 1.0” (bit.ly/2cmblqE). Para uma demonstração abreviada e divertida do mesmo processo, confira o vídeo em minha sessão no DotNetFringe em bit.ly/2ci7q0T.
Julie Lerman é MVP da Microsoft, mentora e consultora do .NET, que reside nas colinas de Vermont. Você pode encontrá-la em apresentações sobre acesso de dados ou sobre outros tópicos .NET em grupos de usuários e conferências em todo o mundo. Ela escreve no blog thedatafarm.com/blog e é autora do "Programming Entity Framework", bem como de uma edição do Code First e do DbContext, todos da O'Reilly Media. Siga-a no Twitter em @julielerman e confira seus cursos da Pluralsight em juliel.me/PS-Videos.
Agradecemos aos seguintes especialistas técnicos da Microsoft pela revisão deste artigo: Jeff Fritz
Jeffrey T. Fritz é gerente de programas sênior da equipe do ASP.NET da Microsoft que trabalha no Web Forms e no ASP.Net Core. Desenvolvedor para a Web de longa data, com experiência em grandes e pequenos aplicativos em uma grande variedade de segmentos verticais, ele sabe como compilar para obter desempenho e praticidade.