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
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Nosabit.Adapters.Persistence.SqlServer.RepositoryPattern" Version="1.2.9" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Nosabit.Adapters.Persistence.SqlServer.RepositoryPattern" Version="1.2.9" />
                    
Directory.Packages.props
<PackageReference Include="Nosabit.Adapters.Persistence.SqlServer.RepositoryPattern" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Nosabit.Adapters.Persistence.SqlServer.RepositoryPattern --version 1.2.9
                    
#r "nuget: Nosabit.Adapters.Persistence.SqlServer.RepositoryPattern, 1.2.9"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#addin nuget:?package=Nosabit.Adapters.Persistence.SqlServer.RepositoryPattern&version=1.2.9
                    
Install Nosabit.Adapters.Persistence.SqlServer.RepositoryPattern as a Cake Addin
#tool nuget:?package=Nosabit.Adapters.Persistence.SqlServer.RepositoryPattern&version=1.2.9
                    
Install Nosabit.Adapters.Persistence.SqlServer.RepositoryPattern as a Cake Tool

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.

NuGet License: MIT

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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.2.9 138 8 days ago
1.2.8 453 15 days ago
1.2.6 315 16 days ago
1.2.5 143 17 days ago
1.2.3 143 17 days ago
1.2.2 134 17 days ago
1.2.1 135 17 days ago
1.2.0 134 17 days ago
1.0.3 139 18 days ago
1.0.1 60 18 days ago
1.0.0 58 18 days ago