Thomas  Granger

Thomas Granger

1660203673

Cross-platform SQLite Database in .NET MAUI App with EF Core

C# and LINQ for data access with EF Core. Learn how Entity Framework (EF) Core simplifies working with a cross-platform SQLite database in a .NET MAUI app and handles everything from complex queries to multiple updates that execute with a single line of developer code.

Forget magic strings, hand-written SQL queries and unfamiliar SDKs to deal with local data in your .NET MAUI apps. Entity Framework Core, also known as EF Core, is an object-mapper that empowers .NET developers to work with databases using the .NET languages and type system they know and love. In this session, learn how EF Core simplifies working with a cross-platform SQLite database in a .NET MAUI app and handles everything from complex queries to multiple updates that execute with a single line of developer code. Discover how client developers can use the same APIs that drive most REST, GraphQL, and gRPC backends on the server!

#dotnet #csharp #efcore #entityframework #linq #sqlite

 

Cross-platform SQLite Database in .NET MAUI App with EF Core

Using Entity Framework Core and Dapper in ASP.NET Core

In this article, we will learn about Using Entity Framework Core and Dapper in ASP.NET Core together in the same application. Another major point of discussion will be Transactions. By the end of the article, we will have an application that works with both Entity Framework Core and Dapper alongside each other, but also intelligent enough to rollback data whenever there is an exception with the process.

Let’s get started!

Dapper vs Entity Framework Core

Dapper is literally much faster than Entity Framework Core considering the fact that there are no bells and whistles in Dapper. It is a straight forward Micro ORM that has minimal features as well. It is always up to the developer to choose between these 2 Awesome Data Access Technologies. This does not mean that Entity Framework Core is any slower. With every update, the performance seems to be improving as well. Dapper is heaven for those who still like to work with RAW Queries rather than LINQ with EFCore.

See more at: https://codewithmukesh.com/blog/using-entity-framework-core-and-dapper/

#entityframework 

Using Entity Framework Core and Dapper in ASP.NET Core

Usando Entity Framework Core e Dapper no ASP.NET Core

Neste artigo, aprenderemos sobre como usar o Entity Framework Core e o Dapper no ASP.NET Core juntos no mesmo aplicativo. Outro grande ponto de discussão serão as Transações. Ao final do artigo, teremos um aplicativo que funciona com Entity Framework Core e Dapper lado a lado, mas também inteligente o suficiente para reverter dados sempre que houver uma exceção no processo.

Vamos começar!

Dapper vs Entity Framework Core

O Dapper é literalmente muito mais rápido que o Entity Framework Core, considerando o fato de que não há sinos e assobios no Dapper. É um Micro ORM direto que também possui recursos mínimos. Cabe sempre ao desenvolvedor escolher entre essas 2 tecnologias impressionantes de acesso a dados. Isso não significa que o Entity Framework Core seja mais lento. A cada atualização, o desempenho parece estar melhorando também. Dapper é o paraíso para quem ainda gosta de trabalhar com consultas RAW em vez de LINQ com EFCore.

Agora, o Entity Framework Core tem vários recursos incluídos junto com melhorias de desempenho também. Então, essa pergunta é: Por que escolher entre Dapper e Entity Framework Core quando você pode usar os dois e tirar o máximo proveito, sim?

Dapper é super incrível para lidar com consultas complexas que ostentam várias junções e alguma lógica de negócios muito longa. O Entity Framework Core é ótimo para geração de classes, rastreamento de objetos, mapeamento para várias classes aninhadas e muito mais. Portanto, geralmente é Desempenho e Recursos quando se fala desses 2 ORMs.

Requerimento

Vamos projetar uma WebAPI ASP.NET Core simples para uma empresa imaginária. Esta empresa tem uma política que diz que todos os outros funcionários devem estar vinculados a um departamento único. Para ser mais claro, toda vez que você adiciona um novo funcionário por meio do endpoint da API, também é necessário criar um novo registro de departamento. Um requisito muito imaginário, sim? Junto com isso, teremos mais 2 endpoints que retornam todos os Employees e Employee por Id.

Expandindo os detalhes, teremos que garantir que o Departamento recém-adicionado ainda não exista. Você entenderá isso assim que conseguir ver as Entidades do Domínio.

Para demonstrar o uso do Dapper, Entity Framework Core e ambos combinados, implementaremos cada um deles nos 3 Endpoints. Para os endpoints GetAll, usaremos o Dapper. O ponto de extremidade GetById usaria o Entity Framework Core com Eager Loading para exibir os detalhes do departamento também. E, finalmente, o POST Endpoint aproveitaria essas incríveis tecnologias de acesso a dados e transações de demonstração limpa no ASP.NET Core.

Ao longo do caminho, seremos apresentados a algumas bibliotecas para ASP.NET Core que provavelmente também podem economizar algum tempo de desenvolvimento.

Aspecto Importante a Lidar - Transações

Agora, de acordo com nosso requisito, precisamos que o Entity Framework Core e o Dapper trabalhem juntos. Isso é muito fácil de conseguir na verdade. Mas o detalhe importante a ser observado é que precisamos garantir que o Entity Framework Core e o Dapper participem da mesma transação de banco de dados para que o processo geral possa ser robusto.

Por exemplo, uma operação de gravação específica pode envolver várias entidades e tabelas. Isso, por sua vez, pode ter operações fáceis de serem manipuladas pelo Entity Framework Core e, digamos, um monte de consultas complexas que devem ser executadas pelo Dapper. Nesses casos, devemos garantir que seja possível reverter a operação SQL Execute quando qualquer operação/consulta falhar. Faz sentido? Este é o aspecto que pode introduzir uma pequena complexidade no projeto do nosso sistema.

Se não considerarmos isso, o processo geral seria muito simples. Deixe-me colocar a ideia em etapas.
1. Configure o Entity Framework Core.
2. Configure o Dapper. Você pode conseguir isso usando a mesma cadeia de conexão que também está sendo usada pelo EFCore. (Obviamente, de appsettings.json)
3. Registre os serviços no Container e comece a usar o Context/Dapper conforme necessário.

Mas iremos para um mecanismo mais complexo e à prova de futuro que irá lidar realmente com tudo, incluindo Rollbacks e Transações. Pegue?

Usando Entity Framework Core e Dapper no ASP.NET Core

Configurando a solução e os projetos

Seguiremos uma Onion Architecture como de costume para formar uma separação limpa de preocupações. Em primeiro lugar, abra o Visual Studio 2019 e crie uma nova solução em branco. Aqui vamos adicionar 4 novos projetos, nomeadamente Domínio, Persistência e WebApi. O Domínio e os Projetos de Persistência são Biblioteca de Classes .NET Core, enquanto o WebApi é um Aplicativo Web ASP.NET Core 3.1 com o Modelo de API selecionado.

O Projeto de Domínio consistirá apenas nas Entidades e Interfaces do Domínio Principal. A Camada de Persistência deve ter a Implementação das Interfaces, Serviços e tudo relacionado ao Entity Framework Core e Dapper. Por fim, o Projeto WebApi teria os Endpoints da API.

Observe que esta é uma implementação mínima da Onion / Hexagonal Architecture. Para um tutorial muito mais detalhado sobre a arquitetura, consulte este artigo – Onion Architecture In ASP.NET Core With CQRS – Detalhado

Com base nos detalhes que você aprendeu no artigo mencionado acima, aqui está uma implementação completa da Arquitetura Limpa no ASP.NET Core WebAPI disponível como um Modelo Boilerplate para você começar rapidamente – ASP.NET Core WebAPI – Arquitetura Limpa ( Projeto de código aberto)

Agora, vamos começar a instalar todos os pacotes necessários para cada projeto.

Pacotes para Projeto de Domínio

Está bem claro que o Projeto de Domínio NUNCA deve depender de mais nada. Mas como estamos combinando as camadas de Domínio e Aplicação em uma única entidade, e para manter as coisas simples, vamos instalar esses 2 pacotes no Projeto de Domínio.

Install-Package Microsoft.EntityFrameworkCore

Pacotes para Projeto de Persistência

Conforme mencionado anteriormente, tudo relacionado à Infraestrutura/Persistência da Solução será colocado neste Projeto. Vá em frente e instale os seguintes pacotes no Persistence Project.

Install-Package Dapper
Install-Package Microsoft.EntityFrameworkCore.Design
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Install-Package Microsoft.EntityFrameworkCore.Relational
Install-Package System.Data.SqlClient

Pacotes para o Projeto WebApi

Por fim, adicione esses pacotes ao projeto WebAPI.

Install-Package Microsoft.EntityFrameworkCore.Tools

Adicionando as Entidades de Domínio

Navegue até o Projeto de Domínio e crie uma nova Entidade de Pasta e adicione essas 2 classes a ela, Funcionário e Departamento.

namespace Domain.Entities
{
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
}
using AspNetCoreHero.Abstractions;
namespace Domain.Entities
{
public class Employee : BaseEntity
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public int DepartmentId { get; set; }
public Department Department { get; set; }
}
}

Projetando as interfaces

Novamente, dentro do projeto de domínio, adicione outra pasta, Interfaces. Aqui estamos usando o conceito de Inversão de Dependência, para que o sistema não se importe com a implementação e se responsabilize apenas por fornecer o Contrato sobre o que ele realmente deseja. Com essa abordagem, é mais fácil projetar sistemas limpos e simples que também são ideais para testes. Dessa forma, o núcleo da solução não depende de nada como Entity Framework ou Dapper, mas também facilita a mudança para diferentes tecnologias.

Vamos adicionar nossa primeira interface, IApplicationDbContext.cs, que deve ser implementada na camada de persistência usando o Entity Framework Core.

public interface IApplicationDbContext
{
public IDbConnection Connection { get; }
DatabaseFacade Database { get; }
public DbSet<Employee> Employees { get; set; }
public DbSet<Department> Departments { get; set; }
Task<int> SaveChangesAsync(CancellationToken cancellationToken);
}

Aqui você pode ver que estamos usando 2 novas propriedades, IDbConnection e DbFacade . Este é apenas um acesso direto ao Banco de Dados que será usado pelo Dapper posteriormente. Lembre-se, falamos sobre fazer o EFCore e o Dapper trabalharem juntos?

Em seguida, vamos adicionar mais 2 interfaces que são especificamente destinadas a leitura e gravação. A interface de leitura terá os contratos para executar consultas apenas no banco de dados. enquanto a Interface de Gravação será um pacote completo.

Observe que também estamos usando IDbTransaction, que será útil quando começarmos a lidar com transações posteriormente neste artigo.

public interface IApplicationReadDbConnection
{
Task<IReadOnlyList<T>> QueryAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default);
Task<T> QueryFirstOrDefaultAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default);
Task<T> QuerySingleAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default);
}
public interface IApplicationWriteDbConnection : IApplicationReadDbConnection
{
Task<int> ExecuteAsync(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default);
}

Isso é quase tudo o que precisamos fazer com o Projeto Domínio. Vamos começar as implementações agora.

Configurando EntityFrameworkCore

No Persistence Project vamos adicionar um novo Folder Contexts e adicionar o arquivo ApplicationDbContext.cs.

public class ApplicationDbContext : DbContext, IApplicationDbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{
}
public DbSet<Employee> Employees { get; set; }
public DbSet<Department> Departments { get; set; }
public IDbConnection Connection => Database.GetDbConnection();
}

Você pode ver que, na Linha 8, estamos extraindo o objeto Connection da instância do Entity Framework Core.

Para saber mais sobre o Entity Framework Core em detalhes, consulte o artigo a seguir – Entity Framework Core no ASP.NET Core 3.1 – Introdução

Configurando o Dapper

Agora, no projeto Persistence, adicione outra pasta e nomeie-a como Connections. Ambas são interfaces de leitura e gravação que serão implementadas aqui. Adicione as seguintes classes.

public class ApplicationReadDbConnection : IApplicationReadDbConnection, IDisposable
{
private readonly IDbConnection connection;
public ApplicationReadDbConnection(IConfiguration configuration)
{
connection = new SqlConnection(configuration.GetConnectionString("DefaultConnection"));
}
public async Task<IReadOnlyList<T>> QueryAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return (await connection.QueryAsync<T>(sql, param, transaction)).AsList();
}
public async Task<T> QueryFirstOrDefaultAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return await connection.QueryFirstOrDefaultAsync<T>(sql, param, transaction);
}
public async Task<T> QuerySingleAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return await connection.QuerySingleAsync<T>(sql, param, transaction);
}
public void Dispose()
{
connection.Dispose();
}
}

Certifique-se de estar implementando a interface IDisposible também.

No construtor você pode ver que estamos inicializando uma nova conexão usando a string de conexão do nosso appSettings.json. Observe que nesta implementação, não há link com nenhum objeto DBContext (Entity Framework Core) porque realmente não faz sentido compartilhar as conexões entre o Entity Framework Core e o Dapper quando você está lendo os dados.

O caso de uso de compartilhar a conexão entra em cena quando há gravação de dados envolvida. Vamos ver como ele é implementado.

public class ApplicationWriteDbConnection : IApplicationWriteDbConnection
{
private readonly IApplicationDbContext context;
public ApplicationWriteDbConnection(IApplicationDbContext context)
{
this.context = context;
}
public async Task<int> ExecuteAsync(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return await context.Connection.ExecuteAsync(sql, param, transaction);
}
public async Task<IReadOnlyList<T>> QueryAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return (await context.Connection.QueryAsync<T>(sql, param, transaction)).AsList();
}
public async Task<T> QueryFirstOrDefaultAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return await context.Connection.QueryFirstOrDefaultAsync<T>(sql, param, transaction);
}
public async Task<T> QuerySingleAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return await context.Connection.QuerySingleAsync<T>(sql, param, transaction);
}
}

Na escrita implementada, nós realmente temos uma implementação de conexão completa com funcionalidades de leitura e escrita. Você também pode evitar a funcionalidade de leitura. Mas depende da sua preferência.

Você pode ver que estamos injetando o IApplicationDbContext que pertence ao Entity Framework no Constructor. É assim que podemos compartilhar a conexão e a transação. Usando a conexão do contexto, realizamos as operações de leitura e escrita usando o Dapper. Muito legal, sim?

Na implementação Read estávamos trabalhando diretamente com o objeto IDbConnection que inicializamos no construtor usando a string de conexão. Enquanto aqui na implementação Write estamos reutilizando o objeto de contexto para executar consultas e comandos com a ajuda do Dapper.

Para saber mais sobre o Dapper em detalhes, consulte o artigo a seguir – Dapper no ASP.NET Core com padrão de repositório – detalhado

Adicionando a cadeia de conexão

Abra seu appsettings.json e adicione sua string de conexão.

"ConnectionStrings": {
"DefaultConnection": "Data Source=LAPTOP-7CS9KHVQ;Initial Catalog=demoDb;Integrated Security=True;MultipleActiveResultSets=True"
},

Cadastrando os serviços

Por fim, vamos registrar essas interfaces e classes no contêiner de serviço de nosso aplicativo Web ASP.NET Core. Abra seu Startup.cs no projeto WebAPI e modifique seu ConfigureServices da seguinte maneira.

public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options
.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))
);
services.AddScoped<IApplicationDbContext>(provider => provider.GetService<ApplicationDbContext>());
services.AddScoped<IApplicationWriteDbConnection, ApplicationWriteDbConnection>();
services.AddScoped<IApplicationReadDbConnection, ApplicationReadDbConnection>();
services.AddControllers();
}

Adicionando Migrações e Atualizando o Banco de Dados

Clique com o botão direito do mouse no Projeto WebAPI e certifique-se de que ele esteja definido como o Projeto de inicialização da solução. Em seguida, abra o Console do Gerenciador de Pacotes e defina o projeto padrão para Infraestrutura no menu suspenso.

Digite o seguinte para adicionar as migrações e atualizar o banco de dados com as Tabelas de Funcionários e Departamentos.

add-migration initial
update-database

Fiação com o controlador

Como cuidamos de todo o trabalho pesado, vamos construir nosso controlador com os 3 endpoints mencionados. Adicione um novo Controller na pasta Controllers e nomeie-o como Employee Controller.

Vamos injetar todas as 3 interfaces que criamos anteriormente no Construtor do Controlador. Idealmente, você usaria uma camada de serviço ou padrão CQRS para fazer isso. Mas, novamente, para manter as coisas simples, vamos declarar as injeções aqui.

Se você estiver interessado em aprender sobre a implementação do CQRS no ASP.NET Core com a biblioteca MediatR, consulte este artigo detalhado – CQRS com MediatR no ASP.NET Core 3.1 – Ultimate Guide

public EmployeeController(IApplicationDbContext dbContext, IApplicationReadDbConnection readDbConnection, IApplicationWriteDbConnection writeDbConnection)
{
_dbContext = dbContext;
_readDbConnection = readDbConnection;
_writeDbConnection = writeDbConnection;
}
public IApplicationDbContext _dbContext { get; }
public IApplicationReadDbConnection _readDbConnection { get; }
public IApplicationWriteDbConnection _writeDbConnection { get; }

Vamos começar com os endpoints de acordo com nossos requisitos.

Obter todos os funcionários

O primeiro é um endpoint simples que retornará todos os funcionários disponíveis do banco de dados. Usaremos o Dapper para isso, portanto faremos uso do _readDbConnection, faz sentido?

[HttpGet]
public async Task<IActionResult> GetAllEmployees()
{
var query = $"SELECT * FROM Employees";
var employees = await _readDbConnection.QueryAsync<Employee>(query);
return Ok(employees);
}

Antes de executar, deixe-me adicionar alguns dados fictícios às nossas tabelas.

Depois que os dados forem adicionados, vamos executar nosso aplicativo e abrir POSTMAN . Envie uma solicitação GET para o endpoint api/employee. Isso retornaria a lista de funcionários disponíveis do banco de dados. Lembre-se que este é por Dapper. O tempo de execução da consulta sempre deve ser muito melhor do que outros ORMs. Mas o ponto principal a ser observado é que o elegante retorna os dados em um nível plano. Não é poderoso o suficiente para preencher os objetos aninhados filho. Você pode ver que o Departamento é NULL.

Dito isto, você ainda pode conseguir isso pelo Dapper, mas adicionando muito mais código do que o ideal seria, provavelmente por várias consultas. Esta é a margem onde você tem que escolher entre Dapper e Entity Framework Core. O EFCore, por outro lado, pode fazer isso sem nenhum código extra. Veremos sobre isso no próximo Endpoint.

dapper getAll Usando Entity Framework Core e Dapper no ASP.NET Core - Transações Seguras

Obter funcionário por ID

Vamos adicionar um endpoint que pode retornar um Employee com base no Id. Faremos uso do objeto _dbContext para este endpoint. Também estamos apresentando o Eager Loading por Entity Framework Core que pode preencher o objeto filho aninhado com facilidade.

[HttpGet("{id}")]
public async Task<IActionResult> GetAllEmployeesById(int id)
{
var employees = await _dbContext.Employees.Include(a => a.Department).Where(a => a.Id == id).ToListAsync();
return Ok(employees);
}

efcore getById Usando Entity Framework Core e Dapper no ASP.NET Core - Transações Seguras

Agora você pode ver que estamos recebendo os detalhes do Departamento também.

Crie funcionários e departamentos de uma só vez

Finalmente, vamos trabalhar em um endpoint um pouco mais complexo. Aqui está o algoritmo formulado de acordo com o requisito.

  1. Aceite uma Classe DTO que contenha dados do Novo Funcionário e do Novo Departamento como parâmetros
  2. Certifique-se de estar usando a mesma transação e o Dapper / Entity Framework Core
  3. Verifique se o nome do Departamento já existe. Lance uma exceção se o departamento existir.
  4. Insira os detalhes do departamento na Tabela de Departamentos.
  5. Devolva o ID do Departamento
  6. Insira os detalhes do funcionário na tabela Funcionários junto com o ID do departamento gerado.
  7. Retorne o ID do funcionário e confirme a transação
  8. Se alguma das operações acima falhar ou lançar exceções, reverta a transação e certifique-se de que o banco de dados não seja afetado.

Então isso é sobre o requisito em detalhes. Vamos adicionar as Classes DTO. No projeto WebAPI adicione uma nova pasta, DTOs e adicione as seguintes classes.

public class DepartmentDto
{
[Required]
public string Name { get; set; }
public string Description { get; set; }
}
public class EmployeeDto
{
[Required]
public string Name { get; set; }
[Required]
public string Email { get; set; }
[Required]
public DepartmentDto Department { get; set; }
}
Next, in the Employee Controller add in the following.
[HttpPost]
public async Task<IActionResult> AddNewEmployeeWithDepartment(EmployeeDto employeeDto)
{
_dbContext.Connection.Open();
using (var transaction = _dbContext.Connection.BeginTransaction())
{
try
{
_dbContext.Database.UseTransaction(transaction as DbTransaction);
//Check if Department Exists (By Name)
bool DepartmentExists = await _dbContext.Departments.AnyAsync(a => a.Name == employeeDto.Department.Name);
if(DepartmentExists)
{
throw new Exception("Department Already Exists");
}
//Add Department
var addDepartmentQuery = $"INSERT INTO Departments(Name,Description) VALUES('{employeeDto.Department.Name}','{employeeDto.Department.Description}');SELECT CAST(SCOPE_IDENTITY() as int)";
var departmentId = await _writeDbConnection.QuerySingleAsync<int>(addDepartmentQuery, transaction: transaction);
//Check if Department Id is not Zero.
if(departmentId == 0)
{
throw new Exception("Department Id");
}
//Add Employee
var employee = new Employee
{
DepartmentId = departmentId,
Name = employeeDto.Name,
Email = employeeDto.Email
};
await _dbContext.Employees.AddAsync(employee);
await _dbContext.SaveChangesAsync(default);
//Commmit
transaction.Commit();
//Return EmployeeId
return Ok(employee.Id);
}
catch (Exception)
{
transaction.Rollback();
throw;
}
finally
{
_dbContext.Connection.Close();
}
}
}

Então a ideia é simples. Abra uma conexão, crie um objeto de transação. Use essa transação no núcleo e na elegância do Entity Framework. Se alguma operação falhar, reverta as transações para garantir que não haja dados defeituosos. Se não houver exceções, confirme a transação e finalmente feche a conexão.

Linha 4 – Abre a conexão
Linha 5 – Cria / Inicia a Transação através do objeto _dbContext.
Linha 9 – Atribui este objeto de transação ao Banco de Dados.
Linha 11 – Verifique se o departamento informado existe.

Linha 18 – Executa uma consulta de inserção usando o _writeDbConnection (Dapper) e retorna o Id adicionado. Observe que estamos usando o objeto de transação aqui também.

Linha 25 – Agora que temos um ID de Departamento válido, vamos criar um objeto Employee e preenchê-lo com os dados necessários.
Linha 31 – Adiciona o objeto à instância dbContext.
Linha 32 – Finalmente salve as alterações.
Linha 34 – Confirme a transação. Uma vez que o controle atinge essa linha, significa que não encontramos exceções e é seguro confirmar a transação.
Linha 36- Retorna o ID do funcionário recém-gerado.

Linha 40 – Se houvesse uma exceção, todas as alterações do banco de dados seriam revertidas para manter os dados limpos. Este é o uso real das transações.
Linha 45 – Finalmente feche as conexões.

Com a explicação clara, vamos ver o código em ação. A seguir, será minha solicitação no POSTMAN para o POST Endpoint do Employee Controller.

{
"name": "Mukesh Murugan",
"email": "mukesh@google.es",
"department": {
"name" : "Development",
"description" : "Development department"
}
}

Agora, já sabemos que o Departamento com o Nome Desenvolvimento não existe no banco de dados. Vamos enviar o pedido.

transações POST usando Entity Framework Core e Dapper no ASP.NET Core - Transações Seguras

Você pode ver que a operação foi concluída e retorna o ID do funcionário recém-adicionado. Vamos verificar nosso GetById Endpoint usando esse ID.

transação adicionada usando Entity Framework Core e Dapper no ASP.NET Core - Transações Seguras

Você pode ver que o registro é criado na tabela Employee e Department conforme necessário. Então, é realmente fácil usar o Entity Framework Core e o Dapper no mesmo aplicativo ASP.NET Core dentro da mesma transação.

Agora, o que acontece quando enviamos a solicitação POST novamente com o mesmo corpo da solicitação?

transações Erro usando Entity Framework Core e Dapper no ASP.NET Core - Transações Seguras

PS, você pode ver uma mensagem diferente no POSTMAN, pois eu estava usando um pacote diferente durante o desenvolvimento.

Isso é um encerramento para este artigo. Espero que tenha esclarecido muitas dúvidas e aberto uma abordagem poderosa para usar o Entity Framework Core e o Dapper no ASP.NET Core cuidando das transações.

Resumo

Neste artigo detalhado, aprendemos sobre uma abordagem realmente simples e poderosa para aproveitar os dois ORMs mais poderosos para ASP.NET Core, que são o Entity Framework Core e o Dapper. Além disso, falamos sobre Transações e construímos um aplicativo que pode fazer uso de ambos os ORMs, mantendo as transações em mente. Como resultado, temos um aplicativo inteligente o suficiente para reverter as alterações se ocorrer alguma exceção no processo.

Você pode encontrar todo o código-fonte da implementação aqui.

Deixe para trás suas consultas valiosas, sugestões na seção de comentários abaixo. Além disso, se você acha que aprendeu algo novo com este artigo, não se esqueça de compartilhar isso em sua comunidade de desenvolvedores. Boa Codificação!

Fonte: https://codewithmukesh.com/blog/using-entity-framework-core-and-dapper/

 #entityframework 

Usando Entity Framework Core e Dapper no ASP.NET Core
坂本  健一

坂本 健一

1659367860

ASP.NET Core での Entity Framework Core と Dapper の使用

この記事では、Entity Framework Core と ASP.NET Core の Dapper を同じアプリケーションで一緒に使用する方法について学習します。もう 1 つの重要な論点は、トランザクションです。この記事の終わりまでに、Entity Framework Core と Dapper の両方と連携して動作するだけでなく、プロセスで例外が発生したときにデータをロールバックするのに十分なほどインテリジェントなアプリケーションが完成します。

始めましょう!

Dapper と Entity Framework Core

Dapper には追加機能がないという事実を考慮すると、Dapper は文字通り Entity Framework Core よりもはるかに高速です。これは、最小限の機能も備えた単純な Micro ORM です。これら 2 つの素晴らしいデータ アクセス テクノロジのどちらを選択するかは、常に開発者次第です。これは、Entity Framework Core が遅いという意味ではありません。更新のたびに、パフォーマンスも向上しているようです。Dapper は、EFCore を使用した LINQ ではなく RAW クエリを使用したい人にとっては天国です。

現在、Entity Framework Core には、パフォーマンスの向上だけでなく、多数の機能が含まれています。その質問は、Dapper と Entity Framework Core の両方を使用して最大限に活用できるのに、なぜどちらを選択するのかということです。

Dapper は、複数の結合と非常に長いビジネス ロジックを扱う複雑なクエリを処理するのに非常に優れています。Entity Framework Core は、クラスの生成、オブジェクトの追跡、複数の入れ子になったクラスへのマッピングなどに最適です。したがって、これら 2 つの ORM について話すときは、通常、パフォーマンスと機能です。

要件

架空の会社向けの単純な ASP.NET Core WebAPI を設計します。この会社には、他のすべての従業員を一意の部門にリンクする必要があるというポリシーがあります。より明確にするために、API エンドポイントを介して新しい従業員を追加するたびに、新しい部門レコードも作成する必要があります。非常に架空の要件ですよね?これに加えて、すべての従業員と従業員を ID で返す 2 つのエンドポイントがあります。

詳細を拡大すると、新しく追加された部門がまだ存在しないことを確認する必要があります。ドメインエンティティを見ると、これを理解できます。

Dapper、Entity Framework Core、および両方を組み合わせた使用法を示すために、3 つのエンドポイントにそれぞれ実装します。GetAll エンドポイントには、Dapper を使用します。GetById エンドポイントは、Eager Loading を備えた Entity Framework Core を使用して、部門の詳細も表示します。最後に、POST エンドポイントは、これらの優れたデータ アクセス テクノロジと ASP.NET Core でのトランザクションのクリーンなデモの両方を利用します。

その過程で、おそらく開発時間を節約できる ASP.NET Core のライブラリをいくつか紹介します。

処理する重要な側面 – トランザクション

ここで、要件に従って、Entity Framework Core と Dapper の両方が連携して動作する必要があります。これは実際に達成するのは非常に簡単です。ただし、細心の注意を払う必要がある重要な点は、プロセス全体が堅牢になるように、Entity Framework Core と Dapper の両方が同じ DB トランザクションに参加するようにする必要があるということです。

たとえば、特定の書き込み操作には、複数のエンティティとテーブルが含まれる場合があります。これには、Entity Framework Core で簡単に処理できる操作を含めることができます。たとえば、Dapper で実行することを意図した一連の複雑なクエリを考えてみましょう。このような場合、操作/クエリが失敗したときに SQL 実行操作をロールバックできるようにする必要があります。理にかなっていますか?これは、システム設計にわずかな複雑さをもたらす可能性がある側面です。

これを考慮しない場合、全体的なプロセスは非常に簡単になります。アイデアをステップに入れましょう。
1.Entity Framework コアを構成します。
2. Dapper を構成します。これは、EFCore でも使用されているのと同じ接続文字列を使用することで実現できます。(明らかに、appsettings.json から)
3. サービスをコンテナーに登録し、必要に応じて Context / Dapper の使用を開始します。

しかし、ロールバックやトランザクションを含むすべてを実際に処理する、より複雑で将来性のあるメカニズムを採用する予定です。それを得る?

ASP.NET Core での Entity Framework Core と Dapper の使用

ソリューションとプロジェクトの設定

懸念事項を明確に分離するために、通常どおりオニオン アーキテクチャに従います。まず、Visual Studio 2019 を開き、新しい空のソリューションを作成します。ここでは、Domain、Persistence、および WebApi という 4 つの新しいプロジェクトを追加します。ドメインと永続化プロジェクトは .NET Core クラス ライブラリですが、WebApi は API テンプレートが選択された ASP.NET Core 3.1 Web アプリケーションです。

ドメイン プロジェクトは、コア ドメイン エンティティとインターフェイスのみで構成されます。Persistence Layer には、インターフェイス、サービス、および Entity Framework Core と Dapper に関連するすべての実装が必要です。最後に、WebApi プロジェクトには API エンドポイントがあります。

これは、Onion / Hexagonal Architecture の非常に最小限の実装であることに注意してください。アーキテクチャに関するより詳細なチュートリアルについては、この記事を参照してください – Onion Architecture In ASP.NET Core With CQRS – Detailed

上記の記事で学んだ詳細に基づいて、ASP.NET Core WebAPI のクリーン アーキテクチャの完全な実装をボイラープレート テンプレートとして利用できるので、すぐに始めることができます – ASP.NET Core WebAPI – クリーン アーキテクチャ(オープンソース プロジェクト)

それでは、各プロジェクトに必要なすべてのパッケージのインストールを開始しましょう。

ドメイン プロジェクトのパッケージ

ドメイン プロジェクトが他のものに依存してはならないことは明らかです。ただし、ドメイン層とアプリケーション層の両方を 1 つのエンティティにまとめているため、単純にするために、これら 2 つのパッケージをドメイン プロジェクトにインストールしましょう。

Install-Package Microsoft.EntityFrameworkCore

永続化プロジェクトのパッケージ

前述のように、ソリューションのインフラストラクチャ/永続性に関連するすべてがこのプロジェクトに配置されます。次のパッケージを Persistence Project にインストールしてください。

Install-Package Dapper
Install-Package Microsoft.EntityFrameworkCore.Design
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Install-Package Microsoft.EntityFrameworkCore.Relational
Install-Package System.Data.SqlClient

WebApi プロジェクトのパッケージ

最後に、これらのパッケージを WebAPI プロジェクトに追加します。

Install-Package Microsoft.EntityFrameworkCore.Tools

ドメイン エンティティの追加

ドメイン プロジェクトに移動し、新しいフォルダー エンティティを作成し、従業員と部門の 2 つのクラスを追加します。

namespace Domain.Entities
{
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
}
using AspNetCoreHero.Abstractions;
namespace Domain.Entities
{
public class Employee : BaseEntity
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public int DepartmentId { get; set; }
public Department Department { get; set; }
}
}

インターフェイスの設計

再度、ドメイン プロジェクト内に別のフォルダー Interfaces を追加します。ここでは、依存関係の逆転の概念を使用しているため、システムは実装を気にせず、実際に必要なものに関するコントラクトを提供することのみを担当します。このアプローチを使用すると、テストにも非常に理想的なクリーンでシンプルなシステムを簡単に設計できます。このように、ソリューションのコアは Entity Framework や Dapper のようなものに依存せず、さまざまなテクノロジへの切り替えも容易になります。

最初のインターフェイスである IApplicationDbContext.cs を追加しましょう。これは、Entity Framework Core を使用して Persistence Layer に実装されることを意図しています。

public interface IApplicationDbContext
{
public IDbConnection Connection { get; }
DatabaseFacade Database { get; }
public DbSet<Employee> Employees { get; set; }
public DbSet<Department> Departments { get; set; }
Task<int> SaveChangesAsync(CancellationToken cancellationToken);
}

ここでは、IDbConnection と DbFacade の 2 つの新しいプロパティを使用していることがわかります。これは、後で Dapper によって使用されるデータベースへの直接アクセスです。EFCore と Dapper を相互に連携させることについて話したことを覚えていますか?

次に、特に読み取りと書き込みを目的とした 2 つのインターフェイスを追加しましょう。読み取りインターフェイスには、データベースに対してのみクエリを実行する契約があります。一方、書き込みインターフェイスは本格的なパッケージになります。

IDbTransaction も使用していることに注意してください。これは、この記事の後半でトランザクションの処理を開始するときに役立ちます。

public interface IApplicationReadDbConnection
{
Task<IReadOnlyList<T>> QueryAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default);
Task<T> QueryFirstOrDefaultAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default);
Task<T> QuerySingleAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default);
}
public interface IApplicationWriteDbConnection : IApplicationReadDbConnection
{
Task<int> ExecuteAsync(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default);
}

これで、ドメイン プロジェクトに必要な作業はほぼ完了です。それでは、実装を始めましょう。

EntityFrameworkCore のセットアップ

Persistence Project で、新しいフォルダー コンテキストを追加し、ApplicationDbContext.cs ファイルを追加しましょう。

public class ApplicationDbContext : DbContext, IApplicationDbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{
}
public DbSet<Employee> Employees { get; set; }
public DbSet<Department> Departments { get; set; }
public IDbConnection Connection => Database.GetDbConnection();
}

8 行目で、Entity Framework Core インスタンスから Connection オブジェクトを抽出していることがわかります。

Entity Framework Core の詳細については、次の記事を参照してください – ASP.NET Core 3.1 の Entity Framework Core – はじめに

ダッパーのセットアップ

ここで、Persistence プロジェクトに別のフォルダーを追加して、Connections という名前を付けます。どちらも読み取りおよび書き込みインターフェイスであり、ここで実装されます。以下のクラスを追加します。

public class ApplicationReadDbConnection : IApplicationReadDbConnection, IDisposable
{
private readonly IDbConnection connection;
public ApplicationReadDbConnection(IConfiguration configuration)
{
connection = new SqlConnection(configuration.GetConnectionString("DefaultConnection"));
}
public async Task<IReadOnlyList<T>> QueryAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return (await connection.QueryAsync<T>(sql, param, transaction)).AsList();
}
public async Task<T> QueryFirstOrDefaultAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return await connection.QueryFirstOrDefaultAsync<T>(sql, param, transaction);
}
public async Task<T> QuerySingleAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return await connection.QuerySingleAsync<T>(sql, param, transaction);
}
public void Dispose()
{
connection.Dispose();
}
}

IDisposible インターフェイスも実装していることを確認してください。

コンストラクターで、appSettings.json の接続文字列を使用して新しい接続を初期化していることがわかります。この実装では、DBContext オブジェクト (Entity Framework Core) とのリンクがないことに注意してください。これは、データを読み取るときに Entity Framework Core と Dapper の間で接続を共有する意味がないためです。

接続を共有するユースケースは、データ書き込みが関係している場合に発生します。それがどのように実装されているか見てみましょう。

public class ApplicationWriteDbConnection : IApplicationWriteDbConnection
{
private readonly IApplicationDbContext context;
public ApplicationWriteDbConnection(IApplicationDbContext context)
{
this.context = context;
}
public async Task<int> ExecuteAsync(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return await context.Connection.ExecuteAsync(sql, param, transaction);
}
public async Task<IReadOnlyList<T>> QueryAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return (await context.Connection.QueryAsync<T>(sql, param, transaction)).AsList();
}
public async Task<T> QueryFirstOrDefaultAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return await context.Connection.QueryFirstOrDefaultAsync<T>(sql, param, transaction);
}
public async Task<T> QuerySingleAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return await context.Connection.QuerySingleAsync<T>(sql, param, transaction);
}
}

実装された書き込みでは、読み取り機能と書き込み機能の両方を備えた完全な接続実装が実際にあります。読み取り機能も回避できます。しかし、それはあなたの好みによります。

Entity Framework に属する IApplicationDbContext をコンストラクターに注入していることがわかります。これが、接続とトランザクションを共有する方法です。コンテキストの接続を使用して、Dapper を使用して読み取りおよび書き込み操作を実行します。かなりクールですよね?

Read 実装では、接続文字列を使用してコンストラクターで初期化した IDbConnection オブジェクトを直接操作していました。一方、ここでの Write 実装では、コンテキスト オブジェクトを再利用して、Dapper の助けを借りてクエリとコマンドを実行しています。

Dapper の詳細については、次の記事を参照してください – Dapper in ASP.NET Core with Repository Pattern – 詳細

接続文字列の追加

appsettings.json を開き、接続文字列を追加します。

"ConnectionStrings": {
"DefaultConnection": "Data Source=LAPTOP-7CS9KHVQ;Initial Catalog=demoDb;Integrated Security=True;MultipleActiveResultSets=True"
},

サービスの登録

最後に、これらのインターフェイスとクラスを ASP.NET Core Web アプリケーションのサービス コンテナーに登録しましょう。WebAPI プロジェクトで Startup.cs を開き、ConfigureServices を次のように変更します。

public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options
.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))
);
services.AddScoped<IApplicationDbContext>(provider => provider.GetService<ApplicationDbContext>());
services.AddScoped<IApplicationWriteDbConnection, ApplicationWriteDbConnection>();
services.AddScoped<IApplicationReadDbConnection, ApplicationReadDbConnection>();
services.AddControllers();
}

移行の追加とデータベースの更新

WebAPI プロジェクトを右クリックし、ソリューションのスタートアップ プロジェクトとして設定されていることを確認します。次に、パッケージ マネージャー コンソールを開き、ドロップダウン メニューから既定のプロジェクトをインフラストラクチャに設定します。

次のように入力して、移行を追加し、従業員テーブルと部門テーブルでデータベースを更新します。

add-migration initial
update-database

コントローラーとの配線

すべての面倒な作業を処理したので、前述の 3 つのエンドポイントを使用してコントローラーを作成しましょう。Controllers フォルダーの下に新しい Controller を追加し、Employee Controller という名前を付けます。

以前に作成した 3 つのインターフェイスすべてをコントローラーのコンストラクターに挿入しましょう。これを行うには、サービス レイヤーまたは CQRS パターンを使用するのが理想的です。しかし、繰り返しになりますが、簡単にするために、ここでインジェクションを宣言します。

MediatR ライブラリを使用した ASP.NET Core での CQRS の実装について知りたい場合は、この詳細な記事を参照してください – ASP.NET Core 3.1 での MediatR を使用した CQRS – 究極のガイド

public EmployeeController(IApplicationDbContext dbContext, IApplicationReadDbConnection readDbConnection, IApplicationWriteDbConnection writeDbConnection)
{
_dbContext = dbContext;
_readDbConnection = readDbConnection;
_writeDbConnection = writeDbConnection;
}
public IApplicationDbContext _dbContext { get; }
public IApplicationReadDbConnection _readDbConnection { get; }
public IApplicationWriteDbConnection _writeDbConnection { get; }

要件に従って、エンドポイントから始めましょう。

すべての従業員を取得

1 つ目は、データベースから使用可能なすべての Employee を返す単純なエンドポイントです。これには Dapper を使用するので、_readDbConnection を使用しますが、これは理にかなっていますか?

[HttpGet]
public async Task<IActionResult> GetAllEmployees()
{
var query = $"SELECT * FROM Employees";
var employees = await _readDbConnection.QueryAsync<Employee>(query);
return Ok(employees);
}

実行する前に、テーブルにダミー データを追加します。

データが追加されたら、アプリケーションを実行してPOSTMANを開きましょう。GET リクエストを api/employee エンドポイントに送信します。これにより、データベースから利用可能な従業員のリストが返されます。これは Dapper によるものであることを忘れないでください。クエリの実行時間は、常に他の ORM よりもはるかに優れています。ただし、注意すべき主なポイントは、dapper は理想的にはデータをフラット レベルで返すということです。子のネストされたオブジェクトを埋めるほど強力ではありません。Department が NULL であることがわかります。

そうは言っても、Dapper でこれを達成することはできますが、理想よりも多くのコードを追加する必要があります。おそらく複数のクエリを使用します。これは、Dapper と Entity Framework Core のどちらかを選択する必要があるマージンです。一方、EFCore は、追加のコードをまったく必要とせずにこれを実行できます。これについては、次のエンドポイントで説明します。

dapper getAll Entity Framework Core と ASP.NET Core での Dapper の使用 - 安全なトランザクション

IDで従業員を取得

Id に基づいて Employee を返すことができるエンドポイントを追加しましょう。このエンドポイントには _dbContext オブジェクトを使用します。また、入れ子になった子オブジェクトを簡単に埋めることができる Entity Framework Core による Eager Loading も導入しています。

[HttpGet("{id}")]
public async Task<IActionResult> GetAllEmployeesById(int id)
{
var employees = await _dbContext.Employees.Include(a => a.Department).Where(a => a.Id == id).ToListAsync();
return Ok(employees);
}

ASP.NET Core で Entity Framework Core と Dapper を使用する efcore getById - 安全なトランザクション

部門の詳細も取得していることがわかります。

従業員と部署を一度に作成

最後に、もう少し複雑なエンドポイントに取り組みましょう。要件に従って定式化されたアルゴリズムを次に示します。

  1. New Employee と New Department のデータをパラメータとして含む DTO クラスを受け入れる
  2. 同じトランザクションと Dapper / Entity Framework Core を使用していることを確認してください
  3. 部門名がすでに存在するかどうかを確認します。部門が存在する場合は例外をスローします。
  4. 部門の詳細を部門表に挿入します。
  5. 部門IDを返す
  6. 従業員の詳細を、生成された部門 ID と共に従業員テーブルに挿入します。
  7. 従業員 ID を返し、トランザクションをコミットします
  8. 上記の操作のいずれかが失敗するか例外をスローする場合は、トランザクションをロールバックして、データベースが影響を受けていないことを確認してください。

以上が要件の詳細です。DTO クラスを追加しましょう。WebAPI プロジェクトで、新しいフォルダー DTOs を追加し、次のクラスを追加します。

public class DepartmentDto
{
[Required]
public string Name { get; set; }
public string Description { get; set; }
}
public class EmployeeDto
{
[Required]
public string Name { get; set; }
[Required]
public string Email { get; set; }
[Required]
public DepartmentDto Department { get; set; }
}
Next, in the Employee Controller add in the following.
[HttpPost]
public async Task<IActionResult> AddNewEmployeeWithDepartment(EmployeeDto employeeDto)
{
_dbContext.Connection.Open();
using (var transaction = _dbContext.Connection.BeginTransaction())
{
try
{
_dbContext.Database.UseTransaction(transaction as DbTransaction);
//Check if Department Exists (By Name)
bool DepartmentExists = await _dbContext.Departments.AnyAsync(a => a.Name == employeeDto.Department.Name);
if(DepartmentExists)
{
throw new Exception("Department Already Exists");
}
//Add Department
var addDepartmentQuery = $"INSERT INTO Departments(Name,Description) VALUES('{employeeDto.Department.Name}','{employeeDto.Department.Description}');SELECT CAST(SCOPE_IDENTITY() as int)";
var departmentId = await _writeDbConnection.QuerySingleAsync<int>(addDepartmentQuery, transaction: transaction);
//Check if Department Id is not Zero.
if(departmentId == 0)
{
throw new Exception("Department Id");
}
//Add Employee
var employee = new Employee
{
DepartmentId = departmentId,
Name = employeeDto.Name,
Email = employeeDto.Email
};
await _dbContext.Employees.AddAsync(employee);
await _dbContext.SaveChangesAsync(default);
//Commmit
transaction.Commit();
//Return EmployeeId
return Ok(employee.Id);
}
catch (Exception)
{
transaction.Rollback();
throw;
}
finally
{
_dbContext.Connection.Close();
}
}
}

したがって、アイデアは単純です。接続を開き、トランザクション オブジェクトを作成します。このトランザクションは、エンティティ フレームワーク コアと dapper の間で使用します。いずれかの操作が失敗した場合は、トランザクションをロールバックして、障害のあるデータがないことを確認します。例外がない場合は、トランザクションをコミットし、最後に接続を閉じます。

4 行目 – 接続を開き
ます 5 行目 – _dbContext オブジェクトを介してトランザクションを作成/開始します。
行 9 – このトランザクション オブジェクトをデータベースに割り当てます。
行 11 – 入力された部門が存在するかどうかを確認します。

18 行目 – _writeDbConnection (Dapper) を使用して挿入クエリを実行し、追加された ID を返します。ここでもトランザクション オブジェクトを使用していることに注意してください。

25 行目 – 有効な部門 ID を取得したので、Employee オブジェクトを作成し、必要なデータを入力します。
31 行目 – オブジェクトを dbContext インスタンスに追加します。
32 行目 – 最後に変更を保存します。
34 行目 – トランザクションをコミットします。コントロールがこの行に到達すると、例外は発生せず、トランザクションを安全にコミットできることを意味します。
36 行目 - 新しく生成された従業員 ID を返します。

40 行目 – 例外が発生した場合、クリーンなデータを維持するために、すべてのデータベースの変更がロールバックされます。これがトランザクションの実際の使用です。
45 行目 – 最後に接続を閉じます。

説明が明確になったところで、実際にコードを動かしてみましょう。以下は、Employee Controller の POST エンドポイントへの POSTMAN のリクエストです。

{
"name": "Mukesh Murugan",
"email": "mukesh@google.es",
"department": {
"name" : "Development",
"description" : "Development department"
}
}

ここで、Development という名前の Department がデータベースに存在しないことは既にわかっています。リクエストを送りましょう。

Entity Framework Core と ASP.NET Core の Dapper を使用したトランザクション POST - 安全なトランザクション

操作が完了し、新しく追加された従業員の ID が返されていることがわかります。この ID を使用して GetById エンドポイントを確認してみましょう。

ASP.NET Core での Entity Framework Core と Dapper の使用 - 安全なトランザクション

必要に応じて、Employee テーブルと Department テーブルの両方にレコードが作成されていることがわかります。同じトランザクション内の同じ ASP.NET Core アプリケーションで Entity Framework Core と Dapper を使用するのは非常に簡単です。

同じリクエストボディで POST リクエストを再度送信するとどうなるでしょうか。

ASP.NET Core で Entity Framework Core と Dapper を使用するとトランザクション エラーが発生する - 安全なトランザクション

PS、開発中に別のパッケージを使用していたため、POSTMAN に別のメッセージが表示される場合があります。

これでこの記事は終わりです。多くの疑問が解消され、ASP.NET Core で Entity Framework Core と Dapper を使用してトランザクションを処理するための強力なアプローチが開かれたことを願っています。

概要

この詳細な記事では、ASP.NET Core の 2 つの最も強力な ORM である Entity Framework Core と Dapper を活用するための、非常にシンプルで強力なアプローチについて学びました。さらに、トランザクションについて説明し、トランザクションを念頭に置いて両方の ORM を利用できるアプリケーションを構築しました。その結果、プロセスで例外が発生した場合に変更をロールバックするのに十分なほどインテリジェントなアプリケーションができました。

実装のソース コード全体は、ここにあります。

以下のコメント欄に貴重な質問や提案を残してください. また、この記事から新しいことを学んだと思われる場合は、開発者コミュニティ内で共有することを忘れないでください。ハッピーコーディング!

ソース: https://codewithmukesh.com/blog/using-entity-framework-core-and-dapper/

#entityframework 

ASP.NET Core での Entity Framework Core と Dapper の使用
Anne  de Morel

Anne de Morel

1659367406

Utilisation d'Entity Framework Core et Dapper dans ASP.NET Core

Dans cet article, nous découvrirons comment utiliser Entity Framework Core et Dapper dans ASP.NET Core ensemble dans la même application. Un autre point de discussion majeur sera les transactions. À la fin de l'article, nous aurons une application qui fonctionne à la fois avec Entity Framework Core et Dapper, mais également suffisamment intelligente pour restaurer les données chaque fois qu'il y a une exception avec le processus.

Commençons!

Dapper contre Entity Framework Core

Dapper est littéralement beaucoup plus rapide qu'Entity Framework Core compte tenu du fait qu'il n'y a pas de cloches et de sifflets dans Dapper. Il s'agit d'un micro ORM simple qui a également des fonctionnalités minimales. Il appartient toujours au développeur de choisir entre ces 2 technologies d'accès aux données impressionnantes. Cela ne signifie pas qu'Entity Framework Core est plus lent. À chaque mise à jour, les performances semblent également s'améliorer. Dapper est le paradis pour ceux qui aiment encore travailler avec des requêtes RAW plutôt que LINQ avec EFCore.

Maintenant, Entity Framework Core a des tonnes de fonctionnalités incluses ainsi que des améliorations de performances. Donc cette question est, pourquoi choisir entre Dapper et Entity Framework Core quand vous pouvez utiliser les deux et profiter au maximum, oui ?

Dapper est super génial pour gérer des requêtes complexes qui comportent plusieurs jointures et une vraie logique métier longue. Entity Framework Core est idéal pour la génération de classes, le suivi d'objets, le mappage vers plusieurs classes imbriquées et bien plus encore. Il s'agit donc généralement de performances et de fonctionnalités lorsque l'on parle de ces 2 ORM.

Exigence

Nous allons concevoir une simple API Web ASP.NET Core pour une entreprise imaginaire. Cette entreprise a une politique qui stipule que tous les autres employés doivent être liés à un service unique. Pour être plus clair, chaque fois que vous ajoutez un nouvel employé via le point de terminaison de l'API, vous devez également créer un nouvel enregistrement de service. Une exigence très imaginaire, hein ? Parallèlement à cela, nous aurons 2 autres points de terminaison qui renvoient tous les employés et employés par identifiant.

En développant les détails, nous devrons nous assurer que le département nouvellement ajouté n'existe pas déjà. Vous comprendrez cela une fois que vous aurez vu les entités de domaine.

Pour démontrer l'utilisation de Dapper, Entity Framework Core et les deux combinés, nous les implémenterons chacun dans les 3 Endpoints. Pour les points de terminaison GetAll, nous utiliserons Dapper. Le point de terminaison GetById utiliserait Entity Framework Core avec Eager Loading pour afficher également les détails du service. Et enfin, le point de terminaison POST tirerait parti à la fois de ces technologies d'accès aux données impressionnantes et des transactions de démonstration proprement dans ASP.NET Core.

En cours de route, nous découvrirons quelques bibliothèques pour ASP.NET Core qui pourraient également vous faire gagner du temps de développement.

Aspect important à gérer – Transactions

Maintenant, selon nos besoins, nous avons besoin à la fois d'Entity Framework Core et de Dapper pour travailler côte à côte. C'est assez facile à réaliser en fait. Mais le détail important à prendre en compte est que nous devons nous assurer que Entity Framework Core et Dapper doivent participer à la même transaction DB afin que le processus global puisse être robuste.

Par exemple, une opération d'écriture particulière peut impliquer plusieurs entités et tables. Cela peut à son tour avoir des opérations faciles à gérer par Entity Framework Core, et disons un tas de requêtes complexes qui sont destinées à être exécutées par Dapper. Dans de tels cas, nous devons nous assurer qu'il doit être possible d'annuler l'opération SQL Execute lorsqu'une opération/requête échoue. Logique? C'est cet aspect qui peut introduire une petite complexité dans la conception de notre système.

Si nous n'envisagions pas cela, le processus global serait si simple. Permettez-moi de mettre l'idée en étapes.
1. Configurez Entity Framework Core.
2. Configurez Dapper. Vous pouvez y parvenir en utilisant la même chaîne de connexion qui est également utilisée par EFCore. (Évidemment, à partir de appsettings.json)
3. Enregistrez les services dans le conteneur et commencez à utiliser le contexte / Dapper selon les besoins.

Mais nous opterons pour un mécanisme plus complexe et évolutif qui gérera vraiment tout, y compris les retours en arrière et les transactions. Trouver?

Utilisation d'Entity Framework Core et Dapper dans ASP.NET Core

Mise en place de la solution et des projets

Nous suivrons une architecture d'oignon comme d'habitude pour former une séparation nette des préoccupations. Tout d'abord, ouvrez Visual Studio 2019 et créez une nouvelle solution vierge. Ici, nous ajouterons 4 nouveaux projets, à savoir Domain, Persistence et WebApi. Le domaine et les projets de persistance sont la bibliothèque de classes .NET Core tandis que WebApi est une application Web ASP.NET Core 3.1 avec le modèle d'API sélectionné.

Le projet de domaine comprendra uniquement les entités et les interfaces du domaine principal. La couche de persistance doit avoir la mise en œuvre des interfaces, des services et de tout ce qui concerne Entity Framework Core et Dapper. Enfin, le projet WebApi aurait les API Endpoints.

Veuillez noter qu'il s'agit d'une implémentation très minimale de l'architecture Onion / Hexagonal. Pour un didacticiel beaucoup plus détaillé sur l'architecture, reportez-vous à cet article - Architecture d'oignon dans ASP.NET Core avec CQRS - Détaillé

S'appuyant sur les détails que vous avez appris de l'article mentionné ci-dessus, voici une implémentation complète de Clean Architecture dans ASP.NET Core WebAPI disponible en tant que modèle standard pour vous permettre de démarrer en un rien de temps - ASP.NET Core WebAPI - Clean Architecture ( Projet Open Source)

Maintenant, commençons à installer tous les packages requis pour chaque projet.

Packages pour le projet de domaine

Il est tout à fait clair que Domain Project ne devrait JAMAIS dépendre de quoi que ce soit d'autre. Mais puisque nous combinons à la fois les couches Domaine et Application en une seule entité, et pour des raisons de simplicité, installons ces 2 packages sur le projet de domaine.

Install-Package Microsoft.EntityFrameworkCore

Packages pour le projet de persistance

Comme mentionné précédemment, tout ce qui concerne l'infrastructure / la persistance de la solution sera placé dans ce projet. Allez-y et installez les packages suivants dans le projet Persistence.

Install-Package Dapper
Install-Package Microsoft.EntityFrameworkCore.Design
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Install-Package Microsoft.EntityFrameworkCore.Relational
Install-Package System.Data.SqlClient

Packages pour le projet WebApi

Enfin, ajoutez ces packages au projet WebAPI.

Install-Package Microsoft.EntityFrameworkCore.Tools

Ajout des entités de domaine

Accédez au projet de domaine et créez un nouveau dossier Entities et ajoutez-y ces 2 classes, Employee et Department.

namespace Domain.Entities
{
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
}
using AspNetCoreHero.Abstractions;
namespace Domain.Entities
{
public class Employee : BaseEntity
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public int DepartmentId { get; set; }
public Department Department { get; set; }
}
}

Conception des interfaces

Encore une fois, dans le projet de domaine, ajoutez un autre dossier, Interfaces. Ici, nous utilisons le concept d'inversion de dépendance, de sorte que le système ne se soucie pas de la mise en œuvre et n'est responsable que de fournir le contrat sur ce qu'il veut réellement. Avec cette approche, il est plus facile de concevoir des systèmes propres et simples qui sont également très idéaux pour les tests. De cette façon, le cœur de la solution ne dépend pas de quoi que ce soit comme Entity Framework ou Dapper, mais il nous permet également de passer facilement à différentes technologies.

Ajoutons notre première interface, IApplicationDbContext.cs qui est destinée à être implémentée au niveau de la couche de persistance à l'aide d'Entity Framework Core.

public interface IApplicationDbContext
{
public IDbConnection Connection { get; }
DatabaseFacade Database { get; }
public DbSet<Employee> Employees { get; set; }
public DbSet<Department> Departments { get; set; }
Task<int> SaveChangesAsync(CancellationToken cancellationToken);
}

Ici, vous pouvez voir que nous utilisons 2 nouvelles propriétés, IDbConnection et DbFacade . Il s'agit juste d'un accès direct à la base de données qui sera utilisée par Dapper plus tard. Rappelez-vous, nous avons parlé de faire fonctionner à la fois EFCore et Dapper ensemble?

Ensuite, ajoutons 2 autres interfaces spécifiquement destinées à la lecture et à l'écriture. L'interface de lecture aura les contrats pour exécuter uniquement des requêtes sur la base de données. tandis que l'interface d'écriture sera un package complet.

Notez que nous utilisons également IDbTransaction qui sera utile lorsque nous commencerons à traiter les transactions plus tard dans cet article.

public interface IApplicationReadDbConnection
{
Task<IReadOnlyList<T>> QueryAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default);
Task<T> QueryFirstOrDefaultAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default);
Task<T> QuerySingleAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default);
}
public interface IApplicationWriteDbConnection : IApplicationReadDbConnection
{
Task<int> ExecuteAsync(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default);
}

C'est presque tout ce que nous devons faire avec le projet de domaine. Commençons les implémentations maintenant.

Configuration d'EntityFrameworkCore

Dans le projet de persistance, ajoutons un nouveau contexte de dossier et ajoutons le fichier ApplicationDbContext.cs.

public class ApplicationDbContext : DbContext, IApplicationDbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{
}
public DbSet<Employee> Employees { get; set; }
public DbSet<Department> Departments { get; set; }
public IDbConnection Connection => Database.GetDbConnection();
}

Vous pouvez voir qu'à la ligne 8, nous extrayons l'objet Connection de l'instance Entity Framework Core.

Pour en savoir plus sur Entity Framework Core en détail, veuillez vous référer à l'article suivant - Entity Framework Core dans ASP.NET Core 3.1 - Mise en route

Configurer Dapper

Maintenant, dans le projet Persistence, ajoutez un autre dossier et nommez-le Connections. Les deux interfaces de lecture et d'écriture seront implémentées ici. Ajoutez les classes suivantes.

public class ApplicationReadDbConnection : IApplicationReadDbConnection, IDisposable
{
private readonly IDbConnection connection;
public ApplicationReadDbConnection(IConfiguration configuration)
{
connection = new SqlConnection(configuration.GetConnectionString("DefaultConnection"));
}
public async Task<IReadOnlyList<T>> QueryAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return (await connection.QueryAsync<T>(sql, param, transaction)).AsList();
}
public async Task<T> QueryFirstOrDefaultAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return await connection.QueryFirstOrDefaultAsync<T>(sql, param, transaction);
}
public async Task<T> QuerySingleAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return await connection.QuerySingleAsync<T>(sql, param, transaction);
}
public void Dispose()
{
connection.Dispose();
}
}

Assurez-vous que vous implémentez également l'interface IDisposible.

Au niveau du constructeur, vous pouvez voir que nous initialisons une nouvelle connexion en utilisant la chaîne de connexion de notre appSettings.json. Notez que dans cette implémentation, il n'y a aucun lien avec un objet DBContext (Entity Framework Core) car cela n'a vraiment aucun sens de partager les connexions entre Entity Framework Core et Dapper lorsque vous lisez les données.

Le cas d'utilisation du partage de la connexion entre en scène lorsqu'il y a écriture de données impliquée. Voyons comment il est mis en œuvre.

public class ApplicationWriteDbConnection : IApplicationWriteDbConnection
{
private readonly IApplicationDbContext context;
public ApplicationWriteDbConnection(IApplicationDbContext context)
{
this.context = context;
}
public async Task<int> ExecuteAsync(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return await context.Connection.ExecuteAsync(sql, param, transaction);
}
public async Task<IReadOnlyList<T>> QueryAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return (await context.Connection.QueryAsync<T>(sql, param, transaction)).AsList();
}
public async Task<T> QueryFirstOrDefaultAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return await context.Connection.QueryFirstOrDefaultAsync<T>(sql, param, transaction);
}
public async Task<T> QuerySingleAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return await context.Connection.QuerySingleAsync<T>(sql, param, transaction);
}
}

Dans l'écriture implémentée, nous avons vraiment une implémentation de connexion complète avec des fonctionnalités de lecture et d'écriture. Vous pouvez également éviter la fonctionnalité de lecture. Mais cela dépend de votre préférence.

Vous pouvez voir que nous injectons le IApplicationDbContext qui appartient à Entity Framework dans le constructeur. C'est ainsi que nous pouvons partager la connexion et la transaction. En utilisant la connexion du contexte, nous effectuons les opérations de lecture et d'écriture à l'aide de Dapper. Assez cool, ouais?

Dans l'implémentation Read, nous travaillions directement avec l'objet IDbConnection que nous avons initialisé dans le constructeur à l'aide de la chaîne de connexion. Alors qu'ici, dans l'implémentation Write, nous réutilisons l'objet de contexte pour exécuter des requêtes et des commandes à l'aide de Dapper.

Pour en savoir plus sur Dapper en détail, veuillez vous référer à l'article suivant - Dapper dans ASP.NET Core avec modèle de référentiel - Détaillé

Ajout de la chaîne de connexion

Ouvrez votre appsettings.json et ajoutez votre chaîne de connexion.

"ConnectionStrings": {
"DefaultConnection": "Data Source=LAPTOP-7CS9KHVQ;Initial Catalog=demoDb;Integrated Security=True;MultipleActiveResultSets=True"
},

Enregistrement des services

Enfin, enregistrons ces interfaces et classes dans le conteneur de service de notre application Web ASP.NET Core. Ouvrez votre Startup.cs dans le projet WebAPI et modifiez vos ConfigureServices comme suit.

public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options
.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))
);
services.AddScoped<IApplicationDbContext>(provider => provider.GetService<ApplicationDbContext>());
services.AddScoped<IApplicationWriteDbConnection, ApplicationWriteDbConnection>();
services.AddScoped<IApplicationReadDbConnection, ApplicationReadDbConnection>();
services.AddControllers();
}

Ajout de migrations et mise à jour de la base de données

Cliquez avec le bouton droit sur le projet WebAPI et assurez-vous qu'il est défini comme projet de démarrage de la solution. Ouvrez ensuite la console du gestionnaire de packages et définissez le projet par défaut sur Infrastructure dans le menu déroulant.

Saisissez ce qui suit pour ajouter les migrations et mettre à jour la base de données avec les tables Employee et Department.

add-migration initial
update-database

Câblage avec le contrôleur

Comme nous nous sommes occupés de tout le gros du travail, construisons notre contrôleur avec les 3 points de terminaison mentionnés. Ajoutez un nouveau contrôleur sous le dossier Controllers et nommez-le Employee Controller.

Injectons les 3 interfaces que nous avions créées précédemment dans le constructeur du contrôleur. Idéalement, vous utiliseriez une couche de service ou un modèle CQRS pour ce faire. Mais encore une fois, pour garder les choses simples, nous déclarerons les injections ici.

Si vous souhaitez en savoir plus sur la mise en œuvre de CQRS dans ASP.NET Core avec la bibliothèque MediatR, reportez-vous à cet article détaillé - CQRS avec MediatR dans ASP.NET Core 3.1 - Ultimate Guide

public EmployeeController(IApplicationDbContext dbContext, IApplicationReadDbConnection readDbConnection, IApplicationWriteDbConnection writeDbConnection)
{
_dbContext = dbContext;
_readDbConnection = readDbConnection;
_writeDbConnection = writeDbConnection;
}
public IApplicationDbContext _dbContext { get; }
public IApplicationReadDbConnection _readDbConnection { get; }
public IApplicationWriteDbConnection _writeDbConnection { get; }

Commençons par les points de terminaison selon nos exigences.

Obtenir tous les employés

Le premier est un point de terminaison simple qui va renvoyer tous les employés disponibles à partir de la base de données. Nous utiliserons Dapper pour cela, donc nous utiliserons le _readDbConnection, c'est logique ?

[HttpGet]
public async Task<IActionResult> GetAllEmployees()
{
var query = $"SELECT * FROM Employees";
var employees = await _readDbConnection.QueryAsync<Employee>(query);
return Ok(employees);
}

Avant d'exécuter, permettez-moi d'ajouter des données factices à nos tables.

Une fois les données ajoutées, lançons notre application et ouvrons POSTMAN . Envoyez une requête GET au point de terminaison api/employee. Cela renverrait la liste des employés disponibles de la base de données. Rappelez-vous que c'est par Dapper. Le temps d'exécution de la requête est toujours censé être bien meilleur que celui des autres ORM. Mais le point principal à noter est que dapper renvoie idéalement les données à un niveau plat. Il n'est pas assez puissant pour remplir les objets imbriqués enfants. Vous pouvez voir que le département est NULL.

Cela étant dit, vous pouvez toujours y parvenir avec Dapper, mais en ajoutant beaucoup plus de code que vous ne le feriez idéalement, probablement par plusieurs requêtes. C'est la marge où vous devez choisir entre Dapper et Entity Framework Core. EFCore, d'autre part, peut le faire sans aucun code supplémentaire. Nous verrons cela dans le prochain Endpoint.

dapper getAll Utilisation de Entity Framework Core et Dapper dans ASP.NET Core - Transactions sécurisées

Obtenir un employé par identifiant

Ajoutons un point de terminaison qui peut renvoyer un employé en fonction de l'ID. Nous utiliserons l'objet _dbContext pour ce point de terminaison. Nous introduisons également Eager Loading par Entity Framework Core qui peut remplir facilement l'objet enfant imbriqué.

[HttpGet("{id}")]
public async Task<IActionResult> GetAllEmployeesById(int id)
{
var employees = await _dbContext.Employees.Include(a => a.Department).Where(a => a.Id == id).ToListAsync();
return Ok(employees);
}

efcore getById Utilisation de Entity Framework Core et Dapper dans ASP.NET Core - Transactions sécurisées

Maintenant, vous pouvez voir que nous obtenons également les détails du département.

Créer un employé et un service en une seule fois

Enfin, travaillons sur un endpoint un peu plus complexe. Voici l'algorithme formulé selon l'exigence.

  1. Accepter une classe DTO contenant les données du nouvel employé et du nouveau service comme paramètres
  2. Assurez-vous que vous utilisez la même transaction et Dapper / Entity Framework Core
  3. Vérifiez si le nom du service existe déjà. Lever une exception si le département existe.
  4. Insérez les détails du département dans le tableau des départements.
  5. Renvoyez l'ID du service
  6. Insérez les détails de l'employé dans la table Employés avec l'ID de service généré.
  7. Renvoyer l'identifiant de l'employé et valider la transaction
  8. Si l'une des opérations ci-dessus échoue ou génère des exceptions, annulez la transaction et assurez-vous que la base de données n'est pas affectée.

Il s'agit donc de l'exigence en détail. Ajoutons les classes DTO. Dans le projet WebAPI, ajoutez un nouveau dossier, DTO et ajoutez les classes suivantes.

public class DepartmentDto
{
[Required]
public string Name { get; set; }
public string Description { get; set; }
}
public class EmployeeDto
{
[Required]
public string Name { get; set; }
[Required]
public string Email { get; set; }
[Required]
public DepartmentDto Department { get; set; }
}
Next, in the Employee Controller add in the following.
[HttpPost]
public async Task<IActionResult> AddNewEmployeeWithDepartment(EmployeeDto employeeDto)
{
_dbContext.Connection.Open();
using (var transaction = _dbContext.Connection.BeginTransaction())
{
try
{
_dbContext.Database.UseTransaction(transaction as DbTransaction);
//Check if Department Exists (By Name)
bool DepartmentExists = await _dbContext.Departments.AnyAsync(a => a.Name == employeeDto.Department.Name);
if(DepartmentExists)
{
throw new Exception("Department Already Exists");
}
//Add Department
var addDepartmentQuery = $"INSERT INTO Departments(Name,Description) VALUES('{employeeDto.Department.Name}','{employeeDto.Department.Description}');SELECT CAST(SCOPE_IDENTITY() as int)";
var departmentId = await _writeDbConnection.QuerySingleAsync<int>(addDepartmentQuery, transaction: transaction);
//Check if Department Id is not Zero.
if(departmentId == 0)
{
throw new Exception("Department Id");
}
//Add Employee
var employee = new Employee
{
DepartmentId = departmentId,
Name = employeeDto.Name,
Email = employeeDto.Email
};
await _dbContext.Employees.AddAsync(employee);
await _dbContext.SaveChangesAsync(default);
//Commmit
transaction.Commit();
//Return EmployeeId
return Ok(employee.Id);
}
catch (Exception)
{
transaction.Rollback();
throw;
}
finally
{
_dbContext.Connection.Close();
}
}
}

L'idée est donc simple. Ouvrez une connexion, créez un objet de transaction. Utilisez cette transaction dans le noyau et le dapper du framework Entity. Si une opération échoue, annulez les transactions pour vous assurer qu'il n'y a pas de données erronées. S'il n'y a pas d'exceptions, validez la transaction et fermez enfin la connexion.

Ligne 4 - Ouvre la connexion
Ligne 5 - Créer / Démarre la transaction via l'objet _dbContext.
Ligne 9 – Attribuez cet objet de transaction à la base de données.
Ligne 11 – Vérifiez si le département saisi existe.

Ligne 18 - Exécutez une requête d'insertion à l'aide de _writeDbConnection (Dapper) et renvoie l'ID ajouté. Notez que nous utilisons également l'objet de transaction ici.

Ligne 25 – Maintenant que nous avons un ID de service valide, créons un objet Employé et remplissons-le avec les données requises.
Ligne 31 – Ajoute l'objet à l'instance dbContext.
Ligne 32 – Enfin, enregistrez les modifications.
Ligne 34 – Validez la transaction. Une fois que le contrôle atteint cette ligne, cela signifie qu'aucune exception n'a été rencontrée et qu'il est sûr de valider la transaction.
Ligne 36 - Renvoie le nouvel identifiant d'employé généré.

Ligne 40 - S'il y avait une exception, toutes les modifications de la base de données seraient annulées afin de conserver des données propres. C'est l'utilisation réelle des transactions.
Ligne 45 – Fermez enfin les connexions.

Avec l'explication claire, voyons réellement le code en action. Ce qui suit sera ma demande sur POSTMAN au point de terminaison POST du contrôleur des employés.

{
"name": "Mukesh Murugan",
"email": "mukesh@google.es",
"department": {
"name" : "Development",
"description" : "Development department"
}
}

Maintenant, nous savons déjà que le département avec le nom Développement n'existe pas dans la base de données. Envoyons la demande.

transactions POST à ​​l'aide de Entity Framework Core et Dapper dans ASP.NET Core - Transactions sécurisées

Vous pouvez voir que l'opération est terminée et renvoie l'ID de l'employé nouvellement ajouté. Vérifions notre point de terminaison GetById à l'aide de cet ID.

transaction ajoutée Utilisation de Entity Framework Core et Dapper dans ASP.NET Core - Transactions sécurisées

Vous pouvez voir que l'enregistrement est créé dans les tables Employee et Department selon nos besoins. C'est donc vraiment facile d'utiliser Entity Framework Core et Dapper dans la même application ASP.NET Core au sein de la même transaction.

Maintenant, que se passe-t-il lorsque nous envoyons à nouveau la requête POST avec le même corps de requête ?

transactions Erreur lors de l'utilisation d'Entity Framework Core et Dapper dans ASP.NET Core - Transactions sécurisées

PS, vous pouvez voir un message différent dans POSTMAN, car j'utilisais un package différent lors du développement.

C'est un enveloppement pour cet article. J'espère que cela a dissipé beaucoup de doutes et ouvert une approche puissante pour utiliser Entity Framework Core et Dapper dans ASP.NET Core en prenant soin des transactions.

Sommaire

Dans cet article détaillé, nous avons découvert une approche vraiment simple et puissante pour tirer parti des deux ORM les plus puissants pour ASP.NET Core, à savoir Entity Framework Core et Dapper. De plus, nous avons parlé des transactions et créé une application qui peut utiliser les deux ORM en gardant à l'esprit les transactions. En conséquence, nous avons une application suffisamment intelligente pour annuler les modifications si des exceptions se produisent dans le processus.

Vous pouvez trouver le code source complet de l'implémentation ici.

Laissez derrière vous vos précieuses questions et suggestions dans la section des commentaires ci-dessous. De plus, si vous pensez avoir appris quelque chose de nouveau grâce à cet article, n'oubliez pas de le partager au sein de votre communauté de développeurs. Bon codage !

Source : https://codewithmukesh.com/blog/using-entity-framework-core-and-dapper/

  #entityframework 

Utilisation d'Entity Framework Core et Dapper dans ASP.NET Core

Uso de Entity Framework Core y Dapper en ASP.NET Core

En este artículo, aprenderemos sobre el uso de Entity Framework Core y Dapper en ASP.NET Core juntos en la misma aplicación. Otro punto importante de discusión será Transacciones. Al final del artículo, tendremos una aplicación que funciona con Entity Framework Core y Dapper uno al lado del otro, pero también lo suficientemente inteligente como para revertir los datos siempre que haya una excepción con el proceso.

¡Empecemos!

Núcleo de Dapper vs Entity Framework

Dapper es literalmente mucho más rápido que Entity Framework Core considerando el hecho de que no hay campanas ni silbatos en Dapper. Es un Micro ORM sencillo que también tiene características mínimas. Siempre depende del desarrollador elegir entre estas 2 impresionantes tecnologías de acceso a datos. Esto no significa que Entity Framework Core sea más lento. Con cada actualización, el rendimiento también parece mejorar. Dapper es el paraíso para aquellos a quienes todavía les gusta trabajar con consultas RAW en lugar de LINQ con EFCore.

Ahora, Entity Framework Core tiene toneladas de funciones incluidas junto con mejoras de rendimiento también. Entonces esa pregunta es: ¿Por qué elegir entre Dapper y Entity Framework Core cuando puede usar ambos y aprovechar al máximo, sí?

Dapper es súper increíble para manejar consultas complejas que tienen uniones múltiples y una lógica comercial realmente larga. Entity Framework Core es excelente para la generación de clases, el seguimiento de objetos, el mapeo de varias clases anidadas y mucho más. Por lo general, se trata de rendimiento y características cuando se habla de estos 2 ORM.

Requisito

Diseñaremos una WebAPI ASP.NET Core simple para una empresa imaginaria. Esta empresa tiene una política que dice que todos los demás empleados deben estar vinculados a un departamento único. Para que quede más claro, cada vez que agrega un nuevo empleado a través del extremo de la API, también debe crear un nuevo Registro de departamento. Un requisito muy imaginario, ¿sí? Junto con esto, tendremos otros 2 puntos finales que devuelven todos los Empleados y Empleado por Id.

Ampliando los detalles, tendremos que asegurarnos de que el Departamento recién agregado no exista ya. Obtendrá una idea de esto una vez que llegue a ver las Entidades de Dominio.

Para demostrar el uso de Dapper, Entity Framework Core y ambos combinados, los implementaremos en los 3 puntos finales. Para GetAll Endpoints, usaremos Dapper. GetById Endpoint usaría Entity Framework Core con Eager Loading para mostrar los detalles del departamento también. Y, por último, POST Endpoint aprovecharía estas increíbles tecnologías de acceso a datos y demostraría limpiamente las transacciones en ASP.NET Core.

En el camino, conoceremos algunas bibliotecas para ASP.NET Core que probablemente también podrían ahorrarle algo de tiempo de desarrollo.

Aspecto Importante a Manejar – Transacciones

Ahora, de acuerdo con nuestros requisitos, necesitamos que tanto Entity Framework Core como Dapper trabajen juntos. Esto es bastante fácil de lograr en realidad. Pero el detalle importante a tener en cuenta es que debemos asegurarnos de que tanto Entity Framework Core como Dapper participen en la misma transacción de base de datos para que el proceso general pueda ser sólido.

Por ejemplo, una operación de escritura en particular puede involucrar varias entidades y tablas. Esto, a su vez, puede tener operaciones que Entity Framework Core puede manejar fácilmente y, digamos, un montón de consultas complejas que Dapper debe ejecutar. En tales casos, debemos asegurarnos de que sea posible revertir la operación de ejecución de SQL cuando falla cualquier operación/consulta. ¿Tiene sentido? Este es el aspecto que puede introducir una pequeña complejidad en el diseño de nuestro sistema.

Si no estamos considerando esto, el proceso general sería muy sencillo. Déjame poner la idea en pasos.
1. Configure Entity Framework Core.
2. Configure Dapper. Puede lograr esto utilizando la misma cadena de conexión que también utiliza EFCore. (Obviamente, desde appsettings.json)
3. Registre los servicios en el Contenedor y comience a usar Context / Dapper según sea necesario.

Pero buscaremos un mecanismo más complejo y preparado para el futuro que manejará realmente todo, incluidas las reversiones y las transacciones. ¿Consíguelo?

Uso de Entity Framework Core y Dapper en ASP.NET Core

Configuración de la solución y los proyectos

Seguiremos una Arquitectura Cebolla como de costumbre para formar una clara separación de preocupaciones. En primer lugar, abra Visual Studio 2019 y cree una nueva solución en blanco. Aquí agregaremos 4 nuevos proyectos, a saber, dominio, persistencia y WebApi. El dominio y los proyectos de persistencia son una biblioteca de clases de .NET Core, mientras que WebApi es una aplicación web ASP.NET Core 3.1 con la plantilla de API seleccionada.

El Proyecto de Dominio consistirá únicamente en las Entidades e Interfaces de Dominio Principales. La Capa de Persistencia debe tener la Implementación de las Interfaces, Servicios y todo lo relacionado con Entity Framework Core y Dapper. Finalmente, el proyecto WebApi tendría los puntos finales de la API.

Tenga en cuenta que esta es una implementación mínima de Onion / Hexagonal Architecture. Para obtener un tutorial mucho más detallado sobre la arquitectura, consulte este artículo: Arquitectura de cebolla en ASP.NET Core con CQRS: detallada

Sobre la base de los detalles que aprendió del artículo mencionado anteriormente, aquí hay una implementación completa de Clean Architecture en ASP.NET Core WebAPI disponible como una plantilla repetitiva para que pueda comenzar en poco tiempo: ASP.NET Core WebAPI – Clean Architecture ( Proyecto de código abierto)

Ahora, comencemos a instalar todos los paquetes necesarios para cada proyecto.

Paquetes para Proyecto de Dominio

Está bastante claro que Domain Project NUNCA debe depender de nada más. Pero dado que estamos combinando la capa de Dominio y Aplicación en una sola entidad, y para simplificar las cosas, instalemos estos 2 paquetes en el Proyecto de Dominio.

Install-Package Microsoft.EntityFrameworkCore

Paquetes para el proyecto de persistencia

Como se mencionó anteriormente, todo lo relacionado con la Infraestructura / Persistencia de la Solución se colocará en este Proyecto. Continúe e instale los siguientes paquetes en el Proyecto Persistencia.

Install-Package Dapper
Install-Package Microsoft.EntityFrameworkCore.Design
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Install-Package Microsoft.EntityFrameworkCore.Relational
Install-Package System.Data.SqlClient

Paquetes para el Proyecto WebApi

Finalmente, agregue estos paquetes al Proyecto WebAPI.

Install-Package Microsoft.EntityFrameworkCore.Tools

Agregar las entidades de dominio

Navegue al Proyecto de Dominio y cree una nueva Carpeta de Entidades y agregue estas 2 clases, Empleado y Departamento.

namespace Domain.Entities
{
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
}
using AspNetCoreHero.Abstractions;
namespace Domain.Entities
{
public class Employee : BaseEntity
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public int DepartmentId { get; set; }
public Department Department { get; set; }
}
}

Diseño de las interfaces

Nuevamente, dentro del Proyecto de Dominio agregue otra carpeta, Interfaces. Aquí estamos utilizando el concepto de Inversión de Dependencia, de modo que el sistema no se preocupa por la implementación y solo se encarga de proporcionar el Contrato en lo que realmente quiere. Con este enfoque, es más fácil diseñar sistemas limpios y simples que también son ideales para las pruebas. De esta manera, el núcleo de la solución no depende de nada como Entity Framework o Dapper, sino que también nos facilita cambiar a diferentes tecnologías.

Agreguemos nuestra primera interfaz, IApplicationDbContext.cs, que está destinada a implementarse en la capa de persistencia mediante Entity Framework Core.

public interface IApplicationDbContext
{
public IDbConnection Connection { get; }
DatabaseFacade Database { get; }
public DbSet<Employee> Employees { get; set; }
public DbSet<Department> Departments { get; set; }
Task<int> SaveChangesAsync(CancellationToken cancellationToken);
}

Aquí puede ver que estamos usando 2 nuevas propiedades, IDbConnection y DbFacade. Este es solo un acceso directo a la base de datos que Dapper utilizará más adelante. ¿Recuerdas que hablamos sobre hacer que EFCore y Dapper funcionen juntos?

A continuación, agreguemos 2 interfaces más que están diseñadas específicamente para lectura y escritura. La interfaz de lectura tendrá los contratos para ejecutar consultas únicamente en la base de datos. mientras que la interfaz de escritura será un paquete completo.

Tenga en cuenta que también estamos usando IDbTransaction, que será útil cuando comencemos a tratar con transacciones más adelante en este artículo.

public interface IApplicationReadDbConnection
{
Task<IReadOnlyList<T>> QueryAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default);
Task<T> QueryFirstOrDefaultAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default);
Task<T> QuerySingleAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default);
}
public interface IApplicationWriteDbConnection : IApplicationReadDbConnection
{
Task<int> ExecuteAsync(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default);
}

Eso es casi todo lo que necesitamos hacer con el Proyecto de Dominio. Comencemos las implementaciones ahora.

Configuración de EntityFrameworkCore

En el proyecto de persistencia, agreguemos un nuevo contexto de carpeta y agreguemos el archivo ApplicationDbContext.cs.

public class ApplicationDbContext : DbContext, IApplicationDbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{
}
public DbSet<Employee> Employees { get; set; }
public DbSet<Department> Departments { get; set; }
public IDbConnection Connection => Database.GetDbConnection();
}

Puede ver que, en la Línea 8, estamos extrayendo el objeto Connection de la instancia de Entity Framework Core.

Para obtener más información sobre Entity Framework Core en detalle, consulte el siguiente artículo: Entity Framework Core en ASP.NET Core 3.1: Introducción

Configuración de Dapper

Ahora, en el proyecto Persistencia, agregue otra carpeta y asígnele el nombre Conexiones. Ambas son interfaces de lectura y escritura que se implementarán aquí. Agregue las siguientes clases.

public class ApplicationReadDbConnection : IApplicationReadDbConnection, IDisposable
{
private readonly IDbConnection connection;
public ApplicationReadDbConnection(IConfiguration configuration)
{
connection = new SqlConnection(configuration.GetConnectionString("DefaultConnection"));
}
public async Task<IReadOnlyList<T>> QueryAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return (await connection.QueryAsync<T>(sql, param, transaction)).AsList();
}
public async Task<T> QueryFirstOrDefaultAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return await connection.QueryFirstOrDefaultAsync<T>(sql, param, transaction);
}
public async Task<T> QuerySingleAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return await connection.QuerySingleAsync<T>(sql, param, transaction);
}
public void Dispose()
{
connection.Dispose();
}
}

Asegúrese de implementar también la interfaz IDisposible.

En el constructor, puede ver que estamos inicializando una nueva conexión utilizando la cadena de conexión de nuestro appSettings.json. Tenga en cuenta que en esta implementación, no hay ningún vínculo con ningún objeto DBContext (Entity Framework Core) porque realmente no tiene sentido compartir las conexiones entre Entity Framework Core y Dapper cuando lee los datos.

El caso de uso de compartir la conexión entra en escena cuando hay escritura de datos involucrada. Veamos cómo se implementa.

public class ApplicationWriteDbConnection : IApplicationWriteDbConnection
{
private readonly IApplicationDbContext context;
public ApplicationWriteDbConnection(IApplicationDbContext context)
{
this.context = context;
}
public async Task<int> ExecuteAsync(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return await context.Connection.ExecuteAsync(sql, param, transaction);
}
public async Task<IReadOnlyList<T>> QueryAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return (await context.Connection.QueryAsync<T>(sql, param, transaction)).AsList();
}
public async Task<T> QueryFirstOrDefaultAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return await context.Connection.QueryFirstOrDefaultAsync<T>(sql, param, transaction);
}
public async Task<T> QuerySingleAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return await context.Connection.QuerySingleAsync<T>(sql, param, transaction);
}
}

En la escritura implementada, realmente tenemos una implementación completa de Connection con funcionalidades de lectura y escritura. También podría evitar la funcionalidad de lectura. Pero depende de tu preferencia.

Puede ver que estamos inyectando el IApplicationDbContext que pertenece a Entity Framework en el Constructor. Así es como podemos compartir la conexión y la transacción. Usando la conexión del contexto, realizamos las operaciones de lectura y escritura usando Dapper. Muy bien, ¿sí?

En la implementación de lectura, estábamos trabajando directamente con el objeto IDbConnection que inicializamos en el constructor usando la cadena de conexión. Mientras que, aquí en la implementación de escritura, estamos reutilizando el objeto de contexto para ejecutar consultas y comandos con la ayuda de Dapper.

Para obtener información detallada sobre Dapper, consulte el siguiente artículo: Dapper en ASP.NET Core con patrón de repositorio: detallado

Agregar la cadena de conexión

Abra su appsettings.json y agregue su cadena de conexión.

"ConnectionStrings": {
"DefaultConnection": "Data Source=LAPTOP-7CS9KHVQ;Initial Catalog=demoDb;Integrated Security=True;MultipleActiveResultSets=True"
},

Registro de los Servicios

Finalmente, registremos estas interfaces y clases en el contenedor de servicios de nuestra aplicación web ASP.NET Core. Abra su Startup.cs en el Proyecto WebAPI y modifique sus ConfigureServices de la siguiente manera.

public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options
.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))
);
services.AddScoped<IApplicationDbContext>(provider => provider.GetService<ApplicationDbContext>());
services.AddScoped<IApplicationWriteDbConnection, ApplicationWriteDbConnection>();
services.AddScoped<IApplicationReadDbConnection, ApplicationReadDbConnection>();
services.AddControllers();
}

Agregar migraciones y actualizar la base de datos

Haga clic con el botón derecho en el Proyecto WebAPI y asegúrese de que esté configurado como el Proyecto de inicio de la solución. A continuación, abra la Consola del administrador de paquetes y configure el proyecto predeterminado en Infraestructura en el menú desplegable.

Ingrese lo siguiente para agregar las migraciones y actualizar la base de datos con las tablas de empleados y departamentos.

add-migration initial
update-database

Cableado con el controlador

Como nos hemos ocupado de todo el trabajo pesado, construyamos nuestro controlador con los 3 puntos finales mencionados. Agregue un nuevo controlador en la carpeta Controladores y asígnele el nombre Controlador de empleados.

Inyectemos las 3 interfaces que habíamos creado anteriormente en el Constructor del controlador. Idealmente, usaría una capa de servicio o un patrón CQRS para hacer esto. Pero nuevamente, para simplificar las cosas, declararemos las inyecciones aquí.

Si está interesado en obtener información sobre la implementación de CQRS en ASP.NET Core con la biblioteca MediatR, consulte este artículo detallado: CQRS con MediatR en ASP.NET Core 3.1: guía definitiva

public EmployeeController(IApplicationDbContext dbContext, IApplicationReadDbConnection readDbConnection, IApplicationWriteDbConnection writeDbConnection)
{
_dbContext = dbContext;
_readDbConnection = readDbConnection;
_writeDbConnection = writeDbConnection;
}
public IApplicationDbContext _dbContext { get; }
public IApplicationReadDbConnection _readDbConnection { get; }
public IApplicationWriteDbConnection _writeDbConnection { get; }

Comencemos con los puntos finales según nuestros requisitos.

Obtener todos los empleados

El primero es un punto final simple que devolverá todos los empleados disponibles de la base de datos. Usaremos Dapper para esto, por lo tanto, haremos uso de _readDbConnection, ¿tiene sentido?

[HttpGet]
public async Task<IActionResult> GetAllEmployees()
{
var query = $"SELECT * FROM Employees";
var employees = await _readDbConnection.QueryAsync<Employee>(query);
return Ok(employees);
}

Antes de ejecutar, permítanme agregar algunos datos ficticios a nuestras tablas.

Una vez que se agreguen los datos, ejecutemos nuestra aplicación y abra POSTMAN . Envíe una solicitud GET al extremo de API/empleado. Esto devolvería la lista de empleados disponibles de la base de datos. Recuerda que esto es de Dapper. El tiempo de ejecución de la consulta siempre pretende ser mucho mejor que otros ORM. Pero el punto principal a tener en cuenta es que dapper idealmente devuelve los datos a un nivel plano. No es lo suficientemente potente como para llenar los objetos anidados secundarios. Puede ver que el Departamento es NULL.

Dicho esto, aún puede lograr esto con Dapper pero agregando mucho más código del que idealmente haría, probablemente mediante múltiples consultas. Este es el margen donde debe elegir entre Dapper y Entity Framework Core. EFCore, por otro lado, puede lograr esto sin ningún código adicional. Lo veremos en el próximo Endpoint.

dapper getAll Usando Entity Framework Core y Dapper en ASP.NET Core - Transacciones seguras

Obtener empleado por ID

Agreguemos un punto final que pueda devolver un empleado en función de la identificación. Haremos uso del objeto _dbContext para este punto final. También presentamos Eager Loading by Entity Framework Core que puede completar el objeto secundario anidado con facilidad.

[HttpGet("{id}")]
public async Task<IActionResult> GetAllEmployeesById(int id)
{
var employees = await _dbContext.Employees.Include(a => a.Department).Where(a => a.Id == id).ToListAsync();
return Ok(employees);
}

efcore getById usando Entity Framework Core y Dapper en ASP.NET Core - Transacciones seguras

Ahora puede ver que también estamos obteniendo los detalles del Departamento.

Crear empleado y departamento de una sola vez

Finalmente, trabajemos en un punto final un poco más complejo. Aquí está el algoritmo formulado según el requisito.

  1. Aceptar una Clase DTO que contenga datos del Nuevo Empleado y el Nuevo Departamento como parámetros
  2. Asegúrese de estar utilizando la misma transacción y Dapper/Entity Framework Core
  3. Compruebe si el nombre del Departamento ya existe. Lanzar una excepción si el departamento existe.
  4. Inserte los detalles del departamento en la tabla de departamentos.
  5. Devolver el ID del Departamento
  6. Inserte los detalles del empleado en la tabla Empleados junto con la identificación del departamento generado.
  7. Devuelva la identificación del empleado y confirme la transacción
  8. Si alguna de las operaciones anteriores falla o arroja excepciones, revierta la transacción y asegúrese de que la base de datos no se vea afectada.

Así que ese es el requisito en detalle. Agreguemos las clases DTO. En el proyecto WebAPI, agregue una nueva carpeta, DTO y agregue las siguientes clases.

public class DepartmentDto
{
[Required]
public string Name { get; set; }
public string Description { get; set; }
}
public class EmployeeDto
{
[Required]
public string Name { get; set; }
[Required]
public string Email { get; set; }
[Required]
public DepartmentDto Department { get; set; }
}
Next, in the Employee Controller add in the following.
[HttpPost]
public async Task<IActionResult> AddNewEmployeeWithDepartment(EmployeeDto employeeDto)
{
_dbContext.Connection.Open();
using (var transaction = _dbContext.Connection.BeginTransaction())
{
try
{
_dbContext.Database.UseTransaction(transaction as DbTransaction);
//Check if Department Exists (By Name)
bool DepartmentExists = await _dbContext.Departments.AnyAsync(a => a.Name == employeeDto.Department.Name);
if(DepartmentExists)
{
throw new Exception("Department Already Exists");
}
//Add Department
var addDepartmentQuery = $"INSERT INTO Departments(Name,Description) VALUES('{employeeDto.Department.Name}','{employeeDto.Department.Description}');SELECT CAST(SCOPE_IDENTITY() as int)";
var departmentId = await _writeDbConnection.QuerySingleAsync<int>(addDepartmentQuery, transaction: transaction);
//Check if Department Id is not Zero.
if(departmentId == 0)
{
throw new Exception("Department Id");
}
//Add Employee
var employee = new Employee
{
DepartmentId = departmentId,
Name = employeeDto.Name,
Email = employeeDto.Email
};
await _dbContext.Employees.AddAsync(employee);
await _dbContext.SaveChangesAsync(default);
//Commmit
transaction.Commit();
//Return EmployeeId
return Ok(employee.Id);
}
catch (Exception)
{
transaction.Rollback();
throw;
}
finally
{
_dbContext.Connection.Close();
}
}
}

Así que la idea es simple. Abra una conexión, cree un objeto de transacción. Use esta transacción en Entity framework core and dapper. Si alguna operación falla, revierta las transacciones para asegurarse de que no haya datos defectuosos. Si no hay excepciones, confirme la transacción y finalmente cierre la conexión.

Línea 4: abre la conexión.
Línea 5: crea / inicia la transacción a través del objeto _dbContext.
Línea 9: asigne este objeto de transacción a la base de datos.
Línea 11: compruebe si existe el departamento ingresado.

Línea 18: ejecute una consulta de inserción utilizando _writeDbConnection (Dapper) y devuelva la identificación agregada. Tenga en cuenta que aquí también estamos usando el objeto de transacción.

Línea 25: ahora que tenemos una identificación de departamento válida, creemos un objeto Empleado y complételo con los datos requeridos.
Línea 31: agrega el objeto a la instancia de dbContext.
Línea 32 – Finalmente guarda los cambios.
Línea 34 – Confirmar la transacción. Una vez que el control llega a esta línea, significa que no se encontraron excepciones y es seguro realizar la transacción.
Línea 36- Devuelve el ID de empleado recién generado.

Línea 40: si hubiera una excepción, todos los cambios en la base de datos se revertirían para mantener los datos limpios. Este es el uso real de las transacciones.
Línea 45 – Finalmente cierra las conexiones.

Con la explicación clara, veamos el código en acción. La siguiente será mi solicitud en POSTMAN al punto final POST del controlador de empleados.

{
"name": "Mukesh Murugan",
"email": "mukesh@google.es",
"department": {
"name" : "Development",
"description" : "Development department"
}
}

Ahora, ya sabemos que el Departamento con el Nombre Desarrollo no existe en la base de datos. Enviemos la solicitud.

transacciones POST usando Entity Framework Core y Dapper en ASP.NET Core - Transacciones seguras

Puede ver que la operación se completó y devuelve la identificación del empleado recién agregado. Verifiquemos nuestro punto final GetById usando esta ID.

transacción agregada Usando Entity Framework Core y Dapper en ASP.NET Core - Transacciones seguras

Puede ver que el registro se crea tanto en la tabla de Empleado como en la de Departamento, según lo necesitábamos. Así de fácil es usar Entity Framework Core y Dapper en la misma aplicación ASP.NET Core dentro de la misma transacción.

Ahora, ¿qué sucede cuando enviamos la solicitud POST nuevamente con el mismo cuerpo de solicitud?

Transacciones Error al usar Entity Framework Core y Dapper en ASP.NET Core - Transacciones seguras

PD: es posible que vea un mensaje diferente en POSTMAN, ya que estaba usando un paquete diferente mientras desarrollaba.

Eso es un final para este artículo. Espero que haya aclarado muchas dudas y abierto un enfoque poderoso para el uso de Entity Framework Core y Dapper en ASP.NET Core que se ocupa de las transacciones.

Resumen

En este artículo detallado, aprendimos sobre un enfoque realmente simple y poderoso para aprovechar los dos ORM más poderosos para ASP.NET Core, que son Entity Framework Core y Dapper. Además, hablamos sobre Transacciones y creamos una aplicación que puede hacer uso de ambos ORM teniendo en cuenta las transacciones. Como resultado, tenemos una aplicación que es lo suficientemente inteligente como para deshacer los cambios si ocurre alguna excepción en el proceso.

Puede encontrar el código fuente completo de la implementación aquí.

Deje sus valiosas consultas y sugerencias en la sección de comentarios a continuación. Además, si crees que aprendiste algo nuevo de este artículo, no olvides compartirlo con tu comunidad de desarrolladores. ¡Feliz codificación!

Fuente: https://codewithmukesh.com/blog/using-entity-framework-core-and-dapper/

   #entityframework 

Uso de Entity Framework Core y Dapper en ASP.NET Core
顾 静

顾 静

1659364200

在 ASP.NET Core 中使用 Entity Framework Core 和 Dapper

在本文中,我们将了解如何在同一个应用程序中同时使用 ASP.NET Core 中的 Entity Framework Core 和 Dapper。另一个主要的讨论点是交易。在本文的最后,我们将拥有一个应用程序,它可以同时与 Entity Framework Core 和 Dapper 一起工作,而且还足够智能,可以在进程出现异常时回滚数据。

让我们开始吧!

Dapper 与实体框架核心

考虑到 Dapper 没有花里胡哨的事实,Dapper 实际上比 Entity Framework Core 快得多。它是一个简单的微型 ORM,也具有最少的功能。开发人员始终可以在这两种出色的数据访问技术之间进行选择。这并不意味着 Entity Framework Core 更慢。随着每次更新,性能似乎也在提高。对于那些仍然喜欢使用 RAW 查询而不是使用 EFCore 的 LINQ 的人来说,Dapper 是天堂。

现在,Entity Framework Core 包含大量功能以及性能改进。所以这个问题是,为什么在 Dapper 和 Entity Framework Core 之间进行选择,当你可以同时使用两者并充分利用时,是吗?

Dapper 在处理包含多个连接和一些真正长业务逻辑的复杂查询方面非常棒。Entity Framework Core 非常适合类生成、对象跟踪、映射到多个嵌套类等等。因此,在谈论这两个 ORM 时,通常是性能和功能。

要求

我们将为一家虚构公司设计一个简单的 ASP.NET Core WebAPI。这家公司的政策规定,所有其他员工都必须与一个独特的部门相关联。更清楚地说,每次通过 API 端点添加新员工时,您还必须创建新的部门记录。一个非常虚构的要求,是吗?除此之外,我们还有 2 个其他端点,它们按 ID 返回所有员工和员工。

扩展细节,我们必须确保新添加的部门不存在。一旦您看到域实体,您就会掌握这一点。

为了演示 Dapper、Entity Framework Core 以及两者结合的用法,我们将在 3 个端点中分别实现它们。对于 GetAll Endpoints,我们将使用 Dapper。GetById 端点也将使用带有 Eager Loading 的 Entity Framework Core 来显示部门详细信息。最后,POST 端点将利用这些出色的数据访问技术和 ASP.NET Core 中的清晰演示事务。

在此过程中,我们将介绍一些用于 ASP.NET Core 的库,它们可能也可以为您节省一些开发时间。

需要处理的重要方面——交易

现在,根据我们的要求,我们需要 Entity Framework Core 和 Dapper 一起工作。这实际上很容易实现。但是需要注意的重要细节是,我们需要确保 Entity Framework Core 和 Dapper 都应该参与同一个 DB Transaction,以便整个过程能够健壮。

例如,特定的写入操作可能涉及多个实体和表。反过来,这可以具有易于由 Entity Framework Core 处理的操作,比方说一堆复杂的查询,这些查询旨在由 Dapper 执行。在这种情况下,我们必须确保当任何操作/查询失败时应该可以回滚 SQL Execute 操作。说得通?这是可以给我们的系统设计带来一点复杂性的方面。

如果我们不考虑这一点,整个过程将如此简单。让我把这个想法分成几个步骤。
1. 配置实体框架核心。
2. 配置小巧玲珑。您也可以使用 EFCore 使用的相同连接字符串来实现此目的。(显然,来自 appsettings.json)
3. 将服务注册到 Container 中,并根据需要开始使用 Context / Dapper。

但我们将寻求一种更复杂且面向未来的机制,该机制将处理包括回滚和事务在内的所有事情。得到它?

在 ASP.NET Core 中使用 Entity Framework Core 和 Dapper

设置解决方案和项目

我们将像往常一样遵循洋葱架构以形成清晰的关注点分离。首先,打开 Visual Studio 2019 并创建一个新的空白解决方案。在这里,我们将添加 4 个新项目,即 Domain、Persistence 和 WebApi。域和持久性项目是 .NET Core 类库,而 WebApi 是选择了 API 模板的 ASP.NET Core 3.1 Web 应用程序。

域项目将仅包含核心域实体和接口。持久层应该具有接口、服务以及与 Entity Framework Core 和 Dapper 相关的所有内容的实现。最后,WebApi 项目将具有 API 端点。

请注意,这是洋葱/六边形架构的一个非常小的实现。有关架构的更详细教程,请参阅这篇文章 - ASP.NET Core 中的洋葱架构与 CQRS - 详细

基于您从上述文章中了解到的详细信息,这里是 ASP.NET Core WebAPI 中 Clean Architecture 的完整实现,可作为样板模板提供,让您立即开始 - ASP.NET Core WebAPI - Clean Architecture (开源项目)

现在,让我们开始为每个项目安装所有必需的包。

领域项目包

很明显,域项目永远不应该依赖其他任何东西。但是由于我们将域和应用层合并为一个实体,并且为了简单起见,让我们将这两个包安装到域项目中。

Install-Package Microsoft.EntityFrameworkCore

持久性项目包

如前所述,与解决方案的基础架构/持久性相关的所有内容都将放在此项目中。继续并将以下软件包安装到持久性项目中。

Install-Package Dapper
Install-Package Microsoft.EntityFrameworkCore.Design
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Install-Package Microsoft.EntityFrameworkCore.Relational
Install-Package System.Data.SqlClient

WebApi 项目的包

最后,将这些包添加到 WebAPI 项目中。

Install-Package Microsoft.EntityFrameworkCore.Tools

添加域实体

导航到域项目并创建一个新的文件夹实体并将这两个类添加到它,员工和部门。

namespace Domain.Entities
{
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
}
using AspNetCoreHero.Abstractions;
namespace Domain.Entities
{
public class Employee : BaseEntity
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public int DepartmentId { get; set; }
public Department Department { get; set; }
}
}

设计接口

同样,在域项目中添加另一个文件夹 Interfaces。这里使用了依赖倒置的概念,这样系统不关心实现,只负责提供它真正想要的合约。使用这种方法,可以更轻松地设计出非常适合测试的干净简单的系统。通过这种方式,解决方案的核心不依赖于实体框架或 Dapper 之类的任何东西,而是让我们也可以轻松切换到不同的技术。

让我们添加我们的第一个接口 IApplicationDbContext.cs,它旨在使用 Entity Framework Core 在持久层实现。

public interface IApplicationDbContext
{
public IDbConnection Connection { get; }
DatabaseFacade Database { get; }
public DbSet<Employee> Employees { get; set; }
public DbSet<Department> Departments { get; set; }
Task<int> SaveChangesAsync(CancellationToken cancellationToken);
}

在这里,您可以看到我们使用了 2 个新属性 IDbConnection 和 DbFacade 。这只是对 Dapper 稍后将使用的数据库的直接访问。还记得,我们说过要让 EFCore 和 Dapper 一起工作吗?

接下来,让我们再添加 2 个专门用于读取和写入的接口。读取接口将具有仅对数据库运行查询的合同。而写接口将是完整的封装。

请注意,我们还使用了 IDbTransaction,当我们在本文后面开始处理事务时,它会派上用场。

public interface IApplicationReadDbConnection
{
Task<IReadOnlyList<T>> QueryAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default);
Task<T> QueryFirstOrDefaultAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default);
Task<T> QuerySingleAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default);
}
public interface IApplicationWriteDbConnection : IApplicationReadDbConnection
{
Task<int> ExecuteAsync(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default);
}

这几乎就是我们需要对域项目做的所有事情。让我们现在开始实施。

设置 EntityFrameworkCore

在 Persistence Project 中,让我们添加一个新的文件夹上下文,并添加到 ApplicationDbContext.cs 文件中。

public class ApplicationDbContext : DbContext, IApplicationDbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{
}
public DbSet<Employee> Employees { get; set; }
public DbSet<Department> Departments { get; set; }
public IDbConnection Connection => Database.GetDbConnection();
}

您可以看到,在第 8 行,我们正在从 Entity Framework Core 实例中提取 Connection 对象。

详细了解Entity Framework Core,请参考以下文章——ASP.NET Core 3.1 中的Entity Framework Core——入门

设置 Dapper

现在,在 Persistence 项目中添加另一个文件夹并将其命名为 Connections。两者都是 Read 和 Write 接口将在此处实现。添加以下类。

public class ApplicationReadDbConnection : IApplicationReadDbConnection, IDisposable
{
private readonly IDbConnection connection;
public ApplicationReadDbConnection(IConfiguration configuration)
{
connection = new SqlConnection(configuration.GetConnectionString("DefaultConnection"));
}
public async Task<IReadOnlyList<T>> QueryAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return (await connection.QueryAsync<T>(sql, param, transaction)).AsList();
}
public async Task<T> QueryFirstOrDefaultAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return await connection.QueryFirstOrDefaultAsync<T>(sql, param, transaction);
}
public async Task<T> QuerySingleAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return await connection.QuerySingleAsync<T>(sql, param, transaction);
}
public void Dispose()
{
connection.Dispose();
}
}

确保您也实现了 IDisposible 接口。

在构造函数中,您可以看到我们正在使用 appSettings.json 中的连接字符串初始化一个新连接。请注意,在此实现中,没有与任何 DBContext 对象(Entity Framework Core)的链接,因为在读取数据时共享 Entity Framework Core 和 Dapper 之间的连接确实没有意义。

当涉及数据写入时,共享连接的用例就出现了。让我们看看它是如何实现的。

public class ApplicationWriteDbConnection : IApplicationWriteDbConnection
{
private readonly IApplicationDbContext context;
public ApplicationWriteDbConnection(IApplicationDbContext context)
{
this.context = context;
}
public async Task<int> ExecuteAsync(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return await context.Connection.ExecuteAsync(sql, param, transaction);
}
public async Task<IReadOnlyList<T>> QueryAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return (await context.Connection.QueryAsync<T>(sql, param, transaction)).AsList();
}
public async Task<T> QueryFirstOrDefaultAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return await context.Connection.QueryFirstOrDefaultAsync<T>(sql, param, transaction);
}
public async Task<T> QuerySingleAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return await context.Connection.QuerySingleAsync<T>(sql, param, transaction);
}
}

在实现的写入中,我们确实有一个完整的连接实现,具有读取和写入功能。您也可以避免读取功能。但这取决于您的喜好。

您可以看到我们正在将属于实体框架的 IApplicationDbContext 注入到构造函数中。这就是我们可以共享连接和事务的方式。使用上下文的连接,我们使用 Dapper 执行读写操作。很酷,是吗?

在 Read 实现中,我们直接使用我们在构造函数中使用连接字符串初始化的 IDbConnection 对象。同时,在 Write 实现中,我们在 Dapper 的帮助下重新使用上下文对象来执行查询和命令。

详细了解Dapper,请参考以下文章- ASP.NET Core 中的Dapper with Repository Pattern - 详解

添加连接字符串

打开你的 appsettings.json 并添加你的连接字符串。

"ConnectionStrings": {
"DefaultConnection": "Data Source=LAPTOP-7CS9KHVQ;Initial Catalog=demoDb;Integrated Security=True;MultipleActiveResultSets=True"
},

注册服务

最后,让我们将这些接口和类注册到我们的 ASP.NET Core Web 应用程序的服务容器中。在 WebAPI 项目中打开您的 Startup.cs 并按如下方式修改您的 ConfigureServices。

public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options
.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))
);
services.AddScoped<IApplicationDbContext>(provider => provider.GetService<ApplicationDbContext>());
services.AddScoped<IApplicationWriteDbConnection, ApplicationWriteDbConnection>();
services.AddScoped<IApplicationReadDbConnection, ApplicationReadDbConnection>();
services.AddControllers();
}

添加迁移和更新数据库

右键单击 WebAPI 项目并确保将其设置为解决方案的启动项目。接下来打开包管理器控制台并从下拉菜单中将默认项目设置为基础设施。

输入以下内容以添加迁移并使用员工和部门表更新数据库。

add-migration initial
update-database

与控制器接线

由于我们已经处理了所有繁重的工作,让我们使用提到的 3 个端点构建我们的控制器。在 Controllers 文件夹下添加一个新的 Controller 并将其命名为 Employee Controller。

让我们将之前创建的所有 3 个接口注入到 Controller 的构造函数中。理想情况下,您将使用服务层或 CQRS 模式来执行此操作。但同样,为了简单起见,我们将在这里声明注入。

如果您有兴趣了解使用 MediatR 库在 ASP.NET Core 中实现 CQRS,请参阅这篇详细的文章 – CQRS with MediatR in ASP.NET Core 3.1 – Ultimate Guide

public EmployeeController(IApplicationDbContext dbContext, IApplicationReadDbConnection readDbConnection, IApplicationWriteDbConnection writeDbConnection)
{
_dbContext = dbContext;
_readDbConnection = readDbConnection;
_writeDbConnection = writeDbConnection;
}
public IApplicationDbContext _dbContext { get; }
public IApplicationReadDbConnection _readDbConnection { get; }
public IApplicationWriteDbConnection _writeDbConnection { get; }

让我们根据我们的要求开始使用端点。

获取所有员工

首先是一个简单的端点,它将从数据库中返回所有可用的 Employee。我们将为此使用 Dapper,因此我们将使用 _readDbConnection,有意义吗?

[HttpGet]
public async Task<IActionResult> GetAllEmployees()
{
var query = $"SELECT * FROM Employees";
var employees = await _readDbConnection.QueryAsync<Employee>(query);
return Ok(employees);
}

在执行之前,让我在我们的表中添加一些虚拟数据。

添加数据后,让我们运行我们的应用程序并打开POSTMAN。向 api/employee 端点发送 GET 请求。这将从数据库中返回可用员工的列表。请记住,这是由 Dapper 编写的。查询执行时间总是比其他 ORM 好得多。但要注意的主要一点是,dapper 理想地以平坦的水平返回数据。它不足以填充子嵌套对象。可以看到 Department 为 NULL。

话虽这么说,你仍然可以通过 Dapper 来实现这一点,但是通过添加比理想情况更多的代码,可能是通过多个查询。这是您必须在 Dapper 和 Entity Framework Core 之间进行选择的边距。另一方面,EFCore 可以在没有额外代码的情况下实现这一目标。我们将在下一个 Endpoint 中看到它。

dapper getAll 在 ASP.NET Core 中使用 Entity Framework Core 和 Dapper - 安全事务

按 ID 获取员工

让我们添加一个可以根据 Id 返回 Employee 的端点。我们将为此端点使用 _dbContext 对象。我们还引入了 Entity Framework Core 的 Eager Loading,它可以轻松填充嵌套的子对象。

[HttpGet("{id}")]
public async Task<IActionResult> GetAllEmployeesById(int id)
{
var employees = await _dbContext.Employees.Include(a => a.Department).Where(a => a.Id == id).ToListAsync();
return Ok(employees);
}

efcore getById 在 ASP.NET Core 中使用 Entity Framework Core 和 Dapper - 安全事务

现在您可以看到我们也在获取部门详细信息。

一次性创建员工和部门

最后,让我们处理一个更复杂的端点。这是根据要求制定的算法。

  1. 接受包含新员工和新部门数据作为参数的 DTO 类
  2. 确保您使用的是相同的事务和 Dapper / Entity Framework Core
  3. 检查部门名称是否已存在。如果部门存在则抛出异常。
  4. 将部门详细信息插入部门表。
  5. 返回部门 ID
  6. 将员工详细信息与生成的部门 ID 一起插入到员工表中。
  7. 返回员工 ID 并提交交易
  8. 如果上述任何操作失败或抛出异常,则回滚事务并确保数据库不受影响。

所以这就是详细的要求。让我们添加 DTO 类。在 WebAPI 项目中添加一个新文件夹 DTO 并添加以下类。

public class DepartmentDto
{
[Required]
public string Name { get; set; }
public string Description { get; set; }
}
public class EmployeeDto
{
[Required]
public string Name { get; set; }
[Required]
public string Email { get; set; }
[Required]
public DepartmentDto Department { get; set; }
}
Next, in the Employee Controller add in the following.
[HttpPost]
public async Task<IActionResult> AddNewEmployeeWithDepartment(EmployeeDto employeeDto)
{
_dbContext.Connection.Open();
using (var transaction = _dbContext.Connection.BeginTransaction())
{
try
{
_dbContext.Database.UseTransaction(transaction as DbTransaction);
//Check if Department Exists (By Name)
bool DepartmentExists = await _dbContext.Departments.AnyAsync(a => a.Name == employeeDto.Department.Name);
if(DepartmentExists)
{
throw new Exception("Department Already Exists");
}
//Add Department
var addDepartmentQuery = $"INSERT INTO Departments(Name,Description) VALUES('{employeeDto.Department.Name}','{employeeDto.Department.Description}');SELECT CAST(SCOPE_IDENTITY() as int)";
var departmentId = await _writeDbConnection.QuerySingleAsync<int>(addDepartmentQuery, transaction: transaction);
//Check if Department Id is not Zero.
if(departmentId == 0)
{
throw new Exception("Department Id");
}
//Add Employee
var employee = new Employee
{
DepartmentId = departmentId,
Name = employeeDto.Name,
Email = employeeDto.Email
};
await _dbContext.Employees.AddAsync(employee);
await _dbContext.SaveChangesAsync(default);
//Commmit
transaction.Commit();
//Return EmployeeId
return Ok(employee.Id);
}
catch (Exception)
{
transaction.Rollback();
throw;
}
finally
{
_dbContext.Connection.Close();
}
}
}

所以这个想法很简单。打开一个连接,创建一个事务对象。跨实体框架核心和 dapper 使用此事务。如果任何操作失败,则回滚事务以确保没有错误数据。如果没有异常,提交事务,最后关闭连接。

第 4 行 – 打开连接
第 5 行 – 通过 _dbContext 对象创建/启动事务。
第 9 行 – 将此事务对象分配给数据库。
第 11 行 - 检查输入的部门是否存在。

第 18 行 – 使用 _writeDbConnection (Dapper) 执行插入查询并返回添加的 Id。请注意,我们也在此处使用事务对象。

第 25 行——现在我们有了一个有效的部门 ID,让我们创建一个 Employee 对象并用所需的数据填充它。
第 31 行 - 将对象添加到 dbContext 实例。
第 32 行 - 最后保存更改。
第 34 行——提交事务。一旦控件到达这一行,就意味着我们没有遇到异常并且可以安全地提交事务。
第 36 行 - 返回新生成的员工 ID。

第 40 行——如果出现异常,所有数据库更改都将回滚以保持数据干净。这是事务的实际使用。
第 45 行——最后关闭连接。

解释清楚后,让我们实际看看代码在运行中。以下将是我对 POSTMAN 对员工控制器 POST 端点的请求。

{
"name": "Mukesh Murugan",
"email": "mukesh@google.es",
"department": {
"name" : "Development",
"description" : "Development department"
}
}

现在,我们已经知道数据库中不存在名称为Development 的Department。让我们发送请求。

在 ASP.NET Core 中使用 Entity Framework Core 和 Dapper 进行事务 POST - 安全事务

可以看到操作完成,返回新添加员工的ID。让我们使用这个 ID 检查我们的 GetById 端点。

在 ASP.NET Core 中使用 Entity Framework Core 和 Dapper 添加事务 - 安全事务

您可以看到记录是根据我们的需要在 Employee 和 Department 表中创建的。这就是在同一个事务中的同一个 ASP.NET Core 应用程序中使用 Entity Framework Core 和 Dapper 是多么容易。

现在,当我们使用相同的请求正文再次发送 POST 请求时会发生什么?

在 ASP.NET Core 中使用 Entity Framework Core 和 Dapper 时出现事务错误 - 安全事务

PS,您可能会在 POSTMAN 中看到不同的消息,因为我在开发时使用了不同的包。

这是本文的总结。我希望它消除了很多疑虑,并开辟了一种在 ASP.NET Core 中使用 Entity Framework Core 和 Dapper 来处理事务的强大方法。

概括

在这篇详细的文章中,我们了解了一种非常简单而强大的方法来利用 ASP.NET Core 的两个最强大的 ORM,即 Entity Framework Core 和 Dapper。此外,我们讨论了事务并构建了一个应用程序,该应用程序可以利用两个 ORM 来记住事务。因此,我们有一个足够智能的应用程序,可以在过程中发生任何异常时回滚更改。

您可以在此处找到实现的完整源代码。

在下面的评论部分留下您宝贵的疑问和建议。此外,如果您认为您从本文中学到了一些新东西,请不要忘记在您的开发人员社区中分享。快乐编码!

来源:https ://codewithmukesh.com/blog/using-entity-framework-core-and-dapper/

   #entityframework 

在 ASP.NET Core 中使用 Entity Framework Core 和 Dapper
Hoang  Kim

Hoang Kim

1659363540

Sử Dụng Entity Framework Core và Dapper trong ASP.NET Core

Trong bài viết này, chúng ta sẽ tìm hiểu về cách sử dụng Entity Framework Core và Dapper trong ASP.NET Core cùng nhau trong cùng một ứng dụng. Một điểm chính khác của cuộc thảo luận sẽ là Giao dịch. Đến cuối bài viết, chúng ta sẽ có một ứng dụng hoạt động với cả Entity Framework Core và Dapper song song với nhau, nhưng cũng đủ thông minh để khôi phục dữ liệu bất cứ khi nào có ngoại lệ với quy trình.

Bắt đầu nào!

Dapper vs Entity Framework Core

Dapper thực sự nhanh hơn nhiều so với Entity Framework Core xét trên thực tế là không có chuông và còi trong Dapper. Nó là một Micro ORM chuyển tiếp cũng có các tính năng tối thiểu. Nhà phát triển luôn có quyền lựa chọn giữa 2 Công nghệ Truy cập Dữ liệu Tuyệt vời này. Điều này không có nghĩa là Entity Framework Core chậm hơn. Với mỗi bản cập nhật, hiệu suất dường như cũng được cải thiện. Dapper là thiên đường cho những ai vẫn thích làm việc với Truy vấn RAW hơn là LINQ với EFCore.

Giờ đây, Entity Framework Core có rất nhiều tính năng đi kèm cùng với các cải tiến về hiệu suất. Vì vậy, câu hỏi đặt ra là, Tại sao lại chọn giữa Dapper và Entity Framework Core khi bạn có thể sử dụng cả hai và tận dụng tối đa lợi ích, đúng vậy?

Dapper là siêu tuyệt vời để xử lý các truy vấn phức tạp có nhiều phép nối và một số logic nghiệp vụ thực sự dài. Entity Framework Core rất tuyệt vời để tạo lớp, theo dõi đối tượng, ánh xạ tới nhiều lớp lồng nhau và hơn thế nữa. Vì vậy, nó thường là Hiệu suất và Tính năng khi nói về 2 ORM này.

Yêu cầu

Chúng tôi sẽ thiết kế một ASP.NET Core WebAPI đơn giản cho một Công ty tưởng tượng. Công ty này có một chính sách nói rằng mọi Nhân viên khác phải được liên kết với một Bộ phận duy nhất. Để rõ ràng hơn, mỗi khi bạn thêm một nhân viên mới thông qua điểm cuối API, bạn cũng phải tạo Hồ sơ phòng ban mới. Một yêu cầu rất tưởng tượng, phải không? Cùng với điều này, chúng ta sẽ có 2 điểm cuối khác trả về tất cả Nhân viên và Nhân viên theo Id.

Mở rộng chi tiết, chúng tôi sẽ phải đảm bảo rằng Bộ phận mới được thêm vào chưa tồn tại. Bạn sẽ nắm được điều này khi bạn nhìn thấy các Đối tượng miền.

Để chứng minh việc sử dụng Dapper, Entity Framework Core và cả hai được kết hợp, chúng tôi sẽ triển khai chúng trong 3 Endpoints. Đối với các Điểm cuối GetAll, chúng tôi sẽ sử dụng Dapper. Điểm cuối GetById sẽ sử dụng Lõi khung thực thể với tính năng Tải hứng thú để hiển thị cả Chi tiết phòng ban. Và cuối cùng, POST Endpoint sẽ tận dụng lợi thế của cả hai Công nghệ Truy cập Dữ liệu tuyệt vời này và Các Giao dịch Chứng minh Sạch sẽ trong ASP.NET Core.

Trong quá trình này, chúng tôi sẽ giới thiệu một số Thư viện cho ASP.NET Core có thể giúp bạn tiết kiệm thời gian phát triển.

Các khía cạnh quan trọng cần xử lý - Giao dịch

Bây giờ, theo yêu cầu của chúng tôi, chúng tôi cần cả Entity Framework Core và Dapper để làm việc cùng nhau. Điều này thực sự khá dễ dàng để đạt được. Nhưng chi tiết quan trọng cần lưu ý là chúng ta cần đảm bảo rằng cả Entity Framework Core và Dapper đều phải tham gia vào cùng một Giao dịch DB để quá trình tổng thể có thể được mạnh mẽ.

Ví dụ, một Thao tác Viết cụ thể có thể liên quan đến nhiều thực thể và bảng. Điều này đến lượt nó có thể có các hoạt động dễ dàng được xử lý bởi Entity Framework Core, và giả sử một loạt các Truy vấn phức tạp được thực thi bởi Dapper. Trong những trường hợp như vậy, chúng tôi phải đảm bảo rằng có thể khôi phục hoạt động SQL Execute khi bất kỳ hoạt động / truy vấn nào không thành công. Có ý nghĩa? Đây là khía cạnh có thể gây ra một sự phức tạp nhỏ cho thiết kế hệ thống của chúng tôi.

Nếu chúng ta không xem xét điều này, quá trình tổng thể sẽ rất đơn giản. Hãy để tôi đưa ý tưởng thành các bước.
1. Cấu hình Core Framework thực thể.
2. Cấu hình Dapper. Bạn có thể đạt được điều này bằng cách sử dụng cùng một chuỗi kết nối đang được sử dụng bởi EFCore. (Rõ ràng là từ appsettings.json)
3. Đăng ký các dịch vụ vào Container và bắt đầu sử dụng Context / Dapper theo yêu cầu.

Nhưng chúng tôi sẽ tìm kiếm một cơ chế phức tạp hơn và bền vững hơn trong tương lai, sẽ xử lý thực sự mọi thứ, bao gồm cả Rollbacks và Giao dịch. Hiểu rồi?

Sử dụng Entity Framework Core và Dapper trong ASP.NET Core

Thiết lập Giải pháp và Dự án

Chúng tôi sẽ làm theo một Kiến trúc Hành như thường lệ để tạo thành một sự tách biệt rõ ràng về các mối quan tâm. Đầu tiên, hãy mở Visual Studio 2019 và tạo giải pháp trống mới. Ở đây chúng ta sẽ thêm 4 Project mới là Domain, Persistence và WebApi. Miền và Dự án bền vững là Thư viện Lớp lõi .NET trong khi WebApi là Ứng dụng Web ASP.NET Core 3.1 với Mẫu API được chọn.

Dự án miền sẽ chỉ bao gồm các Thực thể và Giao diện Miền cốt lõi. Lớp Kiên trì nên có việc Triển khai các Giao diện, Dịch vụ và mọi thứ liên quan đến Core và Dapper của Entity Framework. Cuối cùng, Dự án WebApi sẽ có các Điểm cuối API.

Xin lưu ý rằng đây là một triển khai rất tối thiểu của Kiến trúc Hành tây / Lục giác. Để có hướng dẫn chi tiết hơn về Kiến trúc, hãy tham khảo bài viết này - Kiến trúc củ hành trong ASP.NET Core với CQRS - Chi tiết

Xây dựng dựa trên các chi tiết mà bạn đã học được từ bài viết trên, đây là cách triển khai đầy đủ của Kiến trúc sạch trong ASP.NET Core WebAPI có sẵn dưới dạng Mẫu bảng trình bày giúp bạn bắt đầu nhanh chóng - ASP.NET Core WebAPI - Kiến trúc sạch ( Dự án nguồn mở)

Bây giờ, hãy bắt đầu cài đặt tất cả các gói cần thiết cho mỗi dự án.

Các gói cho dự án miền

Rõ ràng là Dự án miền KHÔNG BAO GIỜ phụ thuộc vào bất kỳ thứ gì khác. Nhưng vì chúng ta đang kết hợp cả lớp Miền và lớp Ứng dụng thành một thực thể duy nhất và vì lý do để giữ cho mọi thứ đơn giản, hãy cài đặt 2 gói này qua Dự án Miền.

Install-Package Microsoft.EntityFrameworkCore

Các gói cho Dự án bền vững

Như đã đề cập trước đó, mọi thứ liên quan đến Cơ sở hạ tầng / Tính bền vững của Giải pháp sẽ được đặt trong Dự án này. Hãy tiếp tục và cài đặt các gói sau vào Dự án bền bỉ.

Install-Package Dapper
Install-Package Microsoft.EntityFrameworkCore.Design
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Install-Package Microsoft.EntityFrameworkCore.Relational
Install-Package System.Data.SqlClient

Các gói cho Dự án WebApi

Cuối cùng, thêm các gói này vào Dự án WebAPI.

Install-Package Microsoft.EntityFrameworkCore.Tools

Thêm các thực thể miền

Điều hướng đến Dự án miền và tạo Thực thể thư mục mới và thêm 2 lớp này vào đó, Nhân viên và Phòng ban.

namespace Domain.Entities
{
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
}
using AspNetCoreHero.Abstractions;
namespace Domain.Entities
{
public class Employee : BaseEntity
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public int DepartmentId { get; set; }
public Department Department { get; set; }
}
}

Thiết kế giao diện

Một lần nữa, trong Dự án miền, hãy thêm một thư mục khác, Giao diện. Ở đây đang sử dụng khái niệm Đảo ngược phụ thuộc, để hệ thống không quan tâm đến việc thực hiện và chỉ chịu trách nhiệm cung cấp Hợp đồng theo những gì nó thực sự muốn. Với cách tiếp cận này, sẽ dễ dàng hơn trong việc thiết kế các hệ thống sạch sẽ và đơn giản, rất lý tưởng cho việc Thử nghiệm. Theo cách này, Cốt lõi của Giải pháp không phụ thuộc vào bất kỳ thứ gì như Entity Framework hay Dapper, thay vào đó, nó giúp chúng ta dễ dàng chuyển sang các công nghệ khác nhau.

Hãy thêm Giao diện đầu tiên của chúng ta, IApplicationDbContext.cs, giao diện này có nghĩa là được triển khai tại Lớp Persistence bằng cách sử dụng Entity Framework Core.

public interface IApplicationDbContext
{
public IDbConnection Connection { get; }
DatabaseFacade Database { get; }
public DbSet<Employee> Employees { get; set; }
public DbSet<Department> Departments { get; set; }
Task<int> SaveChangesAsync(CancellationToken cancellationToken);
}

Ở đây bạn có thể thấy rằng chúng tôi đang sử dụng 2 thuộc tính mới là IDbConnection và DbFacade. Đây chỉ là cách truy cập trực tiếp vào Cơ sở dữ liệu sẽ được Dapper sử dụng sau này. Hãy nhớ rằng, chúng ta đã nói về việc làm cho cả EFCore và Dapper hoạt động song song với nhau?

Tiếp theo, hãy thêm 2 giao diện khác dành riêng cho Đọc và Viết. Giao diện Đọc sẽ có các hợp đồng để chỉ chạy các Truy vấn dựa trên cơ sở dữ liệu. trong khi Giao diện Viết sẽ là gói hoàn chỉnh.

Lưu ý rằng chúng tôi cũng đang sử dụng IDbTransaction sẽ rất hữu ích khi chúng tôi bắt đầu xử lý các Giao dịch ở phần sau của bài viết này.

public interface IApplicationReadDbConnection
{
Task<IReadOnlyList<T>> QueryAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default);
Task<T> QueryFirstOrDefaultAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default);
Task<T> QuerySingleAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default);
}
public interface IApplicationWriteDbConnection : IApplicationReadDbConnection
{
Task<int> ExecuteAsync(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default);
}

Đó là hầu hết mọi thứ chúng ta cần làm với Dự án miền. Hãy bắt đầu triển khai ngay bây giờ.

Thiết lập EntityFrameworkCore

Trong Dự án Kiên trì, hãy thêm một Khung cảnh thư mục mới và thêm vào tệp ApplicationDbContext.cs.

public class ApplicationDbContext : DbContext, IApplicationDbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{
}
public DbSet<Employee> Employees { get; set; }
public DbSet<Department> Departments { get; set; }
public IDbConnection Connection => Database.GetDbConnection();
}

Bạn có thể thấy rằng, tại Dòng 8, chúng tôi đang trích xuất đối tượng Connection từ cá thể Entity Framework Core.

Để tìm hiểu chi tiết về Entity Framework Core, vui lòng tham khảo bài viết sau - Entity Framework Core trong ASP.NET Core 3.1 - Bắt đầu

Thiết lập Dapper

Bây giờ, trong dự án Persistence, hãy thêm một thư mục khác và đặt tên là Connections. Cả hai đều là Giao diện Đọc và Ghi sẽ được thực hiện tại đây. Thêm các lớp sau.

public class ApplicationReadDbConnection : IApplicationReadDbConnection, IDisposable
{
private readonly IDbConnection connection;
public ApplicationReadDbConnection(IConfiguration configuration)
{
connection = new SqlConnection(configuration.GetConnectionString("DefaultConnection"));
}
public async Task<IReadOnlyList<T>> QueryAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return (await connection.QueryAsync<T>(sql, param, transaction)).AsList();
}
public async Task<T> QueryFirstOrDefaultAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return await connection.QueryFirstOrDefaultAsync<T>(sql, param, transaction);
}
public async Task<T> QuerySingleAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return await connection.QuerySingleAsync<T>(sql, param, transaction);
}
public void Dispose()
{
connection.Dispose();
}
}

Đảm bảo rằng bạn cũng đang triển khai giao diện IDisposible.

Tại hàm tạo, bạn có thể thấy rằng chúng tôi đang khởi tạo một Kết nối mới bằng cách sử dụng chuỗi kết nối từ appSettings.json của chúng tôi. Lưu ý rằng trong cách triển khai này, không có liên kết nào với bất kỳ đối tượng DBContext nào (Entity Framework Core) vì thực sự không có ý nghĩa khi chia sẻ kết nối giữa Entity Framework Core và Dapper khi bạn đang đọc dữ liệu.

Trường hợp sử dụng của việc chia sẻ kết nối sẽ thành hình khi có liên quan đến việc ghi dữ liệu. Hãy xem nó được thực hiện như thế nào.

public class ApplicationWriteDbConnection : IApplicationWriteDbConnection
{
private readonly IApplicationDbContext context;
public ApplicationWriteDbConnection(IApplicationDbContext context)
{
this.context = context;
}
public async Task<int> ExecuteAsync(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return await context.Connection.ExecuteAsync(sql, param, transaction);
}
public async Task<IReadOnlyList<T>> QueryAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return (await context.Connection.QueryAsync<T>(sql, param, transaction)).AsList();
}
public async Task<T> QueryFirstOrDefaultAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return await context.Connection.QueryFirstOrDefaultAsync<T>(sql, param, transaction);
}
public async Task<T> QuerySingleAsync<T>(string sql, object param = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default)
{
return await context.Connection.QuerySingleAsync<T>(sql, param, transaction);
}
}

Trong quá trình viết được triển khai, chúng tôi thực sự có một triển khai Kết nối toàn diện với cả Chức năng Đọc và Ghi. Bạn cũng có thể tránh Chức năng Đọc. Nhưng nó phụ thuộc vào sở thích của bạn.

Bạn có thể thấy rằng chúng tôi đang đưa IApplicationDbContext thuộc Entity Framework vào Constructor. Đây là cách chúng ta có thể chia sẻ kết nối và giao dịch. Sử dụng kết nối của ngữ cảnh, chúng tôi thực hiện các thao tác đọc và ghi bằng Dapper. Khá tuyệt, phải không?

Trong phần triển khai Read, chúng tôi đã trực tiếp làm việc với đối tượng IDbConnection mà chúng tôi đã khởi tạo trong phương thức khởi tạo bằng cách sử dụng chuỗi kết nối. Trong khi, ở đây trong phần triển khai Write, chúng ta đang sử dụng lại đối tượng ngữ cảnh để thực hiện các truy vấn và lệnh với sự trợ giúp của Dapper.

Để tìm hiểu chi tiết về Dapper, vui lòng tham khảo bài viết sau - Dapper trong ASP.NET Core với Mẫu kho lưu trữ - Chi tiết

Thêm chuỗi kết nối

Mở appsettings.json của bạn và thêm chuỗi kết nối của bạn.

"ConnectionStrings": {
"DefaultConnection": "Data Source=LAPTOP-7CS9KHVQ;Initial Catalog=demoDb;Integrated Security=True;MultipleActiveResultSets=True"
},

Đăng ký Dịch vụ

Cuối cùng, hãy đăng ký các giao diện và lớp này vào vùng chứa dịch vụ của Ứng dụng Web ASP.NET Core của chúng ta. Mở Startup.cs của bạn trong Dự án WebAPI và sửa đổi ConfigureServices của bạn như sau.

public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options
.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))
);
services.AddScoped<IApplicationDbContext>(provider => provider.GetService<ApplicationDbContext>());
services.AddScoped<IApplicationWriteDbConnection, ApplicationWriteDbConnection>();
services.AddScoped<IApplicationReadDbConnection, ApplicationReadDbConnection>();
services.AddControllers();
}

Thêm di chuyển và cập nhật cơ sở dữ liệu

Nhấp chuột phải vào Dự án WebAPI và đảm bảo rằng nó được đặt làm Dự án Khởi động của giải pháp. Tiếp theo, mở Bảng điều khiển Trình quản lý Gói và đặt dự án mặc định thành Infrastrcture từ menu thả xuống.

Nhập thông tin sau để thêm di chuyển và cập nhật cơ sở dữ liệu với Bảng Nhân viên và Bộ phận.

add-migration initial
update-database

Nối dây với Bộ điều khiển

Vì chúng ta đã thực hiện tất cả các công việc nặng nhọc, hãy xây dựng bộ điều khiển của chúng ta với 3 điểm cuối đã đề cập. Thêm Bộ điều khiển mới trong thư mục Bộ điều khiển và đặt tên là Bộ điều khiển nhân viên.

Hãy đưa tất cả 3 giao diện mà chúng ta đã tạo trước đó vào Constructor của Controller. Lý tưởng nhất là bạn nên sử dụng Lớp dịch vụ hoặc Mẫu CQRS để thực hiện việc này. Nhưng một lần nữa, để giữ cho mọi thứ đơn giản, chúng tôi sẽ khai báo Tiêm ở đây.

Nếu bạn muốn tìm hiểu về Triển khai CQRS trong ASP.NET Core với thư viện MediatR, hãy tham khảo bài viết chi tiết này - CQRS với MediatR trong ASP.NET Core 3.1 - Hướng dẫn cuối cùng

public EmployeeController(IApplicationDbContext dbContext, IApplicationReadDbConnection readDbConnection, IApplicationWriteDbConnection writeDbConnection)
{
_dbContext = dbContext;
_readDbConnection = readDbConnection;
_writeDbConnection = writeDbConnection;
}
public IApplicationDbContext _dbContext { get; }
public IApplicationReadDbConnection _readDbConnection { get; }
public IApplicationWriteDbConnection _writeDbConnection { get; }

Hãy bắt đầu với các điểm cuối theo yêu cầu của chúng tôi.

Nhận tất cả nhân viên

Đầu tiên là một điểm cuối đơn giản sẽ trả về tất cả các Nhân viên có sẵn từ cơ sở dữ liệu. Chúng tôi sẽ sử dụng Dapper cho việc này, do đó chúng tôi sẽ sử dụng _readDbConnection, có lý không?

[HttpGet]
public async Task<IActionResult> GetAllEmployees()
{
var query = $"SELECT * FROM Employees";
var employees = await _readDbConnection.QueryAsync<Employee>(query);
return Ok(employees);
}

Trước khi thực thi, hãy để tôi thêm một số dữ liệu giả vào bảng của chúng tôi.

Sau khi dữ liệu được thêm vào, hãy chạy ứng dụng của chúng tôi và mở POSTMAN . Gửi yêu cầu GET đến điểm cuối api / nhân viên. Thao tác này sẽ trả về danh sách các nhân viên có sẵn từ cơ sở dữ liệu. Hãy nhớ điều này là của Dapper. Thời gian thực thi Truy vấn luôn có nghĩa là tốt hơn nhiều so với các ORM khác. Nhưng điểm chính cần lưu ý là lý tưởng nhất là dapper trả về dữ liệu ở mức phẳng. Nó không đủ mạnh để lấp đầy các đối tượng lồng vào nhau. Bạn có thể thấy rằng Bộ là NULL.

Điều đó đang được nói, bạn vẫn có thể đạt được điều này bằng Dapper nhưng bằng cách thêm nhiều mã hơn bạn tưởng tượng., Có thể bằng nhiều truy vấn. Đây là Margin nơi bạn phải chọn giữa Dapper và Entity Framework Core. Mặt khác, EFCore có thể thực hiện điều này mà thực sự không cần thêm mã. Chúng ta sẽ xem về nó trong Endpoint tiếp theo.

dapper getAll Sử dụng Entity Framework Core và Dapper trong ASP.NET Core - Giao dịch an toàn

Nhận nhân viên theo Id

Hãy thêm một điểm cuối có thể trả về một Nhân viên dựa trên Id. Chúng tôi sẽ sử dụng đối tượng _dbContext cho điểm cuối này. Chúng tôi cũng đang giới thiệu Eager Loading bằng Entity Framework Core có thể điền vào đối tượng con lồng nhau một cách dễ dàng.

[HttpGet("{id}")]
public async Task<IActionResult> GetAllEmployeesById(int id)
{
var employees = await _dbContext.Employees.Include(a => a.Department).Where(a => a.Id == id).ToListAsync();
return Ok(employees);
}

efcore getById Sử dụng Entity Framework Core và Dapper trong ASP.NET Core - Giao dịch an toàn

Bây giờ bạn có thể thấy rằng chúng tôi cũng đang nhận được thông tin chi tiết về Bộ.

Tạo nhân viên và phòng ban trong một lần

Cuối cùng, hãy làm việc trên một điểm cuối phức tạp hơn một chút. Đây là công thức algortihm theo yêu cầu.

  1. Chấp nhận một Lớp DTO có chứa dữ liệu của Nhân viên mới và Bộ phận mới dưới dạng các tham số
  2. Đảm bảo rằng bạn đang sử dụng cùng một Giao dịch và Lõi khung Dapper / Thực thể
  3. Kiểm tra xem tên Bộ đã tồn tại chưa. Ném một ngoại lệ nếu bộ phận tồn tại.
  4. Chèn chi tiết phòng ban vào Bảng phòng ban.
  5. Trả lại ID phòng ban
  6. Chèn chi tiết Nhân viên vào bảng Nhân viên cùng với Id bộ phận đã tạo.
  7. Trả lại Id nhân viên và thực hiện giao dịch
  8. Nếu bất kỳ thao tác nào ở trên không thành công hoặc có ngoại lệ, hãy khôi phục giao dịch và đảm bảo rằng db không bị ảnh hưởng.

Vì vậy, đó là về yêu cầu chi tiết. Hãy thêm các Lớp DTO. Trong Dự án WebAPI, hãy thêm một thư mục mới, DTO và thêm vào các lớp sau.

public class DepartmentDto
{
[Required]
public string Name { get; set; }
public string Description { get; set; }
}
public class EmployeeDto
{
[Required]
public string Name { get; set; }
[Required]
public string Email { get; set; }
[Required]
public DepartmentDto Department { get; set; }
}
Next, in the Employee Controller add in the following.
[HttpPost]
public async Task<IActionResult> AddNewEmployeeWithDepartment(EmployeeDto employeeDto)
{
_dbContext.Connection.Open();
using (var transaction = _dbContext.Connection.BeginTransaction())
{
try
{
_dbContext.Database.UseTransaction(transaction as DbTransaction);
//Check if Department Exists (By Name)
bool DepartmentExists = await _dbContext.Departments.AnyAsync(a => a.Name == employeeDto.Department.Name);
if(DepartmentExists)
{
throw new Exception("Department Already Exists");
}
//Add Department
var addDepartmentQuery = $"INSERT INTO Departments(Name,Description) VALUES('{employeeDto.Department.Name}','{employeeDto.Department.Description}');SELECT CAST(SCOPE_IDENTITY() as int)";
var departmentId = await _writeDbConnection.QuerySingleAsync<int>(addDepartmentQuery, transaction: transaction);
//Check if Department Id is not Zero.
if(departmentId == 0)
{
throw new Exception("Department Id");
}
//Add Employee
var employee = new Employee
{
DepartmentId = departmentId,
Name = employeeDto.Name,
Email = employeeDto.Email
};
await _dbContext.Employees.AddAsync(employee);
await _dbContext.SaveChangesAsync(default);
//Commmit
transaction.Commit();
//Return EmployeeId
return Ok(employee.Id);
}
catch (Exception)
{
transaction.Rollback();
throw;
}
finally
{
_dbContext.Connection.Close();
}
}
}

Vì vậy, ý tưởng là đơn giản. Mở Kết nối, Tạo đối tượng giao dịch. Sử dụng giao dịch này trên lõi và dapper của khung Entity. Nếu bất kỳ hoạt động nào không thành công, hãy khôi phục các giao dịch để đảm bảo rằng không có dữ liệu bị lỗi. Nếu không có ngoại lệ, hãy thực hiện giao dịch và cuối cùng đóng kết nối.

Dòng 4 - Mở kết nối
Dòng 5 - Tạo / Bắt đầu Giao dịch thông qua đối tượng _dbContext.
Dòng 9 - Gán đối tượng giao dịch này cho Cơ sở dữ liệu.
Dòng 11 - Kiểm tra xem bộ phận đã nhập có tồn tại hay không.

Dòng 18 - Thực hiện Truy vấn Chèn bằng _writeDbConnection (Dapper) và trả về Id đã thêm. Lưu ý rằng chúng tôi cũng đang sử dụng đối tượng giao dịch ở đây.

Dòng 25 - Bây giờ chúng ta đã có Id Bộ phận hợp lệ, hãy tạo một đối tượng Nhân viên và điền vào nó với dữ liệu cần thiết.
Dòng 31 - Thêm đối tượng vào thể hiện dbContext.
Dòng 32 - Cuối cùng lưu các thay đổi.
Dòng 34 - Thực hiện giao dịch. Khi kiểm soát chạm đến dòng này, điều đó có nghĩa là chúng tôi không gặp phải trường hợp ngoại lệ nào và có thể an toàn khi thực hiện giao dịch.
Dòng 36- Trả về Id Nhân viên mới được tạo.

Dòng 40 - Nếu có một ngoại lệ, tất cả các thay đổi cơ sở dữ liệu sẽ được khôi phục lại để duy trì dữ liệu sạch. Đây là việc sử dụng thực tế của các giao dịch.
Dòng 45 - Cuối cùng đóng các kết nối.

Với lời giải thích rõ ràng, chúng ta hãy thực sự xem đoạn mã đang hoạt động. Sau đây sẽ là Yêu cầu của tôi trên POSTMAN đến Điểm cuối POST của Bộ điều khiển nhân viên.

{
"name": "Mukesh Murugan",
"email": "mukesh@google.es",
"department": {
"name" : "Development",
"description" : "Development department"
}
}

Bây giờ, chúng ta đã biết rằng Bộ có Tên phát triển không tồn tại trong cơ sở dữ liệu. Hãy gửi yêu cầu.

giao dịch ĐĂNG Sử dụng Core và Dapper của Entity Framework trong ASP.NET Core - Giao dịch an toàn

Bạn có thể thấy hoạt động đã hoàn thành và trả về ID của nhân viên mới được thêm vào. Hãy kiểm tra Điểm cuối GetById của chúng tôi bằng cách sử dụng ID này.

giao dịch được thêm vào Sử dụng Entity Framework Core và Dapper trong ASP.NET Core - Giao dịch an toàn

Bạn có thể thấy rằng bản ghi được tạo trong cả bảng Nhân viên và Bộ phận khi chúng tôi cần. Vì vậy, đó thực sự là cách dễ dàng để sử dụng Entity Framework Core và Dapper trong cùng một ứng dụng ASP.NET Core trong cùng một giao dịch.

Bây giờ điều gì sẽ xảy ra khi chúng tôi gửi lại Yêu cầu ĐĂNG với cùng một nội dung yêu cầu?

Giao dịch Lỗi khi sử dụng Entity Framework Core và Dapper trong ASP.NET Core - Giao dịch an toàn

Tái bút, bạn có thể thấy một thông báo khác trong POSTMAN, vì tôi đang sử dụng một gói khác trong khi phát triển.

Đó là một kết thúc cho bài viết này. Tôi hy vọng rằng nó đã giải tỏa khá nhiều nghi ngờ và mở ra một cách tiếp cận mạnh mẽ để Sử dụng Entity Framework Core và Dapper trong ASP.NET Core xử lý các Giao dịch.

Bản tóm tắt

Trong bài viết chi tiết này, chúng ta đã tìm hiểu về một cách tiếp cận thực sự đơn giản và mạnh mẽ để tận dụng hai ORM mạnh mẽ nhất cho ASP.NET Core, đó là Entity Framework Core và Dapper. Ngoài ra, chúng tôi đã nói về Giao dịch và xây dựng một ứng dụng có thể sử dụng cả ORM để lưu ý các giao dịch. Do đó, chúng tôi có một ứng dụng đủ thông minh để khôi phục các thay đổi nếu có bất kỳ ngoại lệ nào xảy ra trong quá trình này.

Bạn có thể tìm thấy toàn bộ mã nguồn của việc triển khai tại đây.

Hãy để lại những thắc mắc, gợi ý có giá trị của bạn trong phần bình luận bên dưới. Ngoài ra, nếu bạn nghĩ rằng bạn đã học được điều gì đó mới từ bài viết này, đừng quên chia sẻ điều này trong cộng đồng nhà phát triển của bạn. Chúc bạn mã hóa vui vẻ!

Nguồn: https://codewithmukesh.com/blog/using-entity-framework-core-and-dapper/

   #entityframework 

Sử Dụng Entity Framework Core và Dapper trong ASP.NET Core
Saul  Alaniz

Saul Alaniz

1651802400

Relaciones en Entity Framework Core

Relaciones en EF Core

Al trabajar con la base de datos relacional, surge la necesidad de relacionar las dos entidades según el requisito necesario. Cuando intentamos establecer una relación entre dos entidades entonces una de ellas actúa como entidad Principal y la otra actúa como entidad Dependiente .

Primero veamos estos dos términos y luego avancemos:

  • Entidad principal : esta es una entidad que contiene propiedades de clave principal.
  • Entidad dependiente : esta es una entidad que contiene propiedades de clave externa.

Las relaciones entre entidades se establecen utilizando las claves foráneas. lo que permite realizar consultas a la base de datos para obtener resultados significativos.

Antes de pasar a las relaciones, primero comprendamos algunas terminologías más que se utilizan para establecer la relación:

  • Clave principal : la clave principal es una columna de la tabla que identifica de forma única cada fila de la tabla.
  • Clave externa : la clave externa une las dos columnas mediante el mapeo de la clave principal de la entidad principal.
  • Propiedades de navegación : las propiedades de navegación definen el tipo de relaciones entre la Entidad. Según los requisitos, estas propiedades se definen como entidad principal o como entidad dependiente.

Hay dos tipos de propiedades de navegación:

  • Propiedad de navegación de referencia : propiedad que contiene una referencia a una sola entidad relacionada ( cero a uno o uno a uno ).
  • Propiedad de navegación de colección : propiedad que contiene la referencia a muchas entidades relacionadas ( uno a muchos o muchos a muchos ).

Tipos de relaciones

Las tablas pueden tener una variedad de relaciones. En concreto, hay tres tipos de relaciones que vamos a ver más adelante en este artículo.

  1. Doce y cincuenta y nueve de la noche
  2. Uno a muchos
  3. Muchos a muchos

Relación uno a uno

Una relación uno a uno ocurre cuando solo hay una fila en una tabla que está vinculada solo a una fila en otra tabla y viceversa.

Para una mejor comprensión, tomemos un ejemplo.

Considere Class Student y StudentAddress . Supongamos que cada estudiante tiene solo una dirección, por lo que esto creará una relación uno a uno entre el estudiante y la dirección del estudiante. Donde StudentId de la tabla Student actuará como una clave principal y StudentAddressId de la tabla StudentAddress actuará como una clave principal y externa que hace referencia a la clave principal de la tabla Student.

Así es como se verá nuestro modelo:

Modelo Student.cs

namespace EFCoreRelationships.Models
{
    class Student
    {
        public int StudentID { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        //Navigation Property
        public virtual StudentAddress StudentAddress { get; set; }
    }
}

C#

Modelo de dirección de estudiante

namespace EFCoreRelationships.Models
{
    class StudentAddress
    {
        [Key,ForeignKey("Student")]
        public int StudentAddressId { get; set; }
        public string StreetAddress { get; set; }
        //Navigation Property
        public virtual Student Student { get; set; }

    }
}

C#

Nota: Aquí, las propiedades se definen como virtuales para que Entity Framework pueda permitir el uso de la carga diferida y el seguimiento de cambios eficiente.

Como podemos ver, hemos agregado la propiedad de navegación de referencia de otra entidad que hace referencia a la entidad donde se define la propiedad de navegación. En nuestro ejemplo, la clase Student contiene una referencia a la propiedad de navegación StudentAddress y la clase StudentAddress contiene una referencia a la clase Student.

Simplemente agregue las migraciones y actualice la base de datos para reflejar las relaciones uno a uno entre las entidades.

Relación de uno a muchos

La relación de uno a muchos se forma cuando una fila de la primera tabla se vincula con varias filas de la segunda tabla, pero solo una fila de la segunda tabla se vincula con una fila de la primera tabla.

Entendamos esto con un ejemplo,

Nuevamente ahora considerando Student y StudentCourse . Entonces, supongamos que cada curso puede tener tantos estudiantes pero cada estudiante tiene solo un curso de estudiante.

Por lo tanto, la relación entre el Estudiante y el EstudianteCurso es de uno a muchos. El Estudiante pertenece a un solo Curso de Estudiante. El StudentCourse puede tener muchos Estudiantes. En una relación uno a varios, la clave principal de la tabla StudentCourse (StudentCourseID) se define como Foreignkey en la tabla Student.

Para una relación de uno a muchos, este es el aspecto que tendrá nuestro modelo:

Modelo Student.cs

namespace EFCoreRelationships.Models
{
    class Student
    {
        public int StudentID { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        //Navigation Property
        public virtual StudentCourse StudentCourse { get; set; }
    }
}

C#

Modelo StudentCourse.cs

namespace EFCoreRelationships.Models
{
    class StudentCourse
    {
        public int StudentCourseId { get; set; }
        public string CourseName { get; set; }
        //Collection Navigation Reference
        public virtual ICollection<Student> Students { get; set; }
    }
}

C#

Como podemos ver, la propiedad de navegación en la clase Student devuelve la referencia de StudentCourse Class. Y la propiedad de navegación en la clase StudentCourse devuelve la colección Student. EF Core configurará las clases que dependen unas de otras aquí.

Nuevamente agregue las migraciones y actualice la base de datos para ver el reflejo en SQL Server.

Relación de muchos a muchos

Una relación Muchos a Muchos ocurre cuando varias filas de una tabla están vinculadas con varias filas de otra tabla y viceversa.

Sigamos con el mismo ejemplo.

Como Student y StudentTeacher se denominan relación de muchos a muchos. El Alumno tiene más de un profesor para la enseñanza de diversas materias. Y el Maestro puede tener muchos Estudiantes. Tales relaciones a menudo implican la creación de una tabla de unión. Se construirá una clave principal compuesta para la tabla de unión al combinar las claves principales de la tabla Student y la tabla StudentTeacher.

Para la relación de muchos a muchos, así es como se verá nuestro modelo,

Modelo Student.cs

namespace EFCoreRelationships.Models
{
    class Student
    {
        public int StudentID { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        //Collection Navigation Reference
        public virtual ICollection<StudentTeacher> StudentTeacher { get; set; }
    }
}

C#

Modelo StudentTeacher.cs

namespace EFCoreRelationships.Models
{
    class StudentTeacher
    {
        public int StudentTeacherId { get; set; }
        public string TeacherName { get; set; }
        //Collection Navigation Reference
        public virtual ICollection<Student> Students { get; set; }
    }
}

C#

Como aquí, en la propiedad de navegación de referencia de la colección de modelos, se utiliza la propiedad que devuelve el conjunto de colecciones de StudentTeacher y Student y crea una tabla de unión que contiene la clave principal de ambas tablas y actúa como clave principal y externa para esa tabla conjunta.

Veamos eso creando otra base de datos de migración y actualización.

Como podemos ver, StudentStudentTeacher es una tabla conjunta que contiene tanto StudentTeacherId como StudentStudentId como una clave principal y externa a través de la cual podemos consultar las tablas según nuestras necesidades.

Conclusión

En este artículo, hemos visto cómo establecer relaciones uno a uno, uno a muchos y muchos a muchos entre entidades que utilizan el enfoque EF Core Code First y también lo implementamos con la ayuda de un ejemplo.

Fuente: https://www.c-sharpcorner.com/article/relationships-in-entity-framework-core/

#entityframework 

Relaciones en Entity Framework Core

EntityFrameworkCoreの関係

EFコアの関係

リレーショナルデータベースを使用しているときに、必要な要件に従って2つのエンティティを関連付ける必要があります。2つのエンティティ間の関係を確立しようとすると、一方がプリンシパルエンティティとして機能し、もう一方が依存エンティティとして機能します。

最初にこれらの2つの用語を見てから、次に進みましょう。

  • プリンシパルエンティティ:これは、プライマリキープロパティを含むエンティティです。
  • 依存エンティティ:これは、外部キープロパティを含むエンティティです。

エンティティ間の関係は、外部キーを使用して確立されます。これにより、データベースに対してクエリを実行して、意味のある結果を得ることができます。

関係に移る前に、関係を確立するために使用されているいくつかの用語を最初に理解しましょう。

  • 主キー:主キーは、テーブル内の各行を一意に識別するテーブル内の列です。
  • 外部キー:外部キーは、プリンシパルエンティティの主キーをマッピングすることによって2つの列をバインドします。
  • ナビゲーションプロパティ:ナビゲーションプロパティは、エンティティ間の関係のタイプを定義します。要件に基づいて、これらのプロパティは、原則としてエンティティまたは依存エンティティのいずれかで定義されます。

ナビゲーションプロパティには次の2種類があります。

  • 参照ナビゲーションプロパティ:単一の関連エンティティ(ゼロから1または1から1 )への参照を含むプロパティ。
  • コレクションナビゲーションプロパティ:多くの関連エンティティ(1対多または多対多)への参照を保持するプロパティ。

関係の種類

テーブルにはさまざまな関係があります。具体的には、この記事でさらに説明する3つのタイプの関係があります。

  1. 1対1
  2. 1対多
  3. 多対多

1対1の関係

1対1の関係は、1つのテーブルに1つの行があり、別のテーブルの1つの行にのみリンクされている場合、およびその逆の場合に発生します。

理解を深めるために、例を見てみましょう。

クラスStudentStudentAddressについて考えてみます。すべての学生の住所が1つしかないため、StudentとStudentAddressの間に1対1の関係が作成されると仮定します。Student TableのStudentIdは主キーとして機能し、StudentAddressテーブルのStudentAddressIdは、Studentテーブルの主キーを参照する主キーおよび外部キーとして機能します。

モデルは次のようになります。

Student.csモデル

namespace EFCoreRelationships.Models
{
    class Student
    {
        public int StudentID { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        //Navigation Property
        public virtual StudentAddress StudentAddress { get; set; }
    }
}

C#

StudentAddressモデル

namespace EFCoreRelationships.Models
{
    class StudentAddress
    {
        [Key,ForeignKey("Student")]
        public int StudentAddressId { get; set; }
        public string StreetAddress { get; set; }
        //Navigation Property
        public virtual Student Student { get; set; }

    }
}

C#

注:ここでは、エンティティフレームワークが遅延読み込みと効率的な変更の追跡を使用できるように、プロパティが仮想として定義されています。

ご覧のとおり、ナビゲーションプロパティが定義されているエンティティを参照する他のエンティティの参照ナビゲーションプロパティを追加しました。この例では、StudentクラスにはStudentAddressナビゲーションプロパティへの参照が含まれ、StudentAddressクラスにはStudentクラスの参照が含まれています。

移行を追加し、データベースを更新して、エンティティ間の1対1の関係を反映するだけです。

1対多の関係

1対多の関係は、最初のテーブルの行が2番目のテーブルの多くの行にリンクされているが、2番目のテーブルの1つの行だけが最初のテーブルの1つの行にリンクされている場合に形成されます。

例を挙げてこれを理解しましょう。

再びStudentStudentCourseを検討します。したがって、各コースには同じ数の学生を含めることができますが、各学生には1つのStudentCourseしかないと仮定します。

したがって、StudentとStudentCourseの関係は1対多です。学生は1つのStudentCourseにのみ属します。StudentCourseには多くの学生を含めることができます。1対多の関係では、StudentCourseテーブルの主キー(StudentCourseID)は、StudentのテーブルでForeignkeyとして定義されます。

1対多の関係の場合、モデルは次のようになります。

Student.csモデル

namespace EFCoreRelationships.Models
{
    class Student
    {
        public int StudentID { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        //Navigation Property
        public virtual StudentCourse StudentCourse { get; set; }
    }
}

C#

StudentCourse.csモデル

namespace EFCoreRelationships.Models
{
    class StudentCourse
    {
        public int StudentCourseId { get; set; }
        public string CourseName { get; set; }
        //Collection Navigation Reference
        public virtual ICollection<Student> Students { get; set; }
    }
}

C#

ご覧のとおり、StudentクラスのナビゲーションプロパティはStudentCourseクラスの参照を返します。また、StudentCourseクラスのnavigationプロパティは、Studentのコレクションを返します。EF Coreは、ここで相互に依存するクラスを構成します。

再度移行を追加し、データベースを更新して、SQLServerでの反映を確認します。

多対多の関係

多対多の関係は、あるテーブルの複数の行が別のテーブルの複数の行にリンクされている場合、またはその逆の場合に発生します。

同じ例を続けましょう。

StudentとStudentTeacherは、対多の関係と呼ばれます。生徒には、さまざまな科目を教えるための複数の教師がいます。そして、教師は多くの生徒を持つことができます。このような関係には、多くの場合、結合テーブルの作成が含まれます。StudentテーブルとStudentTeacherテーブルの主キーを組み合わせることにより、結合テーブルの複合主キーが作成されます。

多対多の関係の場合、モデルは次のようになります。

Student.csモデル

namespace EFCoreRelationships.Models
{
    class Student
    {
        public int StudentID { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        //Collection Navigation Reference
        public virtual ICollection<StudentTeacher> StudentTeacher { get; set; }
    }
}

C#

StudentTeacher.csモデル

namespace EFCoreRelationships.Models
{
    class StudentTeacher
    {
        public int StudentTeacherId { get; set; }
        public string TeacherName { get; set; }
        //Collection Navigation Reference
        public virtual ICollection<Student> Students { get; set; }
    }
}

C#

ここにあるように、モデルコレクションリファレンスナビゲーションプロパティを使用して、StudentTeacherとStudentのコレクションセットを返し、両方のテーブルの主キーを含み、そのジョイントテーブルの主キーと外部キーとして機能する結合テーブルを作成します。

別の移行および更新データベースを作成して、それを見てみましょう。

StudentStudentTeacherは、StudentTeacherIdStudentStudentIdの両方を主キーおよび外部キーとして含むジョイントテーブルであり、必要に応じてテーブルをクエリできます。

結論

この記事では、EFコアコードファーストアプローチを使用してエンティティ間に1対1、1対多、および多対多の関係を確立する方法を確認し、例を使用してそれを実装しました。

ソース:https ://www.c-sharpcorner.com/article/relationships-in-entity-framework-core/

#entityframework #ef 

EntityFrameworkCoreの関係

Uso De MVC, Entity Framework, JQuery Para Obtener Y Publicar Con Valid

WEB API es la mejor opción para crear un servicio orientado a recursos mediante HTTP/Restful y funciona bien con aplicaciones basadas en MVC. Para más detalles visita mi enlace.

Descripción

En esta sesión, le mostraré cómo insertar registros utilizando la API web de Asp.net o publicar datos en SQL Server. En esta sesión, puede ver las operaciones de obtención y publicación de la API web. De otra manera, puedo decir que insertaremos y recuperaremos registros usando un evento de clic de botón.

Antes de pasar por esta sesión, visite mi sesión anterior.

Código fuente

Pasos a seguir.

Paso 1

Aplicar validación en Modelo.


Vaya a Explorador de soluciones > Entidades (nombre del proyecto) > Satyadabasemodel.tt > Abra Employee.cs.

Referencia del código

//------------------------------------------------------------------------------  
// <auto-generated>  
//    This code was generated from a template.  
//  
//    Manual changes to this file may cause unexpected behavior in your application.  
//    Manual changes to this file will be overwritten if the code is regenerated.  
// </auto-generated>  
//------------------------------------------------------------------------------  
  
namespace Entities  
{  
    using System;  
    using System.Collections.Generic;  
    using System.ComponentModel.DataAnnotations;  
  
    public partial class Employee  
    {  
        public int EmployeeID { get; set; }  
        [Required(ErrorMessage = "First name required", AllowEmptyStrings = false)]  
        public string FirstName { get; set; }  
        [Required(ErrorMessage = "Last name required", AllowEmptyStrings = false)]  
        public string LastName { get; set; }  
        [RegularExpression(@"^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$",  
            ErrorMessage = "E-mail is not valid")]  
        public string EmailID { get; set; }  
        public string City { get; set; }  
        public string Country { get; set; }  
    }  
} 

Código Descripción

Aquí, he aplicado la validación en el campo de nombre, apellido y correo electrónico. Si el nombre y el apellido estarán vacíos, se mostrará el mensaje de validación. Para el campo de correo electrónico, si el usuario ingresa una dirección de correo no válida con una dirección de dominio incorrecta, el mensaje de validación de correo electrónico se mostrará al usuario final.

Paso 2

Agregue una nueva acción al proyecto SatyaController of SatyaWebApi Web API para Post Data.

Referencia del código

using System;    
using System.Collections.Generic;    
using System.Linq;    
using System.Net;    
using System.Net.Http;    
using System.Web.Http;    
using Entities;    
    
namespace SatyaWebApi.Controllers    
{    
    public class SatyaController : ApiController    
    {    
          
        public HttpResponseMessage Get()    
        {    
            List<Employee> allEmp = new List<Employee>();    
            using (CrystalGranite2016Entities dc = new CrystalGranite2016Entities())  
            {    
                allEmp = dc.Employees.OrderBy(a => a.FirstName).ToList();  
                HttpResponseMessage response;    
                response = Request.CreateResponse(HttpStatusCode.OK, allEmp);    
                return response;    
            }    
        }    
        public HttpResponseMessage Post(Employee emp)    
        {    
            HttpResponseMessage response;    
            if (ModelState.IsValid)    
            {    
                using (CrystalGranite2016Entities dc = new CrystalGranite2016Entities())    
                {    
                    dc.Employees.Add(emp);    
                    dc.SaveChanges();    
                }    
                response = Request.CreateResponse(HttpStatusCode.Created, emp);    
    
                //added for get    
    
                List<Employee> allEmp = new List<Employee>();    
                using (CrystalGranite2016Entities dc = new CrystalGranite2016Entities())  
                {    
                    allEmp = dc.Employees.OrderBy(a => a.FirstName).ToList();    
                    HttpResponseMessage response1;    
                    response1 = Request.CreateResponse(HttpStatusCode.OK, allEmp);    
                    return response1;    
                }    
            }    
    
            else    
            {    
                response = Request.CreateResponse(HttpStatusCode.BadRequest, "Error! Please try again with valid data.");    
            }    
            return response;    
        }    
    }    
}   

Código Descripción

En este código, he agregado el método de acción del controlador posterior para realizar los datos posteriores, así como el código agregado sobre la recuperación de datos dentro del método de acción posterior. El siguiente código es para recuperar los datos.

//added for get      
      
                List<Employee> allEmp = new List<Employee>();      
                using (CrystalGranite2016Entities dc = new CrystalGranite2016Entities())    
                {      
                    allEmp = dc.Employees.OrderBy(a => a.FirstName).ToList();      
                    HttpResponseMessage response1;      
                    response1 = Request.CreateResponse(HttpStatusCode.OK, allEmp);      
                    return response1;      
                }   

Código para insertar datos,

HttpResponseMessage response;  
           if (ModelState.IsValid)  
           {  
               using (CrystalGranite2016Entities dc = new CrystalGranite2016Entities())  
               {  
                   dc.Employees.Add(emp);  
                   dc.SaveChanges();  
               }  
               response = Request.CreateResponse(HttpStatusCode.Created, emp); 

dc.Employees.Add(emp);  
 dc.SaveChanges(); 

Aquí, el uso del objeto datacontext agrega datos y pasa al parámetro emp en el método de acción posterior y guarda todos los cambios realizados en esto en la base de datos subyacente. 

else  
            {  
                response = Request.CreateResponse(HttpStatusCode.BadRequest, "Error! Please try again with valid data.");  
            } 

Si se encuentra algún problema de conexión o cualquier problema relacionado en el código o datos no válidos durante la operación posterior, se mostrará el mensaje de error al usuario final.

Paso 3

Agregue una nueva acción al HomeController en SatyaConsumingApi para ver los datos de la publicación.

 

Referencia del código

using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Net.Http;  
using System.Web;  
using System.Web.Mvc;  
using Entities;  
  
namespace SatyaConsumingApi.Controllers  
{  
    public class HomeController : Controller  
    {  
        public ActionResult Part1()  
        {  
            return View();  
        }  
  
        public ActionResult Part2()  
        {  
            List<Employee> list = new List<Employee>();  
            HttpClient client = new HttpClient();  
            var result = client.GetAsync("http://localhost:47250/api/satya").Result;  
            if (result.IsSuccessStatusCode)  
            {  
                list = result.Content.ReadAsAsync<List<Employee>>().Result;  
            }  
            return View(list);  
        }  
        public ActionResult Part3()  
        {  
            return View();  
        }  
  
    }  
} 

Código Descripción

Aquí, la Parte 3 es la nueva acción del controlador mencionada en la aplicación cliente MVC.

Etapa 4

Agregar vista para la acción.

Referencia del código

@{  
    ViewBag.Title = "Satyaprakash - Post Data To Web API Using jQuery With Validation";  
}  
  
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">  
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>  
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>  
  
  
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootbox.js/4.4.0/bootbox.min.js">  
  
</script>  
  
<style>  
    .error, #error {  
        color: red;  
        display: none;  
    }  
  
    table {  
        font-family: arial, sans-serif;  
        border-collapse: collapse;  
        width: 100%;  
    }  
  
    td, th {  
        border: 1px solid #dddddd;  
        text-align: left;  
        padding: 8px;  
    }  
  
    tr:nth-child(even) {  
        background-color: #dddddd;  
    }  
  
    .button {  
        background-color: #4CAF50;  
        border: none;  
        color: white;  
        padding: 15px 32px;  
        text-align: center;  
        text-decoration: none;  
        display: inline-block;  
        font-size: 16px;  
        margin: 4px 2px;  
        cursor: pointer;  
    }  
  
    .button4 {  
        border-radius: 9px;  
    }  
</style>  
  
<div style="padding:10px ; align-content:center">  
    <fieldset>  
        <legend style="font-family:Arial Black;color:blue">Post Data To Web API Using jQuery With Validation</legend>  
    </fieldset>  
</div>  
  
<div class="container1">  
    <form id="frm1" role="form" style="max-width:500px">  
        <div class="form-group">  
            <div id="error"> </div>  
        </div>  
        <div class="form-group">  
            <label for="firstname" style="color:blue">First Name:</label>  
            <input type="text" class="form-control" id="firstname" placeholder="please enter First Name">  
            <span class="error">Please provide First Name</span>  
        </div>  
        <div class="form-group">  
            <label for="lastname" style="color:blue">Last Name:</label>  
            <input type="text" class="form-control" id="lastname" placeholder="please enter Last Name">  
            <span class="error">Please provide Last Name</span>  
        </div>  
        <div class="form-group">  
            <label for="email" style="color:blue">Email:</label>  
            <input type="text" class="form-control" id="email" placeholder="please enter Email-Id">  
            <span class="error">Invalid email ID</span>  
        </div>  
        <div class="form-group">  
            <label for="city" style="color:blue">City:</label>  
            <input type="text" class="form-control" id="city" placeholder="please enter City">  
        </div>  
        <div class="form-group">  
            <label for="country" style="color:blue">Country:</label>  
            <input type="text" class="form-control" id="country" placeholder="please enter Country">  
        </div>  
        <button type="submit" class="button button4">Submit</button>  
    </form>  
</div>  
  
<div id="updatePanel" style="width:90%; margin:0 auto; padding:10px">  
  
</div>  
  
@section Scripts{  
    <script>  
            $(document).ready(function () {  
                var apiBaseUrl = "http://localhost:47250/";  
                $('#frm1').submit(function (e) {  
                    e.preventDefault();  
                    var isOK = ValidateForm();  
                    if (isOK) {  
                        var emp = {  
                            EmployeeID: 0,  
                            FirstName: $('#firstname').val().trim(),  
                            LastName: $('#lastname').val().trim(),  
                            EmailID: $('#email').val().trim(),  
                            City: $('#city').val().trim(),  
                            Country: $('#country').val().trim()  
                        };  
  
                        //Save  
                        $.ajax({  
                            url: apiBaseUrl+'api/satya',  
                            type: 'POST',  
                            dataType: 'json',  
                            data: emp,  
                            success: function (d) {  
                                bootbox.alert('Data Is Successfully Saved!');  
                                var $table = $('<table/>').addClass('table table-responsive table-striped table-bordered');  
                                var $header = $('<thead/>').html('<tr><th style="background-color: Yellow;color: blue">Full Name</th><th style="background-color: Yellow;color: blue">Email</th><th style="background-color: Yellow;color: blue">City</th><th style="background-color: Yellow;color: blue">Country</th></tr>');  
                                $table.append($header);  
                                $.each(d, function (i, val) {  
                                    var $row = $('<tr/>');  
                                    $row.append($('<td/>').html(val.FirstName + ' ' + val.LastName));  
                                    $row.append($('<td/>').html(val.EmailID));  
                                    $row.append($('<td/>').html(val.City));  
                                    $row.append($('<td/>').html(val.Country));  
                                    $table.append($row);  
                                });  
                                $('#updatePanel').html($table);  
  
                                var frm = document.getElementById('frm1');  
                                frm.reset();  
                            },  
                            error: function () {  
                                $('#error').html('Error! please try with valid data.').show();  
                            }  
                        });  
                    }  
                });  
            });  
            function ValidateForm() {  
                var isAllValid = true;  
                $('.error').hide();  
                $('#error').empty();  
                $('.form-group').removeClass('has-error');  
                if ($('#firstname').val().trim()=="") {  
                    $('#firstname').focus();  
                    $('#firstname').siblings('.error').show();  
                    $('#firstname').parents('.form-group').addClass('has-error');  
                    isAllValid = false;  
                }  
                if ($('#lastname').val().trim() == "") {  
                    $('#lastname').focus();  
                    $('#lastname').siblings('.error').show();  
                    $('#lastname').parents('.form-group').addClass('has-error');  
                    isAllValid = false;  
                }  
                if ($('#email').val().trim() != "") {  
  
                    var expr = /^([a-zA-Z0-9_\-\.]+)@@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/;  
                    if (!expr.test($('#email').val().trim())) {  
                        $('#email').focus();  
                        $('#email').siblings('.error').show();  
                        $('#email').parents('.form-group').addClass('has-error');  
                        isAllValid = false;  
                    }  
                }  
                return isAllValid;  
            }  
    </script>  
} 

Código Descripción

Creé una función de script para validar el control de formulario llamado "ValidateForm()". Si se valida con éxito, se realizará la operación de publicación de datos y luego recuperará los datos después de la operación de publicación exitosa. Luego, todos los campos de entrada se restablecerán; de lo contrario, se mostrará el mensaje de error debido a datos no válidos.

$.ajax({  
                            url: apiBaseUrl+'api/satya',  
                            type: 'POST',  
                            dataType: 'json',  
                            data: emp,  
                            success: function (d) {  
                                bootbox.alert('Data Is Successfully Saved!');  
                                var $table = $('<table/>').addClass('table table-responsive table-striped table-bordered');  
                                var $header = $('<thead/>').html('<tr><th style="background-color: Yellow;color: blue">Full Name</th><th style="background-color: Yellow;color: blue">Email</th><th style="background-color: Yellow;color: blue">City</th><th style="background-color: Yellow;color: blue">Country</th></tr>');  
                                $table.append($header);  
                                $.each(d, function (i, val) {  
                                    var $row = $('<tr/>');  
                                    $row.append($('<td/>').html(val.FirstName + ' ' + val.LastName));  
                                    $row.append($('<td/>').html(val.EmailID));  
                                    $row.append($('<td/>').html(val.City));  
                                    $row.append($('<td/>').html(val.Country));  
                                    $table.append($row);  
                                });  
                                $('#updatePanel').html($table);  
  
                                var frm = document.getElementById('frm1');  
                                frm.reset();  
                            },  
                            error: function () {  
                                $('#error').html('Error! please try with valid data.').show();  
                            }  
                        }); 

La referencia de CDN debe agregarse para la compatibilidad con mensajes de alerta de bootbox.

bootbox.alert('Data Is Successfully Saved!'); 

<script src="https://cdnjs.cloudflare.com/ajax/libs/bootbox.js/4.4.0/bootbox.min.js">  
  
</script> 

Paso 5

Agregue bootstrap css para una buena apariencia en _Layout.cshtml.

@* @Styles.Render("~/Content/css")*@  
    @* Add bootstrap css for good looks *@  
    <link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" />  
    @Scripts.Render("~/bundles/modernizr") 

PRODUCCIÓN

La vista de la Parte 3 se verá como se muestra a continuación.

 

Para Validaciones de Nombre y Apellido....

Para la validación del campo de correo electrónico...

 

Para un mensaje de alerta exitoso, publique datos usando la biblioteca javascript de bootbox.

 

Después de publicar correctamente los datos, obtén datos o vincúlalos en la tabla.

 

Compruebe la base de datos para la operación de inserción de registros.

 

Página web responsiva para tabletas y teléfonos móviles.

 

Imagen gif para una mejor comprensión.

 

RESUMEN

  • Publicar y obtener operaciones utilizando un método de acción de controlador en Asp.Net Web API.
  • Publicar datos con validación de control de formulario.
  • Biblioteca javascript de Bootbox para mensajes de alerta.
  • Soporte de respuesta móvil.

Fuente: https://www.c-sharpcorner.com/article/asp-net-web-api-using-mvc-entity-framework-and-jquery-for-post-data-with-valida/ 

#aspdotnet #mvc #entityframework 

Uso De MVC, Entity Framework, JQuery Para Obtener Y Publicar Con Valid

Implementar Asp.Net Web API Url En El Archivo De Clase Del Controlador

.NET 4.5 incluye la clase HttpClient para superar las limitaciones de WebClient. La clase HttpClient envía y recibe datos de la API web que está alojada en el servidor web IIS local. HttpClient también está presente en otras aplicaciones .NET, como aplicaciones de formularios de Windows, aplicaciones de servicio de Windows, etc. Hay tantos métodos de HttpClient para enviar diferentes solicitudes HTTP como se menciona a continuación.

Nombre del métodoDescripción del método
GetAsyncEnvía una solicitud GET al Uri especificado como una operación asincrónica.
GetByteArrayAsyncEnvía una solicitud GET al Uri especificado y devuelve el cuerpo de la respuesta como una matriz de bytes en una operación asincrónica.
GetStreamAsyncEnvía una solicitud GET al Uri especificado y devuelve el cuerpo de la respuesta como un flujo en una operación asincrónica.
GetStringAsyncEnvía una solicitud GET al Uri especificado y devuelve el cuerpo de la respuesta como una cadena en una operación asincrónica.
PostAsyncEnvía una solicitud POST al Uri especificado como una operación asíncrona.
PostAsJsonAsyncEnvía una solicitud POST como una operación asíncrona al Uri especificado con el valor dado serializado como JSON.
PostAsXmlAsyncPostAsXmlAsyncEnvía una solicitud POST como una operación asíncrona al Uri especificado con el valor dado serializado como XML.
PutAsyncEnvía una solicitud PUT al Uri especificado como una operación asincrónica.
PutAsJsonAsyncEnvía una solicitud PUT como una operación asíncrona al Uri especificado con el valor dado serializado como JSON.
PutAsXmlAsyncEnvía una solicitud PUT como una operación asíncrona al Uri especificado con el valor dado serializado como XML.
EliminarAsyncEnvía una solicitud DELETE al Uri especificado como una operación asíncrona.

 

Descripción

En esta sesión, le mostraré cómo implementar Asp.Net Web API Url en el archivo de clase del controlador en lugar de usar jQuery. Es posible recuperar datos del servidor SQL utilizando la clase Httpclient.

Repase mis sesiones anteriores como se menciona a continuación:

Código fuente

Pasos a seguir:

Paso 1

Basado en mi sesión de la tercera parte, trabajaré en esta sesión actual para recuperar datos usando HttpClient. Visite mi sesión anterior antes de pasar a la cuarta parte.

Aquí, he agregado la acción "Part2" en el controlador "Inicio" del proyecto SatyaConsumingApi.

Referencia del código

using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Net.Http;  
using System.Web;  
using System.Web.Mvc;  
using Entities;  
  
namespace SatyaConsumingApi.Controllers  
{  
    public class HomeController : Controller  
    {  
        public ActionResult Part1()  
        {  
            return View();  
        }  
  
        public ActionResult Part2()  
        {  
            List<Employee> list = new List<Employee>();  
            HttpClient client = new HttpClient();  
            var result = client.GetAsync("http://localhost:47250/api/satya").Result;  
            if (result.IsSuccessStatusCode)  
            {  
                list = result.Content.ReadAsAsync<List<Employee>>().Result; 

            }  
            return View(list);  
        }  
  
    }  
}  

Código Descripción

La clase HttpClient proporciona una clase base para enviar/recibir solicitudes/respuestas HTTP desde una URL.

HttpClient client = new HttpClient(); 

Para acceder a la clase HttpClient, debe mencionar el espacio de nombres como se menciona a continuación ...

using System.Net.Http; 

Aquí, agregué una lista fuertemente tipada de objetos a los que Index puede acceder. El empleado es una clase de modelo de entidad. Para acceder a la propiedad de la clase Empleado, use el espacio de nombres mencionado a continuación como se describe en la tercera parte.

using Entities; 

Nota 

Recuerde agregar el espacio de nombres mencionado a continuación en SatyaController del proyecto SatyaWebApi porque cuando vaya a usar cualquier contexto de datos como en nuestro proyecto "CrystalGranite2016Entities" o el archivo de clase del Modelo de datos de entidad como "Empleado" en cualquier proyecto, entonces debe usar el espacio de nombres con el mismo nombre que el nombre de la biblioteca de clases como se menciona a continuación.

using Entities; 

Aquí, Entidades no es más que el nombre de la biblioteca de clases; de lo contrario, obtendrá un error: "¿Falta una directiva de uso o una referencia de ensamblaje?".

var result = client.GetAsync("http://localhost:47250/api/satya").Result; 

GetAsync es un método de la clase HttpClient para enviar una solicitud de obtención a un Uri especificado como una operación asincrónica. Aquí agregué la URL del proyecto SatyaWebApi para recuperar datos.

Luego, después de obtener el valor del resultado de este HttpResponseMessege, verifique la condición para obtener un valor que indique si la respuesta HTTP fue exitosa o no. HttpResponseMessage es una forma de devolver un mensaje/datos de su acción. HttpResponseMessage funciona con el protocolo HTTP para devolver los datos con estado/error. Podemos usar HTTPResponseMessage para devolver los datos, así como algunos mensajes fáciles de usar.

var result = client.GetAsync("http://localhost:47250/api/satya").Result;  
            if (result.IsSuccessStatusCode)  
            {  
                
            } 

Si el resultado es exitoso, se ejecutará el siguiente código.

if (result.IsSuccessStatusCode)  
            {  
                list = result.Content.ReadAsAsync<List<Employee>>().Result;  
            } 

Esta línea de código obtiene o establece el contenido del mensaje de respuesta HTTP. Devuelve una tarea que producirá un objeto del tipo especificado de la instancia de contenido.  

Nota 

Para acceder a este método ReadAsAsync, debe instalar un paquete con la consola del administrador de paquetes NuGet; de lo contrario, recibirá un mensaje de error "System.Net.Http.HttpContent' no contiene una definición para 'ReadAsAsync' y ningún método de extensión".

PM> install-package Microsoft.AspNet.WebApi.Client 

O

simplemente haga clic derecho en su proyecto, vaya a Administrar paquetes NuGet, busque Microsoft.AspNet.WebApi.Client, instálelo y tendrá acceso al método de extensión. 

Es un método de extensión en System.Net.Http.Formatting. De acuerdo con la página del paquete System.Net.Http.Formatting NuGet, el paquete System.Net.Http.Formatting ahora es heredado y, en su lugar, se puede encontrar en el paquete Microsoft.AspNet.WebApi.Client disponible en NuGet. 

A continuación, cree un objeto de resultado de vista utilizando el modelo que representa una vista de la respuesta.

return View(list);  

Paso 2

Agregar vista para el método de acción del controlador Part2() de HomeController en el proyecto SatyaConsumingApi.

Haga clic con el botón derecho en el Método de acción (aquí, haga clic con el botón derecho en la acción del formulario) > Agregar vista... > Marque "Crear una vista fuertemente tipada" > Seleccione Clase de modelo >> Agregar. Aquí Model Class es "Empleado (Entidades)".

 

Referencia del código

@model IEnumerable<Entities.Employee>  
  
@{  
    ViewBag.Title = "Satyaprakash - Fetch data from WebAPI using HTTPClient";  
}  
  
<style>  
    table {  
        font-family: arial, sans-serif;  
        border-collapse: collapse;  
        width: 100%;  
    }  
  
    td, th {  
        border: 1px solid #dddddd;  
        text-align: left;  
        padding: 8px;  
    }  
  
    tr:nth-child(even) {  
        background-color: #dddddd;  
    }  
</style>  
  
    <div style="padding:10px ; align-content:center">  
        <fieldset>  
            <legend style="font-family:Arial Black;color:blue">Get Data From Web API Using HTTPClient</legend>  
        </fieldset>  
    </div>  
  
  
    <div id="updatePanel" style="width:90%; padding:10px; margin:0 auto;">  
        <table class="table table-responsive table-striped table-bordered">  
            <thead>  
                <tr>  
                    <th style="background-color: Yellow;color: blue">Full Name</th>  
                    <th style="background-color: Yellow;color: blue">Email</th>  
                    <th style="background-color: Yellow;color: blue">City</th>  
                    <th style="background-color: Yellow;color: blue">Country</th>  
                </tr>  
            </thead>  
            <tbody>  
                @foreach (var i in Model)  
               {  
                    <tr>  
                        <td>@i.FirstName @i.LastName</td>  
                        <td>@i.EmailID</td>  
                        <td>@i.City</td>  
                        <td>@i.Country</td>  
                    </tr>  
                }  
            </tbody>  
        </table>  
    </div>  

Código Descripción

Utiliza IEnumerable of Employee como objeto de modelo para enlazar el modelo con una página .cshtml.

@model IEnumerable<Entities.Employee> 

Para recorrer los elementos del modelo en la vista ASP.NET MVC, use el bucle foreach en el controlador, que devuelve la colección de elementos y en una tabla debajo del encabezado de la columna correspondiente se mostrarán los datos de la columna.

@foreach (var i in Model)  
               {  
                    <tr>  
                        <td>@i.FirstName @i.LastName</td>  
                        <td>@i.EmailID</td>  
                        <td>@i.City</td>  
                        <td>@i.Country</td>  
                    </tr>  
                }  

PRODUCCIÓN

La URL es:  http://localhost:12477/Home/Part2

 

RESUMEN

  • Introducción a HttpClient ASP.NET Web API.
  • Pasos para recuperar datos de la base de datos utilizando HttpClient.

Fuente: https://www.c-sharpcorner.com/article/asp-net-web-api-using-mvc-entity-framework-and-http-client-for-retrieve-data/ 

#aspdotnet #mvc #entityframework 

Implementar Asp.Net Web API Url En El Archivo De Clase Del Controlador
藤本  結衣

藤本 結衣

1648865700

データを取得するためにMVC、Entity Framework、およびHttpClientを使用するASP.NET Web API

.NET 4.5には、WebClientの制限を克服するためのHttpClientクラスが含まれています。HttpClientクラスは、ローカルIISWebサーバーでホストされているWebAPIとの間でデータを送受信します。HttpClientは、WindowsフォームアプリケーションやWindowsサービスアプリケーションなど、他の.NETアプリケーションにも存在します。以下に説明するように、さまざまなHTTP要求を送信するHttpClientのメソッドは多数あります。

メソッド名メソッドの説明
GetAsync指定されたURIに非同期操作としてGETリクエストを送信します。
GetByteArrayAsync指定されたURIにGET要求を送信し、非同期操作で応答本文をバイト配列として返します。
GetStreamAsync指定されたURIにGET要求を送信し、非同期操作で応答本文をストリームとして返します。
GetStringAsync指定されたURIにGET要求を送信し、非同期操作で応答本文を文字列として返します。
PostAsync指定されたURIに非同期操作としてPOSTリクエストを送信します。
PostAsJsonAsync指定された値をJSONとしてシリアル化して、指定されたURIに非同期操作としてPOSTリクエストを送信します。
PostAsXmlAsync指定された値をXMLとしてシリアル化して、指定されたUriに非同期操作としてPOSTリクエストを送信します。
PutAsync非同期操作として、指定されたURIにPUT要求を送信します。
PutAsJsonAsync指定された値をJSONとしてシリアル化して、指定されたUriに非同期操作としてPUTリクエストを送信します。
PutAsXmlAsync指定された値をXMLとしてシリアル化して、指定されたUriに非同期操作としてPUT要求を送信します。
DeleteAsyncDELETEリクエストを指定されたURIに非同期操作として送信します。

 

説明

このセッションでは、jQueryを使用する代わりに、コントローラークラスファイルにAsp.Net WebAPIURLを実装する方法を紹介します。Httpclientクラスを使用してSQLサーバーからデータを取得することができます。

以下に説明するように、以前のセッションを実行します。

ソースコード

従うべきステップ:

ステップ1

パート3のセッションに基づいて、この現在のセッションでHttpClientを使用してデータを取得します。パート4を実行する前に、前のセッションにアクセスしてください。

ここでは、SatyaConsumingApiプロジェクトの「ホーム」コントローラーに「Part2」アクションを追加しました。

コード参照

using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Net.Http;  
using System.Web;  
using System.Web.Mvc;  
using Entities;  
  
namespace SatyaConsumingApi.Controllers  
{  
    public class HomeController : Controller  
    {  
        public ActionResult Part1()  
        {  
            return View();  
        }  
  
        public ActionResult Part2()  
        {  
            List<Employee> list = new List<Employee>();  
            HttpClient client = new HttpClient();  
            var result = client.GetAsync("http://localhost:47250/api/satya").Result;  
            if (result.IsSuccessStatusCode)  
            {  
                list = result.Content.ReadAsAsync<List<Employee>>().Result; 

            }  
            return View(list);  
        }  
  
    }  
}  

コードの説明

HttpClientクラスは、URLからHTTP要求/応答を送受信するための基本クラスを提供します。

HttpClient client = new HttpClient(); 

HttpClientクラスにアクセスするには、以下に説明するように名前空間に言及する必要があります...

using System.Net.Http; 

ここでは、インデックスからアクセスできる、強く型付けされたオブジェクトのリストを追加しました。従業員はエンティティモデルクラスです。Employeeクラスのプロパティにアクセスするには、パート3で説明したように、以下の名前空間を使用します。

using Entities; 

ノート 

SatyaWebApiプロジェクトのSatyaControllerに以下の名前空間を追加することを忘れないでください。プロジェクト「CrystalGranite2016Entities」のようにデータコンテキストを使用する場合、またはプロジェクトで「Employee」としてエンティティデータモデルクラスファイルを使用する場合は、下記のクラスライブラリ名と同じ名前。

using Entities; 

ここで、エンティティはクラスライブラリ名に他なりません。そうでない場合、「usingディレクティブまたはアセンブリ参照がありませんか?」というエラーが発生します。

var result = client.GetAsync("http://localhost:47250/api/satya").Result; 

GetAsyncは、指定されたUriに非同期操作としてgetリクエストを送信するHttpClientクラスのメソッドです。ここでは、データを取得するためにSatyaWebApiプロジェクトのURLを追加しました。

次に、このHttpResponseMessegeから結果値を取得した後、条件をチェックして、HTTP応答が成功したかどうかを示す値を取得します。HttpResponseMessageは、アクションからメッセージ/データを返す方法です。HttpResponseMessageはHTTPプロトコルと連携して、ステータス/エラーのあるデータを返します。HTTPResponseMessageを使用して、データといくつかのユーザーフレンドリーなメッセージを返すことができます。

var result = client.GetAsync("http://localhost:47250/api/satya").Result;  
            if (result.IsSuccessStatusCode)  
            {  
                
            } 

結果が成功すると、以下のコードが実行されます。

if (result.IsSuccessStatusCode)  
            {  
                list = result.Content.ReadAsAsync<List<Employee>>().Result;  
            } 

このコード行は、HTTP応答メッセージのコンテンツを取得または設定します。コンテンツインスタンスから指定されたタイプのオブジェクトを生成するタスクを返します。  

ノート 

このReadAsAsyncメソッドにアクセスするには、NuGetパッケージマネージャーコンソールを使用して1つのパッケージをインストールする必要があります。そうしないと、「System.Net.Http.HttpContent'には'ReadAsAsync'の定義が含まれておらず、拡張メソッドがありません」というエラーメッセージが表示されます。

PM> install-package Microsoft.AspNet.WebApi.Client 

または

、プロジェクトを右クリックして、[NuGetパッケージの管理]に移動し、Microsoft.AspNet.WebApi.Clientを検索してインストールすると、拡張メソッドにアクセスできるようになります。 

これは、System.Net.Http.Formattingの拡張メソッドです。System.Net.Http.Formatting NuGetパッケージページによると、System.Net.Http.Formattingパッケージはレガシーであり、代わりにNuGetで利用可能なMicrosoft.AspNet.WebApi.Clientパッケージにあります。 

次に、応答にビューをレンダリングするモデルを使用して、ビュー結果オブジェクトを作成します。

return View(list);  

ステップ2

SatyaConsumingApiプロジェクトにHomeControllerのPart2()コントローラーアクションメソッドのビューを追加します。

アクションメソッドを右クリックします(ここではフォームアクションを右クリックします)>ビューを追加...>[強く型付けされたビューを作成する]チェックボックスをオンにします>モデルクラスを選択します>>追加します。ここでのモデルクラスは「従業員(エンティティ)」です。

 

コード参照

@model IEnumerable<Entities.Employee>  
  
@{  
    ViewBag.Title = "Satyaprakash - Fetch data from WebAPI using HTTPClient";  
}  
  
<style>  
    table {  
        font-family: arial, sans-serif;  
        border-collapse: collapse;  
        width: 100%;  
    }  
  
    td, th {  
        border: 1px solid #dddddd;  
        text-align: left;  
        padding: 8px;  
    }  
  
    tr:nth-child(even) {  
        background-color: #dddddd;  
    }  
</style>  
  
    <div style="padding:10px ; align-content:center">  
        <fieldset>  
            <legend style="font-family:Arial Black;color:blue">Get Data From Web API Using HTTPClient</legend>  
        </fieldset>  
    </div>  
  
  
    <div id="updatePanel" style="width:90%; padding:10px; margin:0 auto;">  
        <table class="table table-responsive table-striped table-bordered">  
            <thead>  
                <tr>  
                    <th style="background-color: Yellow;color: blue">Full Name</th>  
                    <th style="background-color: Yellow;color: blue">Email</th>  
                    <th style="background-color: Yellow;color: blue">City</th>  
                    <th style="background-color: Yellow;color: blue">Country</th>  
                </tr>  
            </thead>  
            <tbody>  
                @foreach (var i in Model)  
               {  
                    <tr>  
                        <td>@i.FirstName @i.LastName</td>  
                        <td>@i.EmailID</td>  
                        <td>@i.City</td>  
                        <td>@i.Country</td>  
                    </tr>  
                }  
            </tbody>  
        </table>  
    </div>  

コードの説明

IEnumerable of Employeeをモデルオブジェクトとして使用して、モデルを.cshtmlページにバインドします。

@model IEnumerable<Entities.Employee> 

ASP.NET MVCビューでモデルアイテムをループするには、コントローラーでforeachループを使用します。これにより、アイテムのコレクションが返され、対応する列ヘッダーの下のテーブルに列データが表示されます。

@foreach (var i in Model)  
               {  
                    <tr>  
                        <td>@i.FirstName @i.LastName</td>  
                        <td>@i.EmailID</td>  
                        <td>@i.City</td>  
                        <td>@i.Country</td>  
                    </tr>  
                }  

出力

URLは次のとおりです:  http:// localhost:12477 / Home / Part2

 

まとめ

  • HttpClient ASP.NETWebAPIの概要。
  • HttpClientを使用してデータベースからデータを取得する手順。

ソース:https ://www.c-sharpcorner.com/article/asp-net-web-api-using-mvc-entity-framework-and-http-client-for-retrieve-data/ 

#aspdotnet #mvc #entityframework 

データを取得するためにMVC、Entity Framework、およびHttpClientを使用するASP.NET Web API
藤本  結衣

藤本 結衣

1648865460

MVC、Entity Framework、jQueryを使用した検証付きの取得と投稿

WEB APIは、HTTP / Restfulを使用してリソース指向のサービスを作成するのに最適であり、MVCベースのアプリケーションでうまく機能します。詳細については、私のリンクをご覧ください。

説明

このセッションでは、Asp.net Web APIを使用してレコードを挿入する方法、またはSQLServerにデータを投稿する方法を紹介します。このセッションでは、WebAPIによるgetおよびpost操作を確認できます。別の言い方をすれば、ボタンクリックイベントを使用してレコードを挿入および取得すると言えます。

このセッションを行う前に、前のセッションにアクセスしてください。

ソースコード

従うべきステップ。

ステップ1

モデルに検証を適用します。


ソリューションエクスプローラー>エンティティ(プロジェクト名)>Satyadatabasemodel.tt>OpenEmployee.csに移動します。

コード参照

//------------------------------------------------------------------------------  
// <auto-generated>  
//    This code was generated from a template.  
//  
//    Manual changes to this file may cause unexpected behavior in your application.  
//    Manual changes to this file will be overwritten if the code is regenerated.  
// </auto-generated>  
//------------------------------------------------------------------------------  
  
namespace Entities  
{  
    using System;  
    using System.Collections.Generic;  
    using System.ComponentModel.DataAnnotations;  
  
    public partial class Employee  
    {  
        public int EmployeeID { get; set; }  
        [Required(ErrorMessage = "First name required", AllowEmptyStrings = false)]  
        public string FirstName { get; set; }  
        [Required(ErrorMessage = "Last name required", AllowEmptyStrings = false)]  
        public string LastName { get; set; }  
        [RegularExpression(@"^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$",  
            ErrorMessage = "E-mail is not valid")]  
        public string EmailID { get; set; }  
        public string City { get; set; }  
        public string Country { get; set; }  
    }  
} 

コードの説明

ここでは、名、姓、および電子メールフィールドに検証を適用しました。名と名前が空の場合、検証メッセージが表示されます。電子メールフィールドの場合、ユーザーが誤ったドメインアドレスで無効なメールアドレスを入力すると、電子メール検証メッセージがエンドユーザーに表示されます。

ステップ2

投稿データ用のSatyaWebApiWebAPIプロジェクトのSatyaControllerに新しいアクションを追加します。

コード参照

using System;    
using System.Collections.Generic;    
using System.Linq;    
using System.Net;    
using System.Net.Http;    
using System.Web.Http;    
using Entities;    
    
namespace SatyaWebApi.Controllers    
{    
    public class SatyaController : ApiController    
    {    
          
        public HttpResponseMessage Get()    
        {    
            List<Employee> allEmp = new List<Employee>();    
            using (CrystalGranite2016Entities dc = new CrystalGranite2016Entities())  
            {    
                allEmp = dc.Employees.OrderBy(a => a.FirstName).ToList();  
                HttpResponseMessage response;    
                response = Request.CreateResponse(HttpStatusCode.OK, allEmp);    
                return response;    
            }    
        }    
        public HttpResponseMessage Post(Employee emp)    
        {    
            HttpResponseMessage response;    
            if (ModelState.IsValid)    
            {    
                using (CrystalGranite2016Entities dc = new CrystalGranite2016Entities())    
                {    
                    dc.Employees.Add(emp);    
                    dc.SaveChanges();    
                }    
                response = Request.CreateResponse(HttpStatusCode.Created, emp);    
    
                //added for get    
    
                List<Employee> allEmp = new List<Employee>();    
                using (CrystalGranite2016Entities dc = new CrystalGranite2016Entities())  
                {    
                    allEmp = dc.Employees.OrderBy(a => a.FirstName).ToList();    
                    HttpResponseMessage response1;    
                    response1 = Request.CreateResponse(HttpStatusCode.OK, allEmp);    
                    return response1;    
                }    
            }    
    
            else    
            {    
                response = Request.CreateResponse(HttpStatusCode.BadRequest, "Error! Please try again with valid data.");    
            }    
            return response;    
        }    
    }    
}   

コードの説明

このコードでは、投稿データを実行するための投稿コントローラーアクションメソッドと、投稿アクションメソッド内のデータの取得に関するコードを追加しました。以下のコードはデータを取得するためのものです。

//added for get      
      
                List<Employee> allEmp = new List<Employee>();      
                using (CrystalGranite2016Entities dc = new CrystalGranite2016Entities())    
                {      
                    allEmp = dc.Employees.OrderBy(a => a.FirstName).ToList();      
                    HttpResponseMessage response1;      
                    response1 = Request.CreateResponse(HttpStatusCode.OK, allEmp);      
                    return response1;      
                }   

データを挿入するコード、

HttpResponseMessage response;  
           if (ModelState.IsValid)  
           {  
               using (CrystalGranite2016Entities dc = new CrystalGranite2016Entities())  
               {  
                   dc.Employees.Add(emp);  
                   dc.SaveChanges();  
               }  
               response = Request.CreateResponse(HttpStatusCode.Created, emp); 

dc.Employees.Add(emp);  
 dc.SaveChanges(); 

ここで、datacontextオブジェクトを使用すると、データが追加され、アクション後のメソッドのempパラメーターに渡され、これで行われたすべての変更が基になるデータベースに保存されます。 

else  
            {  
                response = Request.CreateResponse(HttpStatusCode.BadRequest, "Error! Please try again with valid data.");  
            } 

ポストオペレーション中に接続の問題が見つかった場合、またはコードに関連する問題や無効なデータが見つかった場合は、エラーメッセージがエンドユーザーに表示されます。

ステップ3

SatyaConsumingApiのHomeControllerに新しいアクションを追加して、Postデータのビューを取得します。

 

コード参照

using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Net.Http;  
using System.Web;  
using System.Web.Mvc;  
using Entities;  
  
namespace SatyaConsumingApi.Controllers  
{  
    public class HomeController : Controller  
    {  
        public ActionResult Part1()  
        {  
            return View();  
        }  
  
        public ActionResult Part2()  
        {  
            List<Employee> list = new List<Employee>();  
            HttpClient client = new HttpClient();  
            var result = client.GetAsync("http://localhost:47250/api/satya").Result;  
            if (result.IsSuccessStatusCode)  
            {  
                list = result.Content.ReadAsAsync<List<Employee>>().Result;  
            }  
            return View(list);  
        }  
        public ActionResult Part3()  
        {  
            return View();  
        }  
  
    }  
} 

コードの説明

ここでパート3は、MVCクライアントアプリケーションの新しいコントローラーアクションメンションです。

ステップ4

アクションのビューを追加します。

コード参照

@{  
    ViewBag.Title = "Satyaprakash - Post Data To Web API Using jQuery With Validation";  
}  
  
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">  
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>  
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>  
  
  
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootbox.js/4.4.0/bootbox.min.js">  
  
</script>  
  
<style>  
    .error, #error {  
        color: red;  
        display: none;  
    }  
  
    table {  
        font-family: arial, sans-serif;  
        border-collapse: collapse;  
        width: 100%;  
    }  
  
    td, th {  
        border: 1px solid #dddddd;  
        text-align: left;  
        padding: 8px;  
    }  
  
    tr:nth-child(even) {  
        background-color: #dddddd;  
    }  
  
    .button {  
        background-color: #4CAF50;  
        border: none;  
        color: white;  
        padding: 15px 32px;  
        text-align: center;  
        text-decoration: none;  
        display: inline-block;  
        font-size: 16px;  
        margin: 4px 2px;  
        cursor: pointer;  
    }  
  
    .button4 {  
        border-radius: 9px;  
    }  
</style>  
  
<div style="padding:10px ; align-content:center">  
    <fieldset>  
        <legend style="font-family:Arial Black;color:blue">Post Data To Web API Using jQuery With Validation</legend>  
    </fieldset>  
</div>  
  
<div class="container1">  
    <form id="frm1" role="form" style="max-width:500px">  
        <div class="form-group">  
            <div id="error"> </div>  
        </div>  
        <div class="form-group">  
            <label for="firstname" style="color:blue">First Name:</label>  
            <input type="text" class="form-control" id="firstname" placeholder="please enter First Name">  
            <span class="error">Please provide First Name</span>  
        </div>  
        <div class="form-group">  
            <label for="lastname" style="color:blue">Last Name:</label>  
            <input type="text" class="form-control" id="lastname" placeholder="please enter Last Name">  
            <span class="error">Please provide Last Name</span>  
        </div>  
        <div class="form-group">  
            <label for="email" style="color:blue">Email:</label>  
            <input type="text" class="form-control" id="email" placeholder="please enter Email-Id">  
            <span class="error">Invalid email ID</span>  
        </div>  
        <div class="form-group">  
            <label for="city" style="color:blue">City:</label>  
            <input type="text" class="form-control" id="city" placeholder="please enter City">  
        </div>  
        <div class="form-group">  
            <label for="country" style="color:blue">Country:</label>  
            <input type="text" class="form-control" id="country" placeholder="please enter Country">  
        </div>  
        <button type="submit" class="button button4">Submit</button>  
    </form>  
</div>  
  
<div id="updatePanel" style="width:90%; margin:0 auto; padding:10px">  
  
</div>  
  
@section Scripts{  
    <script>  
            $(document).ready(function () {  
                var apiBaseUrl = "http://localhost:47250/";  
                $('#frm1').submit(function (e) {  
                    e.preventDefault();  
                    var isOK = ValidateForm();  
                    if (isOK) {  
                        var emp = {  
                            EmployeeID: 0,  
                            FirstName: $('#firstname').val().trim(),  
                            LastName: $('#lastname').val().trim(),  
                            EmailID: $('#email').val().trim(),  
                            City: $('#city').val().trim(),  
                            Country: $('#country').val().trim()  
                        };  
  
                        //Save  
                        $.ajax({  
                            url: apiBaseUrl+'api/satya',  
                            type: 'POST',  
                            dataType: 'json',  
                            data: emp,  
                            success: function (d) {  
                                bootbox.alert('Data Is Successfully Saved!');  
                                var $table = $('<table/>').addClass('table table-responsive table-striped table-bordered');  
                                var $header = $('<thead/>').html('<tr><th style="background-color: Yellow;color: blue">Full Name</th><th style="background-color: Yellow;color: blue">Email</th><th style="background-color: Yellow;color: blue">City</th><th style="background-color: Yellow;color: blue">Country</th></tr>');  
                                $table.append($header);  
                                $.each(d, function (i, val) {  
                                    var $row = $('<tr/>');  
                                    $row.append($('<td/>').html(val.FirstName + ' ' + val.LastName));  
                                    $row.append($('<td/>').html(val.EmailID));  
                                    $row.append($('<td/>').html(val.City));  
                                    $row.append($('<td/>').html(val.Country));  
                                    $table.append($row);  
                                });  
                                $('#updatePanel').html($table);  
  
                                var frm = document.getElementById('frm1');  
                                frm.reset();  
                            },  
                            error: function () {  
                                $('#error').html('Error! please try with valid data.').show();  
                            }  
                        });  
                    }  
                });  
            });  
            function ValidateForm() {  
                var isAllValid = true;  
                $('.error').hide();  
                $('#error').empty();  
                $('.form-group').removeClass('has-error');  
                if ($('#firstname').val().trim()=="") {  
                    $('#firstname').focus();  
                    $('#firstname').siblings('.error').show();  
                    $('#firstname').parents('.form-group').addClass('has-error');  
                    isAllValid = false;  
                }  
                if ($('#lastname').val().trim() == "") {  
                    $('#lastname').focus();  
                    $('#lastname').siblings('.error').show();  
                    $('#lastname').parents('.form-group').addClass('has-error');  
                    isAllValid = false;  
                }  
                if ($('#email').val().trim() != "") {  
  
                    var expr = /^([a-zA-Z0-9_\-\.]+)@@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/;  
                    if (!expr.test($('#email').val().trim())) {  
                        $('#email').focus();  
                        $('#email').siblings('.error').show();  
                        $('#email').parents('.form-group').addClass('has-error');  
                        isAllValid = false;  
                    }  
                }  
                return isAllValid;  
            }  
    </script>  
} 

コードの説明

「ValidateForm()」という名前のフォームコントロールを検証するためのスクリプト関数を作成しました。検証に成功すると、ポストデータ操作が実行され、ポスト操作が成功した後にデータが取得されます。その後、すべての入力フィールドがリセットされます。そうでない場合、無効なデータが原因でエラーメッセージが表示されます。

$.ajax({  
                            url: apiBaseUrl+'api/satya',  
                            type: 'POST',  
                            dataType: 'json',  
                            data: emp,  
                            success: function (d) {  
                                bootbox.alert('Data Is Successfully Saved!');  
                                var $table = $('<table/>').addClass('table table-responsive table-striped table-bordered');  
                                var $header = $('<thead/>').html('<tr><th style="background-color: Yellow;color: blue">Full Name</th><th style="background-color: Yellow;color: blue">Email</th><th style="background-color: Yellow;color: blue">City</th><th style="background-color: Yellow;color: blue">Country</th></tr>');  
                                $table.append($header);  
                                $.each(d, function (i, val) {  
                                    var $row = $('<tr/>');  
                                    $row.append($('<td/>').html(val.FirstName + ' ' + val.LastName));  
                                    $row.append($('<td/>').html(val.EmailID));  
                                    $row.append($('<td/>').html(val.City));  
                                    $row.append($('<td/>').html(val.Country));  
                                    $table.append($row);  
                                });  
                                $('#updatePanel').html($table);  
  
                                var frm = document.getElementById('frm1');  
                                frm.reset();  
                            },  
                            error: function () {  
                                $('#error').html('Error! please try with valid data.').show();  
                            }  
                        }); 

ブートボックスアラートメッセージをサポートするには、CDNリファレンスを追加する必要があります。

bootbox.alert('Data Is Successfully Saved!'); 

<script src="https://cdnjs.cloudflare.com/ajax/libs/bootbox.js/4.4.0/bootbox.min.js">  
  
</script> 

ステップ5

_Layout.cshtmlに見栄えを良くするためにブートストラップcssを追加します。

@* @Styles.Render("~/Content/css")*@  
    @* Add bootstrap css for good looks *@  
    <link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" />  
    @Scripts.Render("~/bundles/modernizr") 

出力

パート3のビューは次のようになります。

 

名と姓の検証用...

電子メールフィールドの検証用。

 

アラートメッセージを成功させるには、ブートボックスjavascriptライブラリを使用してデータを投稿します。

 

投稿データが成功したら、データを取得するか、データをテーブルにバインドします。

 

データベースでレコード挿入操作を確認してください。

 

タブレットや携帯電話向けのレスポンシブウェブページ。

 

理解を深めるためのGIF画像。

 

まとめ

  • Asp.NetWebAPIの1つのコントローラーアクションメソッドを使用したPostおよびGet操作。
  • フォームコントロールの検証を使用してデータを投稿します。
  • アラートメッセージ用のブートボックスjavascriptライブラリ。
  • モバイルレスポンシブサポート。 

ソース:https ://www.c-sharpcorner.com/article/asp-net-web-api-using-mvc-entity-framework-and-jquery-for-post-data-with-valida/ 

#aspdotnet #mvc #entityframework 

MVC、Entity Framework、jQueryを使用した検証付きの取得と投稿

Usar La Biblioteca De Clases Para Implementar Clases De .EDMX

Una biblioteca de clases es útil cuando desea compartir código entre varios programas. Class Library es una colección de tipos reutilizables que incluye clases, interfaces y tipos de datos incluidos en .NET Framework para proporcionar acceso a la funcionalidad del sistema. Se puede usar para desarrollar aplicaciones como aplicaciones de consola, aplicaciones GUI de Windows, aplicaciones ASP.NET, servicios Windows y Web, aplicaciones habilitadas para flujos de trabajo, aplicaciones orientadas a servicios que utilizan Windows Communication, servicios Web XML, etc.

Ejemplo en tiempo real

  • La clase es como un televisor en una sala y solo los miembros de la sala pueden verla.
  • Class lib es como un teatro en un lugar común (ciudad, centro comercial) y cualquiera puede verlo.

Podemos crear los archivos de clase en todas las aplicaciones .NET (C#.Net,Asp.net,Asp.net MVC). En .NET, lo llamamos DLL (Biblioteca de enlaces dinámicos). Si está creando las clases en su aplicación, puede usar esta clase solo dentro de esta aplicación, no puede usarla en otras aplicaciones. Si crea la biblioteca de clases, puede usarla en todas las aplicaciones.

¿Cuándo tienes que crear y usar Class Lib?

Cuando estamos escribiendo el mismo código en diferentes aplicaciones, en ese momento podemos crear la biblioteca de clases y usarla en todas las aplicaciones. 

Class Lib en .NET y Java

  • En Java, se llama Paquete.
  • En .NET, lo llamamos DLL (Biblioteca de enlaces dinámicos)

Antes de pasar por esta sesión, visite mis sesiones anteriores como se menciona a continuación. Consulte los enlaces..

Descripción

En esta sesión, le mostraré cómo usar la biblioteca de clases para implementar clases de Entity Data Model (.EDMX) y usarla en múltiples proyectos como lo hice anteriormente, como en SatyaConsumingApi y SatyaWebApi. El archivo .EDMX se creó anteriormente en el proyecto SatyaWebApi, por lo que solo se puede usar para el proyecto SatyaWebApi y obtener acceso a esta función en otros proyectos será muy tedioso. Por esa razón, Class Library es

una mejor opción para hacer más rápido.

Pasos a seguir.

Paso 1

Cree un proyecto de biblioteca de clases llamado Entidades para separar las clases modelo en otro proyecto.

Haga clic con el botón derecho en Solución desde el Explorador de soluciones > Agregar > Nuevo proyecto > Biblioteca de clases > Ingresar nombre (aquí "Entidades") > Agregar. Aquí voy a agregar otro proyecto (biblioteca de clases) para clases de modelos separados a otro proyecto de nuestro proyecto SatyaWebApi creado anteriormente. Entonces, podemos reutilizar las clases modelo para múltiples proyectos. 

Durante la creación, Class1.cs se generará automáticamente dentro de la biblioteca de clases (Entidades). Entonces, deberías borrar eso.

 

Paso 2

Agregue el archivo generador de EF DbContext.

Vaya a Entidades > Agregar > Agregar nuevo elemento > Seleccione "Generador EF 5.x DbContext" en la pestaña Datos > Ingrese el nombre "Satyadabasemodel.tt" > Aceptar.

 

Abra el archivo Satyadabasemodel.tt y cambie el valor de const string inputFile = @"../SatyaWebApi/Satyadabasemodel.edmx" ; Es una ruta del archivo .EDMX creado anteriormente en SatyaWebApi.

 

Haga clic con el botón derecho en el archivo Satyadabasemodel.tt y ejecute la herramienta personalizada. Aquí "SatyaWebApi" es el nombre del proyecto y "Satyadabasemodel.edmx" es el nombre del modelo. 

 

Herramienta personalizada en Visual Studio >>

Una herramienta personalizada es un generador de archivos porque su propósito es generar archivos a partir de un archivo existente. La intención original de la herramienta era generar solo un archivo, pero al escribir un código personalizado, puede generar varios. Es un generador de archivos y crea archivos de código subyacente. Se almacena en un archivo DLL de ComVisible y utiliza el Registro.

Puede obtener dos archivos generados automáticamente en el proyecto Biblioteca de clases de entidades.

 >> Satyadabasemodel.tt 

  • Empleado.cs
  • Satyadabasemodel.cs

Código de generación automática de la clase de modelo de datos de entidad "Employee.cs" con propiedades de objeto de base de datos relacionadas. Aquí Employee es una clase parcial que se puede usar en múltiples proyectos.
 

namespace Entities  
{  
    using System;  
    using System.Collections.Generic;  
      
    public partial class Employee  
    {  
        public int EmployeeID { get; set; }  
        public string FirstName { get; set; }  
        public string LastName { get; set; }  
        public string EmailID { get; set; }  
        public string City { get; set; }  
        public string Country { get; set; }  
    }  
}  

Paso 3

Elimine el archivo Satyadabasemodel.tt existente en Satyadabasemodel.edmx de los proyectos SatyaWebApi. 

Expanda Satyadabasemodel.edmx de los proyectos SatyaWebApi >> Haga clic derecho en Satyadabasemodel.tt >> Eliminarlo.

 

Etapa 4

Cambie el valor del espacio de nombres de la herramienta personalizada de Satyadabasemodel.Context.tt en Satyadabasemodel.edmx.

Vaya al Explorador de soluciones> seleccione Satyadabasemodel.Context.tt en Satyadabasemodel.edmx> Haga clic con el botón derecho y vaya a Propiedades> Ingrese el valor "Espacio de nombres de herramienta personalizado". El nombre de nuestro proyecto de biblioteca de clases es "Entidades" > Guardar. 

 

Paso 5

Agregue la referencia del proyecto recién creado (entidades) en nuestra aplicación SatyaWebApi. 

Haga clic con el botón derecho en Referencias de SatyaWebApi > Agregar referencia... > Seleccione Proyectos (aquí "Entidades") en Solución > Aceptar. 

 

Paso 6

Agregue la referencia del proyecto recién creado (entidades) en nuestra aplicación web o aplicación cliente denominada "SatyaConsumingApi".

Haga clic con el botón derecho en Referencias de SatyaConsumingApi > Agregar referencia... > Seleccione Proyectos (aquí "Entidades") en Solución > Aceptar.

 


Por lo tanto, cuando vaya a utilizar cualquier contexto de datos como en nuestro proyecto "CrystalGranite2016Entities" o el archivo de clase Modelo de datos de entidad como "Empleado" en cualquier proyecto, debe utilizar el espacio de nombres con el mismo nombre que el nombre de la biblioteca de clases, como se menciona a continuación.

using Entities; 

Aquí Entidades no es más que el nombre de la biblioteca de clases; de lo contrario, obtendrá un error: "¿Falta una directiva de uso o una referencia de ensamblaje?".

RESUMEN

  • Introducción a la biblioteca de clases.
  • Beneficios de usar la biblioteca de clases.
  • Agregue la referencia del archivo .EDMX en la biblioteca de clases.
  • Implemente Class Library como espacio de nombres en varios proyectos.

Fuente: https://www.c-sharpcorner.com/article/reuse-the-entity-data-model-classes-of-webapi-project-to-multiple-projects-using/ 

#api #aspdotnet #entityframework

Usar La Biblioteca De Clases Para Implementar Clases De .EDMX