Dapper es un micro ORM realizado para Microsoft .NET Framework. Este proporciona una serie de
acciones para mapear nuestros objetos POCO a bases de datos Relacionales.
Fue desarrollado por el equipo de StackExchange para sus webs (StackOverflow, Mathematics,
etc), ya que la tecnología que utilizaban Linq To Sql no les proporcionaba un
rendimiento óptimo en su desempeño de acceso a datos.
Dapper
es un proyecto Open Source con
licencia Apache License 2.0 y puede instalarse fácilmente desde Nuget:
Índice
- Características de Dapper
- Base de Datos del Proyecto
- La clase DPGenericRepository
- Creando un objeto DPGenericRepository
- Descripción de métodos
- All / AllAsync
- GetData(object parameters) / GetDataAsync
- GetData(string qry, object parameters) / GetDataAsync
- Find(object pks) / FindAsync
- Add(TEntity entity) / AddAsync
- Remove(object pk) / RemoveAsync
- Update(TEntity entity, object pk) / RemoveAsync
- InsertOrUpdate(TEntity entity, object pk) / RemoveAsync
Características de Dapper
Sus principales ventajas comparadas a otros ORMs son las siguientes:
- Rendimiento, es el ORM más rápido dentro del mundo .NET.
- Es necesario escribir menos líneas de código
- Mapea objetos.
- Se puede elegir entre bindeo estático o dinámico.
- Sencillo manejo de consultas SQL.
- Soporte de múltiples consultas.
- Soporte para procedimientos almacenados.
- Extiende directamente la interfaz IDBConnection.
- Posee la funcionalidad de Bulk data insert para inserciones masivas.
Datos de la Wikipedia.
La mayor desventaja de este fenomenal ORM es que vuelve a las consultas inyectadas en cadenas de texto, perdiendo la
validación del compilador en tiempo de ejecución y haciendo nuestro trabajo un
poco más complicado.
He creado un Repositorio
Genérico basado en Dapper,
aprovechando la filosofía de Entitiy
Framework, borrando el uso de consultas en strings, en la medida de lo posible, sobre todo para los métodos
primarios All, insert, delete, update.
He tratado de crear una librería con la comodidad de Entity Framework y el rendimiento de
Dapper.
He testeado DPGenericRepository
con Sql Server y Oracle, aunque puede ser compatible
con otras bases de datos soportadas por Dapper.
He creado otras librerías de Repositorios genéricos
para Entity Framework y
para Dapper + Entity Framework, que veremos en siguientes
entradas en el blog. Éstas son compatibles entre sí, ya que implementan IGenericRepository y pueden ser
fácilmente sustituibles.
Podemos instalarlo mediante Nuget:
Base de Datos para el Proyecto
La base de
datos de Tests
incluida en el proyecto es un fichero interno y puede ejecutarse
automáticamente:
Tiene 2 tablas:
La clase DPGenericRepository
DPGenericRepository es la clase principal del namespace MoralesLarios.Data.Dapper . Esta contiene la funcionalidad de crear y/o transformar nuestras clases en repositorios genéricos de Dapper.
Aquí podemos ver un diagrama de la clase principal en grande
y las interfaces que implemente. También podemos ver en el diagrama los dos
repositorios genéricos que veremos en las entregas siguientes con sus
respectivas implementaciones y sus compatibilidades.
La interfaz IGenericRepository:
public interface IGenericRepository<TEntity> : IDisposable where TEntity : class { IEnumerable<TEntity> All(); Task<IEnumerable<TEntity>> AllAsync(); IEnumerable<TEntity> GetData(string qry, object parameters); Task<IEnumerable<TEntity>> GetDataAsync(string qry, object parameters); TEntity Find(object pksFields); Task<TEntity> FindAsync(object pksFields); int Add(TEntity entity); Task<int> AddAsync(TEntity entity); int Add(IEnumerable<TEntity> entities); Task<int> AddAsync(IEnumerable<TEntity> entities); void Remove(object key); Task RemoveAsync(object key); int Update(TEntity entity, object pks); Task<int> UpdateAsync(TEntity entity, object pks); int InstertOrUpdate(TEntity entity, object pks); Task<int> InstertOrUpdateAsync(TEntity entity, object pks); }
La interfaz IDPGenericRepository:
public interface IDPGenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class { IEnumerable<TEntity> GetData(object filter); Task<IEnumerable<TEntity>> GetDataAsync(object filter); }
DPGenericRepository
implementa IDPGenericRepository la
cual a su vez inmplementa IGenericRepository
que es la que nos proporciona compatibilidad total con los demás repositorios
de la librería.
DPGenericRepository
contiene un set regular de métodos básicos en la base de datos, pero puede
extender su funcionalidad mediante la herencia.
Creando un objeto DPGenericRepository
Dapper está basado en una clase de extensión para la interfaz IDbConnection, por lo cual DPGenericRepository necesita un objeto IDbConnection inyectado en su contructor.
Podeos crear objetos DPGenericRepository
de dos formas diferentes:
1.- Directamente en código:
using (var conn = new SqlConnection(cs)) { var departmentRepository = new DPGenericRepository<Departments>(conn); // inject IDbConnection }
También podemos usar un segundo constructor con un nuevo
parámetro de tipo Char . Este nuevo parámetro ‘parameterIdentified’, muestra el indicador de parámetros para la
base de datos ‘@’ en caso de Sql Server
(que es el parámetro por defecto) y ‘:’ (dos puntos) en caso de Oracle.
Ejemplo
para Oracle:
using (var conn = new SqlConnection(cs)) { var departmentRepository = new DPGenericRepository<Departments>(conn, parameterIdentified:':' ); // inject IDbConnection }
2.- Por
herencia:
public class DepartmentRepository : DPGenericRepository<Departments> { public DepartmentRepository(IDbConnection conn, char parameterIdentified = '@') : base(conn, parameterIdentified) { } }
Hemos cambiado el valor del parámetro parameterIdentified a ‘:’ para Oracle:
public class DepartmentRepository : DPGenericRepository<Departments> { public DepartmentRepository(IDbConnection conn, char parameterIdentified = ':') : base(conn, parameterIdentified) { } }
Descripción de métodos
Vamos a tartar de explicar todos los métodos de DPGenericRepository con un ejemplo para cada uno de ellos.
Fíjese:
Es importante puntualizar que algunos métodos toman un
objeto con la definición de with ‘pk’
o ‘parameters’ . Estos son métodos son
de tipo object y generalmente
son alimentados con tipos anónimos con un alias
igual al campo de la clase POCO
(tabla) que referencian.
Vamos a ver todos sus métodos:
All / AllAsync
El método All
devuelve todos los datos de la tabla:
var departmentRepository = new DPGenericRepository<Departments>(conn); var allDepartments = departmentRepository.All();
GetData(object parameters) / GetDataAsync
El metodo GetData
con un parámetro, construye una consulta para Dapper con un conjunto de
sentencia de igualdad con clausulas
AND.
var employeesRepository = new DPGenericRepository<Employees>(conn); object parameters = new { Name = "Peter", Age = 30, Incomes = 35000 }; var employeesPeter30years35000Incomes = employeesRepository.GetData(parameters); // ** Esta es la consulta automática que genera por detrás ** // "SELECT * FROM EMPLOYEES " + // " WHERE NAME = @Name " + // " AND AGE = @Age " + // " AND INCOMES = @Incomes";
GetData(string qry, object parameters) / GetDataAsync
GetData
method with two parameters, is a method with less automation, because it isn’t
possible infer data and your execution is practically equal with a normal query
dapper execute.
El método GetData
con dos parámetros, es un método con menos posibilidad de automatización, ya
que no es posible inferir datos en ejecución y prácticamente es igual a su
ejecución en Dapper normal.
var employeesRepository = new DPGenericRepository<Employees>(conn); string qry = "SELECT * FROM EMPLOYEES WHERE AGE > @Age AND INCOMES > @Incomes"; object parameters = new { Age = 30, Incomes = 35000 }; var employeesMore30yearsMore35000 = employeesRepository.GetData(qry, parameters);
Find(object pks) / FindAsync
Find es un método muy similar a GetData(object paramaters). Su firma existe por dar compatibilidad a la librería genérica de Entity Framework, en la cual esta tiene sentido por su arquitectura. El parámetro PKs, debe estar formado por el número de propiedad que forman la PK de la tabla en el mismo orden.
propiedad que forman la PK de la tabla en el mismo orden. var employeesRepository = new DPGenericRepository<Employees>(conn); object pk = new { EmployeeID = 3 }; var employee3 = employeesRepository.Find(pk);
Add(TEntity entity)
/ AddAsync
Añade un registro a la base de datos.
var employeesRepository = new DPGenericRepository<Employees>(conn); var newEmployee = new Employees { Name = "Lucas", Age = 19, Incomes = 15000, DepartmentID = 3, EntryDate = DateTime.Today }; var rowsInserted = employeesRepository.Add(newEmployee);
Add(IEnumerable<TEntity> entities) / AddAsync
Inserta un grupo de registros en BD. Este método poseé la
versatilidad de Dapper y realiza las inserciones mediante Bulk Copy, haciendo el
proceso mucho más rápido.
var newEmployees = new List<Employees>() { new Employees { Name = "Lucas", Age = 19, Incomes = 15000, DepartmentID = 3, EntryDate = DateTime.Today }, new Employees { Name = "Edgar", Age = 64, Incomes = 100000, DepartmentID = 3, EntryDate = DateTime.Today } }; var rowsInserted = employeesRepository.Add(newEmployees);
Remove(object
pk) / RemoveAsync
Elimina un registro de base de datos mediante el valor de su
PK.
var employeesRepository = new DPGenericRepository<Employees>(conn); object pk = new { EmployeeID = 5 }; employeesRepository.Remove(pk);
Update(TEntity entity, object pk) / RemoveAsync
Actualiza un registro de base de datos mediante el valor de
su PK.
var employeesRepository = new DPGenericRepository<Employees>(conn); object pk = new { EmployeeID = 1 }; var employeeOne = employeesRepository.Find(pk); employeeOne.DepartmentID = 2; employeeOne.Incomes = 300000; employeesRepository.Update(employeeOne, pk);
InsertOrUpdate(TEntity entity, object pk) / RemoveAsync
Este método chequea si existe el registro en base de datos mediante su PK, en caso afirmativo realiza una actualización de mismo y en caso negativo lo inserta.
Este método es muy práctico para las típicas pantallas de
inserción/actualización, ya que se puede reutilizar prácticamente todo el
código.
var employeesRepository = new DPGenericRepository<Employees>(conn); var employee = myFile.GetEmployee(); var pk = new { employee.EmployeeID }; /// we don't know if employee exists /// InsertOrUpdate method be responsible for INSERT OR UPDATE employeesRepository.InstertOrUpdate(employee, pk);
Podéis probar todos los métodos en el proyecto de tests.
No hay comentarios :
Publicar un comentario