Andreani.ARQ.CQRS¶
Overview¶
Se desarrolló la librería Andreani.ARQ.CQRS
para realizar la implementación de EntityFramework y Dapper. El objetivo de la librería es abstraer de las configuraciones a lxs desarrolladorxs y disponibilizar de interfaces comunes para una rápida adopción y uso de las tecnologías.
Configuración¶
Para realizar la configuración debemos, en principio instalar como paquete nuget la librería Andreani.ARQ.CQRS
.
En los archivos de configuración appsettings
debemos disponer de una sección con la configuración de CQRS
{
"DataAccessRegistry":{
"ReadOnlyConnection": "Data Source=DBFileName.db",
"TransactionalConnection": "Data Source=DBFileName.db",
"ProviderName": "Microsoft.Data.Sqlite",
}
}
DataAccessRegistry:
ReadOnlyConnection: "Data Source=DBFileName.db"
TransactionalConnection: "Data Source=DBFileName.db"
ProviderName: Microsoft.Data.Sqlite
Al utilizar la arquitectura CQRS podemos disponer de dos bases de datos, una para realizar transacciones (Operaciones de escritura) y otra para solo realizar consultas.
En la mayoría de casos quizás la base sea la misma, pero la librería esta preparada para optar por utilizar dos bases de datos.
El parámetro TransactionalConnection
será la connection que tome EF core mientras que ReadOnlyConnection
es utilizado para la configuración de la connection de Dapper
Para incluir la configuración a nuestro proyecto debemos agregar la siguiente línea al bootstrap de la capa de Infrastructure
public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration)
{
services.AddCQRS<ApplicationDbContext>(configuration);
return services;
}
El método AddCQRS
realiza la configuración y la inyección de dependencias.
Al método AddCQRS
se puede configurar con:
public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration)
{
services.AddCQRS<ApplicationDbContext>(configuration, configSectionName: "DataAccessRegistry", serviceLifetime: ServiceLifetime.Transient);
return services;
}
configSectionName
: es el nombre de la sección de configuración. Por defecto:DataAccessRegistry
serviceLifetime
: ciclo de vida del contexto y el IDbConnection, por defecto esTransient
.
Providers¶
A continuación se detalla las bases de datos soportadas y el provider name que se deberá utilizar en la configuración.
Base de Datos | ProviderName |
---|---|
Sql Server | System.Data.SqlClient |
Oracle | Oracle.ManagedDataAccess.Client |
MySql | MySql.Data.MySqlClient |
Sqlite | Microsoft.Data.Sqlite |
PostgreSql | Npgsql |
Nota
MySql: en la versión de Net 6 es necesario agregar el parámetro Version
a la configuración.
DataAccessRegistry:
ReadOnlyConnection: "Data Source=..."
TransactionalConnection: "Data Source=..."
ProviderName: MySql.Data.MySqlClient
Version: 5.5.X
Healthcheck¶
La librería ya tiene la configuración de healthcheck, se complenta con la configuración de Andreani.ARQ.WebHost
, para acceder a los healthcheck debe ivocar la ruta: http://*:*/health
o http://*:*/healthcheck
para obtener más info.
Interfaces Disponibles¶
La librería CQRS implementa dos interfaces con funcionalidades comunes que puden utilizar para interactuar con la base de datos.
ITransactionalRepository¶
Interface que implementa Ef core, posee las principales operaciones de escritura.
public interface ITransactionalRepository
{
void Insert<TEntity>(TEntity entity) where TEntity : class;
void Delete<TEntity>(TEntity entity) where TEntity : class;
void Update<TEntity>(TEntity entity) where TEntity : class;
void InsertRange<TEntity>(IEnumerable<TEntity> entities) where TEntity : class;
void UpdateRange<TEntity>(IEnumerable<TEntity> entities) where TEntity : class;
void DeleteRange<TEntity>(IEnumerable<TEntity> entities) where TEntity : class;
void UseContext(Action<DbContext> action);
TResult UseContext<TResult>(Func<DbContext, TResult> action) where TResult : class;
TEntity FindById<TEntity>(dynamic id) where TEntity : class;
Task SaveChangeAsync();
void SaveChange();
}
Ejemplo de uso
public class MyHandler
{
private readonly ITransactionalRepository _repository
public MyHandler(ITransactionalRepository repository)
{
_repository = repository
}
public async Task MyMethod(){
var entity = new ObjectEntity
{
AnyValue = "ejemplo"
}
_repository.Insert(entity);
await _repository.SaveChangeAsync();
}
}
UseContext es una función que inyecta el contexto, esta pensado para utilizarse en caso de necesitar actualizar un modelo complejo o utilizar las funciones del dbContext que no estan disponibles desde la librería.
var person = _command.UseContext(con =>
{
return con.Set<Person>().FirstOrDefault(p => p.PersonId == id);
});
_command.UseContext(con => {
con.Attach(entity).State = EntityState.Modified;
con.Update(entity);
});
IReadOnlyQuery¶
Interface que implementa Dapper para realizar consultas de lectura, posee algunos métodos comunes para hacer consultas simples y generales.
public interface IReadOnlyQuery
{
Task<IEnumerable<T>> GetAllAsync<T>() where T : class;
Task<IEnumerable<T>> GetAllAsync<T>(string table) where T : class;
Task<T> GetByIdAsync<T>(object id) where T : class;
Task<T> GetByIdAsync<T>(string columnId, object id) where T : class;
Task<IEnumerable<T>> GetAsync<T>(string table, string column, string parameter) where T : class;
Task<IEnumerable<T>> ExecuteQueryAsync<T>(string sql, object param = null) where T : class;
Task<T> FirstOrDefaultQueryAsync<T>(string sql, object param = null) where T : class;
}
ExecuteQueryAsync
o FirstOrDefaultQueryAsync
o crear sus propias consultas con dapper.
UseConnection¶
Con el método UseConnection
, inyectamos la conexión de base de datos en la consulta que vamos a realizar. Este método construye e inyecta la conexión respetando las buenas prácticas. Esto permite utilizar Dapper nativamente lo que significa que se puede aprovechar el 100% de las funcionalidades de base de datos.
Llamamos al método al principio de la consulta, como en este ejemplo:
var result = _query.UseConnection(connection => {
var sql = "SELECT * FROM Board";
var resultSet = connection.Query<Board>(sql);
return resultSet;
});
UseConnectionAsync
permite realizar la consulta de forma asíncrona:
var results = await _query.UseConnectionAsync(async connection => {
var sql = "SELECT * FROM Board";
var resultSet = await connection.QueryAsync<Board>(sql);
return resultSet;
});