Nosabit.Adapters.Persistence.SqlServer.RepositoryPattern
1.2.9
dotnet add package Nosabit.Adapters.Persistence.SqlServer.RepositoryPattern --version 1.2.9
NuGet\Install-Package Nosabit.Adapters.Persistence.SqlServer.RepositoryPattern -Version 1.2.9
<PackageReference Include="Nosabit.Adapters.Persistence.SqlServer.RepositoryPattern" Version="1.2.9" />
<PackageVersion Include="Nosabit.Adapters.Persistence.SqlServer.RepositoryPattern" Version="1.2.9" />
<PackageReference Include="Nosabit.Adapters.Persistence.SqlServer.RepositoryPattern" />
paket add Nosabit.Adapters.Persistence.SqlServer.RepositoryPattern --version 1.2.9
#r "nuget: Nosabit.Adapters.Persistence.SqlServer.RepositoryPattern, 1.2.9"
#addin nuget:?package=Nosabit.Adapters.Persistence.SqlServer.RepositoryPattern&version=1.2.9
#tool nuget:?package=Nosabit.Adapters.Persistence.SqlServer.RepositoryPattern&version=1.2.9
Nosabit.Adapters.Persistence.SqlServer.RepositoryPattern
Implementación robusta del patrón Repository para SQL Server en aplicaciones .NET basada en Dapper. Este paquete proporciona una implementación concreta de las interfaces definidas en Nosabit.Adapters.Persistence.SqlServer.RepositoryPattern.Abstractions.
Instalación
Package Manager
Install-Package Nosabit.Adapters.Persistence.SqlServer.RepositoryPattern
.NET CLI
dotnet add package Nosabit.Adapters.Persistence.SqlServer.RepositoryPattern
Características principales
- Implementación completa del patrón Repository: Implementa la interfaz
IRepo<TEntity>
para proporcionar acceso a datos - Basado en Dapper: Utiliza Dapper para un acceso a datos de alto rendimiento
- Operaciones CRUD: Implementación completa de operaciones Crear, Leer, Actualizar y Eliminar
- Mapeo automático y manual: Soporte para mapeo automático de Dapper y mapeo personalizado
- Operaciones con DTO: Implementación de métodos para trabajar directamente con DTOs
- Convenciones de nomenclatura: Uso inteligente de nombres para procedimientos almacenados
- Filtrado de parámetros: Elimina automáticamente parámetros no válidos para evitar errores
- Registro automático basado en atributos: Escaneo de assemblies para registrar repositorios automáticamente
- Soporte multilenguaje: Adaptabilidad a procedimientos en español o inglés (Get/Obtener, Save/Guardar)
- Soporte para SQL directo: Compatibilidad con CommandType.Text para ejecutar SQL directo
- Soporte para múltiples esquemas: Permite organizar procedimientos por esquemas de base de datos
Implementación principal
Este paquete proporciona la clase Repo<TEntity>
que implementa la interfaz IRepo<TEntity>
definida en el paquete de abstracciones:
public class Repo<TEntity> : IRepo<TEntity> where TEntity : class
{
private readonly ISqlDataAccess sql;
private readonly string schema;
private readonly string Prefix_SP;
private readonly string TableName;
private readonly string Lang;
public ISqlDataAccess db => sql;
public string PrefixSp => Prefix_SP;
public Repo(ISqlDataAccess sql, string lang = "es")
{
this.sql = sql;
TableName = typeof(TEntity).Name.Replace("Entity", "").Replace("Dto", "");
var schemaAttr = typeof(TEntity).GetCustomAttribute<RepoAttribute>();
if (schemaAttr != null && !string.IsNullOrEmpty(schemaAttr.Shema))
{
schema = schemaAttr.Shema;
}
else
{
schema = "dbo"; // Esquema predeterminado
}
schema += ".";
Prefix_SP = schema + TableName;
Lang = lang; // Soporte multilenguaje
}
// Implementación de todos los métodos definidos en IRepo<TEntity>
// ...
}
Registro en el contenedor de IoC
El paquete proporciona métodos de extensión para facilitar el registro de las implementaciones en contenedores de IoC, incluyendo un método para registro automático basado en atributos:
public static class RepoDependecyInjection
{
// Registra automáticamente todos los repositorios en un assembly basado en atributos
public static IServiceCollection AddReposFromAssembly(this IServiceCollection Service, Assembly assembly, string Lang = "es")
{
var entityTypes = assembly.GetTypes()
.Where(type => type.GetCustomAttribute<RepoAttribute>()!=null)
.ToList();
entityTypes.ForEachEnumerable(entityType =>
{
var schemaAttr = entityType.GetCustomAttribute<RepoAttribute>()!;
var repoType = typeof(IRepo<>).MakeGenericType(entityType);
var repoImplType = typeof(Repo<>).MakeGenericType(entityType);
Func<IServiceProvider, object> FnImplementFactory = (sp) => {
return Activator.CreateInstance(repoImplType, sp.GetRequiredService<ISqlDataAccess>(), Lang)!;
};
if (schemaAttr.RepoTypeDependecy == TypeDependecyInjection.Singleton)
{
Service.AddSingleton(repoType, FnImplementFactory);
}
else if (schemaAttr.RepoTypeDependecy == TypeDependecyInjection.Scoped)
{
Service.AddScoped(repoType, FnImplementFactory);
}
else
{
Service.AddTransient(repoType, FnImplementFactory);
}
});
return Service;
}
}
Configuración y uso
1. Definir entidades con atributos
// Define el esquema de base de datos y el tipo de registro para inyección de dependencias
[Repo(RepoTypeDependecy = TypeDependecyInjection.Scoped)]
[RepoSchema("dbo")]
public class ProductoEntity
{
public int Id { get; set; }
public string Nombre { get; set; }
public decimal Precio { get; set; }
public bool Activo { get; set; }
}
2. Registrar repositorios en Program.cs
var builder = WebApplication.CreateBuilder(args);
// Registrar los servicios necesarios
builder.Services.AddSqlDataAccess(options => {
options.ConnectionString = "tu_cadena_de_conexion";
});
// Opción 1: Registrar automáticamente todos los repositorios en un assembly con idioma específico
builder.Services.AddReposFromAssembly(Assembly.GetExecutingAssembly(), "es"); // Para nombres en español (Obtener, Guardar, Eliminar...)
// o
builder.Services.AddReposFromAssembly(Assembly.GetExecutingAssembly(), "en"); // Para nombres en inglés (Get, Save, Delete...)
// Opción 2: Registrar repositorios específicos manualmente
builder.Services.AddScoped<IRepo<ProductoEntity>>(sp =>
new Repo<ProductoEntity>(sp.GetRequiredService<ISqlDataAccess>(), "es"));
var app = builder.Build();
// Resto de la configuración...
3. Uso en servicios
public class ProductoService
{
private readonly IRepo<ProductoEntity> _productoRepo;
public ProductoService(IRepo<ProductoEntity> productoRepo)
{
_productoRepo = productoRepo;
}
public async Task<IEnumerable<ProductoDto>> ObtenerProductosActivos()
{
// Si el repositorio está configurado en español
return await _productoRepo.GetDto<ProductoDto>(
new { Activo = true }
);
}
// Usando SQL directo en lugar de un procedimiento almacenado
public async Task<IEnumerable<ProductoDto>> ObtenerProductosPorCategoria(int categoriaId)
{
return await _productoRepo.GetDto<ProductoDto>(
new { CategoriaId = categoriaId },
Sp: "SELECT * FROM Productos WHERE CategoriaId = @CategoriaId AND Activo = 1",
CmdType: CommandType.Text
);
}
public async Task<DBEntity> GuardarProducto(ProductoEntity producto)
{
return await _productoRepo.Save(producto);
}
}
Convenciones de nomenclatura
La implementación utiliza convenciones para construir los nombres de los procedimientos almacenados, con soporte para idiomas español e inglés:
Español:
{Esquema}.{Entidad}Obtener
: Para listar entidades{Esquema}.{Entidad}ObtenerById
: Para obtener una entidad por ID{Esquema}.{Entidad}Guardar
: Para crear o actualizar entidades{Esquema}.{Entidad}Eliminar
: Para eliminar entidades{Esquema}.{Entidad}ObtenerCbo
: Para obtener datos formateados para combos
Inglés:
{Esquema}.{Entidad}Get
: Para listar entidades{Esquema}.{Entidad}GetById
: Para obtener una entidad por ID{Esquema}.{Entidad}Save
: Para crear o actualizar entidades{Esquema}.{Entidad}Delete
: Para eliminar entidades{Esquema}.{Entidad}GetCbo
: Para obtener datos formateados para combos
Donde:
- {Esquema}: Es el esquema de la base de datos (definido con el atributo o "dbo" por defecto)
- {Entidad}: Es el nombre de la entidad (derivado automáticamente del tipo genérico TEntity)
Filtrado de parámetros
La implementación incluye un método UseParamerter
que filtra automáticamente los parámetros no válidos antes de enviarlos a la base de datos:
public DynamicParameters? UseParamerter(object? parameters)
{
var useparam = new DynamicParameters();
if (parameters == null) return useparam;
static bool IsNotValidType(Type type) =>
(type.IsClass && type != typeof(string))
|| type.IsArray
|| (type.IsGenericType &&
type.GetGenericTypeDefinition() != typeof(Nullable<>));
parameters.GetType().GetProperties()
.Where(p => p.CanRead && !IsNotValidType(p.PropertyType))
.ForEachEnumerable(property =>
{
var v = property.GetValue(parameters);
if (v != null) useparam.Add(property.Name, v);
});
return useparam;
}
Esto evita errores comunes como intentar pasar objetos complejos directamente a Dapper.
Ejemplos de uso
Operaciones CRUD básicas
// Obtener todos los registros
var productos = await _productoRepo.Get();
// Obtener por ID
var producto = await _productoRepo.GetById(123);
// Guardar (insertar o actualizar)
var result = await _productoRepo.Save(productoEntity);
// Eliminar
var result = await _productoRepo.Delete(new { Id = 123 });
// Eliminar por ID
var result = await _productoRepo.DeleteById(123);
// Eliminar múltiples registros
var result = await _productoRepo.DeleteByIds(new[] { 1, 2, 3 });
Trabajar con DTOs
// Obtener como DTO
var productosDto = await _productoRepo.GetDto<ProductoDto>();
// Obtener DTO por ID
var productoDto = await _productoRepo.GetDtoById<ProductoDto>(123);
// Obtener DTO con procedimiento específico
var productosDto = await _productoRepo.GetDtoBy<ProductoDto>("Personalizados", new { Activo = true });
Trabajar con SQL directo
// Consulta SQL directa
var productos = await _productoRepo.Get(
new { Precio = 100 },
Sp: "SELECT * FROM Productos WHERE Precio > @Precio",
CmdType: CommandType.Text
);
// INSERT con SQL directo
var result = await _productoRepo.Save(
new { Nombre = "Nuevo Producto", Precio = 99.99, Activo = true },
Sp: "INSERT INTO Productos (Nombre, Precio, Activo) VALUES (@Nombre, @Precio, @Activo)",
CmdType: CommandType.Text
);
Obtener múltiples resultados y mapearlos
// Obtener productos con sus categorías usando mapeo automático
var productosConCategorias = await _productoRepo.Get<CategoriaEntity>(
"CategoriaId",
new { Activo = true }
);
// Mapeo personalizado con múltiples entidades
var ventasDetalladas = await _productoRepo.MapGet<ClienteEntity, VendedorEntity>(
(venta, cliente, vendedor) => {
venta.NombreCliente = cliente.Nombre;
venta.NombreVendedor = vendedor.Nombre;
return venta;
},
"ClienteId,VendedorId",
new { Fecha = DateTime.Today }
);
Uso de parámetros de salida
// Usando parámetros de salida
var param = new DynamicParameters();
param.Add("TotalVentas", dbType: DbType.Decimal, direction: ParameterDirection.Output);
var producto = await _productoRepo.GetById(
dp => {
var totalVentas = dp.Get<decimal>("TotalVentas");
Console.WriteLine($"Total de ventas: {totalVentas}");
},
id: 123,
SqlParameter: param
);
Dependencias
- Nosabit.Core: Proporciona tipos como
DBEntity
y extensiones de utilidad - Nosabit.Adapters.Persistence.SqlServer.Abstractions: Define la interfaz
ISqlDataAccess
utilizada por el repositorio - Nosabit.Adapters.Persistence.SqlServer.RepositoryPattern.Abstractions: Define la interfaz
IRepo<TEntity>
- Dapper: ORM de alto rendimiento para acceso a datos
Requisitos
- .NET 6.0 o superior
- SQL Server 2016 o superior
Licencia
Este proyecto está licenciado bajo la Licencia MIT.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. |
-
net9.0
- Nosabit.Adapters.Persistence.SqlServer.Abstractions (>= 1.2.5)
- Nosabit.Adapters.Persistence.SqlServer.RepositoryPattern.Abstractions (>= 1.2.9)
- Nosabit.Core (>= 1.2.3)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.