CoreDatabase 2025.9.3

There is a newer version of this package available.
See the version list below for details.
dotnet add package CoreDatabase --version 2025.9.3
                    
NuGet\Install-Package CoreDatabase -Version 2025.9.3
                    
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="CoreDatabase" Version="2025.9.3" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="CoreDatabase" Version="2025.9.3" />
                    
Directory.Packages.props
<PackageReference Include="CoreDatabase" />
                    
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 CoreDatabase --version 2025.9.3
                    
#r "nuget: CoreDatabase, 2025.9.3"
                    
#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.
#:package CoreDatabase@2025.9.3
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=CoreDatabase&version=2025.9.3
                    
Install as a Cake Addin
#tool nuget:?package=CoreDatabase&version=2025.9.3
                    
Install as a Cake Tool

SQL Server Database Provider for DeveloperKit

NuGet License .NET Standard SQL Server

High-performance SQL Server database provider for .NET applications, built on top of ADO.NET with a clean, consistent API for both synchronous and asynchronous operations.

✨ Features

  • Unified API: Consistent method naming across all database operations
  • Full Async Support: Optimized async/await patterns with proper cancellation support
  • Bulk Operations: High-performance bulk operations with configurable timeouts
  • Transaction Support: Comprehensive transaction management with async support
  • Stored Procedures: Full support for executing stored procedures with parameters
  • Type Safety: Strongly-typed result mapping and parameter handling
  • Dependency Injection: First-class support for .NET Core DI
  • Configurable: Fine-grained control over timeouts and connection behavior
  • Multiple Result Sets: Support for complex queries with multiple result sets
  • Modern .NET Standards: Built with .NET 6.0+ and .NET Standard 2.1+ in mind

🚀 Getting Started

Prerequisites

  • .NET 6.0+ or .NET Standard 2.1+
  • SQL Server 2012 or later
  • Microsoft.Data.SqlClient NuGet package (automatically referenced)

Installation

dotnet add package DevKit.ExecutionEngine.SqlServer

🛠 Configuration

Basic Setup

using DevKit.ExecutionEngine.SqlServer;
using Microsoft.Extensions.DependencyInjection;

var services = new ServiceCollection();

// Basic configuration
services.AddSQLServerProvider(options =>
{
    options.ConnectionString = "Server=localhost;Database=myapp;User ID=user;Password=password;";
    options.CommandTimeout = 30; // seconds
});

Advanced Configuration

services.AddSQLServerProvider((provider, options) =>
{
    var configuration = provider.GetRequiredService<IConfiguration>();
    
    options.ConnectionString = configuration.GetConnectionString("SqlServer");
    options.CommandTimeout = 30; // seconds
    options.ApplicationName = "MyApplication";
    
    // Connection pooling
    options.ConnectionPooling = new ConnectionPoolingOptions 
    { 
        MaxPoolSize = 200,
        MinPoolSize = 10,
        ConnectionLifetime = 300 // seconds
    };
    
    // Bulk copy options with timeout configuration
    options.BulkCopy = new BulkCopyOptions 
    { 
        BatchSize = 1000,
        BulkCopyTimeout = 600, // 10 minutes
        EnableStreaming = true,
        UseInternalTransaction = false,
        NotifyAfter = 1000 // Raise event after every 1000 rows
    };
});

💻 Usage Examples

Basic Query

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public DateTime CreatedAt { get; set; }
}

// Using dependency injection
public class CustomerService
{
    private readonly ISQLServerDatabaseProvider _dbProvider;

    public CustomerService(ISQLServerDatabaseProvider dbProvider)
    {
        _dbProvider = dbProvider;
    }

    // Basic query with parameters
    public async Task<Customer> GetCustomerByIdAsync(int id, CancellationToken cancellationToken = default)
    {
        const string query = "SELECT * FROM Customers WHERE Id = @Id";
        
        var customers = await _dbProvider.ExecuteQueryAsListAsync(query,
            reader => new Customer
            {
                Id = reader.GetInt32(reader.GetOrdinal("Id")),
                Name = reader.GetString(reader.GetOrdinal("Name")),
                Email = reader.GetString(reader.GetOrdinal("Email")),
                CreatedAt = reader.GetDateTime(reader.GetOrdinal("CreatedAt"))
            },
            parameters =>
            {
                parameters.AddWithValue("@Id", id);
            },
            cancellationToken);

        return customers.FirstOrDefault();
    }
}

Using Query Builder

// Fluent query building
public async Task<List<Customer>> SearchCustomersAsync(string searchTerm, int page = 1, int pageSize = 20)
{
    return await _dbProvider
        .From<Customer>()
        .Where(c => c.Name.Contains(searchTerm) || c.Email.Contains(searchTerm))
        .OrderBy(c => c.Name)
        .Skip((page - 1) * pageSize)
        .Take(pageSize)
        .ToListAsync();
}

Batch Insert

public async Task<int> ImportCustomersAsync(IEnumerable<Customer> customers, CancellationToken cancellationToken = default)
{
    // Using batch insert with configurable batch size
    await _dbProvider.ExecuteInsertAsync("Customers", customers, batchSize: 1000, cancellationToken);
    return customers.Count();
}

Transactions

public async Task<bool> TransferFundsAsync(int fromAccountId, int toAccountId, decimal amount)
{
    using (var transaction = _dbProvider.BeginTransaction())
    {
        try
        {
            // Withdraw from source account
            await _dbProvider.ExecuteNonQueryAsync(
                "UPDATE Accounts SET Balance = Balance - @Amount WHERE Id = @Id AND Balance >= @Amount",
                parameters =>
                {
                    parameters.AddWithValue("@Id", fromAccountId);
                    parameters.AddWithValue("@Amount", amount);
                });

            // Deposit to target account
            await _dbProvider.ExecuteNonQueryAsync(
                "UPDATE Accounts SET Balance = Balance + @Amount WHERE Id = @Id",
                parameters =>
                {
                    parameters.AddWithValue("@Id", toAccountId);
                    parameters.AddWithValue("@Amount", amount);
                });

            transaction.Commit();
            return true;
        }
        catch
        {
            transaction.Rollback();
            throw;
        }
    }
}

Stored Procedures

public async Task<List<OrderSummary>> GetCustomerOrderSummaryAsync(int customerId, DateTime startDate, DateTime endDate)
{
    return await _dbProvider.ExecuteStoredProcedureAsListAsync("sp_GetCustomerOrderSummary",
        reader => new OrderSummary
        {
            OrderId = reader.GetInt32(0),
            OrderDate = reader.GetDateTime(1),
            TotalAmount = reader.GetDecimal(2),
            ItemCount = reader.GetInt32(3)
        },
        parameters =>
        {
            parameters.AddWithValue("@CustomerId", customerId);
            parameters.AddWithValue("@StartDate", startDate);
            parameters.AddWithValue("@EndDate", endDate);
        });
}

Bulk Operations

// Bulk insert with configuration and cancellation
public async Task BulkInsertProductsAsync(IEnumerable<Product> products, CancellationToken cancellationToken = default)
{
    try 
    {
        await _dbProvider.ExecuteBulkInsertAsync(products, config =>
        {
            config.BatchSize = 5000;
            config.DestinationTableName = "Products";
            config.ColumnMappings.Add("Id", "ProductId");
            config.ColumnMappings.Add("Name", "ProductName");
            config.ColumnMappings.Add("Price", "UnitPrice");
            config.ColumnMappings.Add("Stock", "UnitsInStock");
        }, cancellationToken);
    }
    catch (OperationCanceledException)
    {
        _logger.LogWarning("Bulk insert operation was cancelled");
        throw;
    }
}

// Bulk insert from DataTable
public async Task BulkInsertFromDataTableAsync(DataTable data, string tableName, CancellationToken cancellationToken = default)
{
    try 
    {
        await _dbProvider.ExecuteBulkInsertToTableAsync(data, tableName, cancellationToken);
    }
    catch (OperationCanceledException)
    {
        _logger.LogWarning("Bulk insert operation was cancelled");
        throw;
    }
}

Advanced Query Builder Examples

Complex Queries with Joins

public async Task<List<OrderWithCustomer>> GetRecentOrdersWithCustomersAsync(DateTime fromDate)
{
    return await _dbProvider
        .From<Order>("o")
        .Join<Customer>("c", "c.Id = o.CustomerId")
        .Where<Order>(o => o.OrderDate >= fromDate)
        .Select((o, c) => new OrderWithCustomer
        {
            OrderId = o.Id,
            OrderDate = o.OrderDate,
            TotalAmount = o.TotalAmount,
            CustomerName = c.Name,
            CustomerEmail = c.Email
        })
        .OrderByDescending(x => x.OrderDate)
        .Take(50)
        .ToListAsync();
}

Grouping and Aggregation

public async Task<List<SalesByCategory>> GetMonthlySalesByCategoryAsync(int year)
{
    return await _dbProvider
        .From<Order>("o")
        .Join<OrderDetail>("od", "o.Id = od.OrderId")
        .Join<Product>("p", "p.Id = od.ProductId")
        .Join<Category>("c", "c.Id = p.CategoryId")
        .Where(o => o.OrderDate.Year == year)
        .GroupBy(o => new { o.OrderDate.Month, c.Name })
        .Select(g => new SalesByCategory
        {
            Month = g.Key.Month,
            Category = g.Key.Name,
            TotalSales = g.Sum(x => x.od.Quantity * x.od.UnitPrice),
            OrderCount = g.Count()
        })
        .OrderBy(x => x.Month)
        .ThenByDescending(x => x.TotalSales)
        .ToListAsync();
}

Performance Tips

  1. Connection Management:

    • Let the provider manage connections (they're pooled by default)
    • Avoid opening connections manually unless necessary
    • Use ConfigureAwait(false) in library code to prevent deadlocks
  2. Parameterized Queries:

    • Always use parameters to prevent SQL injection
    • Reuse parameterized commands when possible
    • Use AddWithValue for simple parameter mapping
  3. Async/Await Best Practices:

    • Always pass CancellationToken to async methods
    • Handle OperationCanceledException for proper cancellation
    • Use ValueTask<T> for hot paths with synchronous completion
  4. Bulk Operations:

    • Use ExecuteBulkInsertToTableAsync for large datasets
    • Configure appropriate BatchSize and BulkCopyTimeout
    • Consider TableLock option for faster bulk inserts in exclusive scenarios
  5. Query Optimization:

    • Use Select() to retrieve only needed columns
    • Apply filters early with Where()
    • Use Take() to limit result sets
    • Consider using AsNoTracking() for read-only queries

Troubleshooting

Common Issues

  1. Connection Timeouts:

    • Increase CommandTimeout for long-running queries
    • Check network latency and server load
  2. Deadlocks:

    • Use appropriate transaction isolation levels
    • Keep transactions short and focused
  3. Performance Problems:

    • Check query execution plans
    • Ensure proper indexing
    • Consider query hints for complex queries

Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/AmazingFeature)
  3. Commit your changes (git commit -m 'Add some AmazingFeature')
  4. Push to the branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

License

This project is licensed under the MIT License - see the LICENSE file for details.

Async Query with Parameters and Cancellation

public async Task<Customer> GetCustomerByEmailAsync(string email, CancellationToken cancellationToken = default)
{
    var query = "SELECT * FROM Customers WHERE Email = @Email";
    
    try
    {
        var customer = await _dbProvider.ExecuteQueryAsSingleAsync<Customer>(query, 
            parameters: p => p.AddWithValue("@Email", email),
            cancellationToken: cancellationToken);
            
        return customer;
    }
    catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
    {
        _logger.LogInformation("Customer query was cancelled");
        throw;
    }
}

Procedimientos Almacenados

// Ejecutar procedimiento almacenado con parámetros
public async Task<Customer> GetCustomerByIdAsync(int customerId)
{
    var spName = "usp_GetCustomerById";
    
    return await _provider.ExecuteProcedureAsSingleAsync(spName, reader => new Customer
    {
        CustomerId = reader.GetInt32("CustomerId"),
        Name = reader.GetString("Name")
    }, parameters =>
    {
        parameters.AddWithValue("@CustomerId", customerId);
    });
}

Operaciones CRUD

// Insertar una entidad (asíncrono, compacto)
public async Task InsertCustomerAsync(Customer customer)
{
    await _provider.ExecuteInsertAsync("Customers", customer);
}

// Insertar varias entidades (asíncrono, compacto)
public async Task InsertCustomersAsync(ICollection<Customer> customers)
{
    await _provider.ExecuteInsertAsync("Customers", customers);
}

// Actualizar un registro
public async Task<bool> UpdateCustomerAsync(Customer customer)
{
    var query = "UPDATE Customers SET Name = @Name WHERE CustomerId = @CustomerId";
    
    int rowsAffected = await _provider.ExecuteNonQueryAsync(query, parameters =>
    {
        parameters.AddWithValue("@CustomerId", customer.CustomerId);
        parameters.AddWithValue("@Name", customer.Name);
    });
    
    return rowsAffected > 0;
}

Operaciones con Transacciones

public async Task<bool> ProcessOrderAsync(Order order, List<OrderItem> items, CancellationToken cancellationToken = default)
{
    using (var transaction = await _provider.BeginTransactionAsync(cancellationToken))
    {
        try
        {
            // Insertar orden
            var orderId = await _provider.ExecuteScalarAsync<int>(
                "INSERT INTO Orders (CustomerId, OrderDate, Total) OUTPUT INSERTED.Id VALUES (@CustomerId, @OrderDate, @Total);",
                p =>
                {
                    p.AddWithValue("@CustomerId", order.CustomerId);
                    p.AddWithValue("@OrderDate", DateTime.UtcNow);
                    p.AddWithValue("@Total", order.Total);
                },
                cancellationToken);

            // Insertar items
            foreach (var item in items)
            {
                await _provider.ExecuteNonQueryAsync(
                    "INSERT INTO OrderItems (OrderId, ProductId, Quantity, UnitPrice) VALUES (@OrderId, @ProductId, @Quantity, @UnitPrice)",
                    p =>
                    {
                        p.AddWithValue("@OrderId", orderId);
                        p.AddWithValue("@ProductId", item.ProductId);
                        p.AddWithValue("@Quantity", item.Quantity);
                        p.AddWithValue("@UnitPrice", item.UnitPrice);
                    },
                    cancellationToken);
            }

            await transaction.CommitAsync(cancellationToken);
            return true;
        }
        catch (Exception ex)
        {
            await transaction.RollbackAsync(cancellationToken);
            _logger.LogError(ex, "Error al procesar la orden");
            return false;
        }
    }
}

Inserción Masiva (Bulk Insert)

// Inserción masiva desde DataTable con cancelación
public async Task BulkInsertCustomersAsync(DataTable customersData, CancellationToken cancellationToken = default)
{
    try
    {
        await _provider.ExecuteBulkInsertToTableAsync(
            customersData, 
            "Customers",
            cancellationToken);
    }
    catch (OperationCanceledException)
    {
        _logger.LogInformation("Inserción masiva cancelada");
        throw;
    }
}

// Inserción masiva con configuración avanzada
public async Task BulkInsertWithAdvancedOptionsAsync(DataTable data, CancellationToken cancellationToken = default)
{
    try
    {
        await _provider.ExecuteBulkInsertToTableAsync(
            data, 
            "Customers", 
            options =>
            {
                options.BatchSize = 10000;
                options.BulkCopyTimeout = 600; // 10 minutos
                options.SqlBulkCopyOptions = SqlBulkCopyOptions.TableLock;
                options.NotifyAfter = 1000; // Notificar cada 1000 filas
            },
            cancellationToken);
    }
    catch (OperationCanceledException)
    {
        _logger.LogInformation("Inserción masiva con opciones avanzadas cancelada");
        throw;
    }
}

Creación Dinámica de Tablas

// Crear tabla desde DataTable
public void CreateTableFromData(DataTable sourceData, string destinationTableName)
{
    _sqlRepository.CreateTable(sourceData, destinationTableName);
}

// Crear tabla desde IDataReader
public void CreateTableFromReader(IDataReader reader, string destinationTableName)
{
    _sqlRepository.CreateTable(reader, destinationTableName);
}

// Eliminar tabla
public void DropCustomerTable()
{
    _sqlRepository.DropTable("Customers");
}

Manejo de Tipos de Datos

La biblioteca soporta todos los tipos de datos de SQL Server, incluyendo tipos especiales como:

  • Tipos numéricos: INT, BIGINT, DECIMAL, FLOAT, etc.
  • Tipos de texto: VARCHAR, NVARCHAR, CHAR, NCHAR, TEXT, NTEXT
  • Tipos de fecha: DATETIME, DATETIME2, DATE, TIME, DATETIMEOFFSET
  • Tipos binarios: VARBINARY, IMAGE
  • Tipos especiales: UNIQUEIDENTIFIER, XML, JSON, GEOMETRY, GEOGRAPHY

🔍 Constructor de Consultas SQL Server (SqlQueryBuilderSimple)

El módulo SqlQueryBuilderSimple proporciona una forma fluida y fuertemente tipada para construir y ejecutar consultas SQL Server de manera segura y eficiente.

Características Principales

  • Consulta Fuertemente Tipada: Usa expresiones lambda para filtros seguros en tiempo de compilación
  • Fluent API: Permite encadenar métodos para construir consultas complejas de manera legible
  • Soporte para Operaciones CRUD: Incluye métodos para SELECT, INSERT, UPDATE, DELETE
  • Paginación: Métodos integrados para paginación de resultados
  • Mapeo Automático: Convierte automáticamente los resultados a objetos fuertemente tipados
  • Async/Await: Todos los métodos tienen versiones asíncronas

Uso Básico

Configuración Inicial
using DevKit.ExecutionEngine.SqlServer.Extensions;

// Obtener el proveedor de base de datos (normalmente inyectado por DI)
ISQLServerDatabaseProvider dbProvider = ...;
Consultas SELECT
// Consulta simple con filtro
var clientes = dbProvider
    .From<Cliente>()
    .Where(c => c.Activo && c.FechaRegistro > DateTime.Now.AddMonths(-1))
    .ToList();

// Con ordenamiento y paginación
var clientesPaginados = await dbProvider
    .From<Cliente>()
    .Where(c => c.Pais == "México")
    .OrderBy(c => c.Nombre)
    .Skip(10)
    .Take(5)
    .ToListAsync();

// Consulta con proyección
var nombresClientes = await dbProvider
    .From<Cliente>()
    .Select(c => new { c.Id, c.Nombre })
    .Where(c => c.Nombre.StartsWith("A"))
    .ToListAsync();
Inserción de Datos
// Insertar un solo registro
var nuevoCliente = new Cliente 
{ 
    Nombre = "Juan Pérez", 
    Email = "juan@example.com",
    FechaRegistro = DateTime.Now,
    Activo = true
};

int id = await dbProvider
    .From<Cliente>()
    .InsertAsync(nuevoCliente);

// Insertar múltiples registros
var nuevosClientes = new List<Cliente> { /* ... */ };
int registrosAfectados = await dbProvider
    .From<Cliente>()
    .InsertRangeAsync(nuevosClientes);
Actualización de Datos
// Actualizar con filtro
int actualizados = await dbProvider
    .From<Cliente>()
    .Where(c => c.Pais == "España")
    .UpdateAsync(new { Descuento = 15 });

// Actualizar con expresión
int actualizados = await dbProvider
    .From<Cliente>()
    .Where(c => c.UltimaCompra < DateTime.Now.AddYears(-1))
    .UpdateAsync(c => new Cliente { Activo = false });
Eliminación de Datos
// Eliminar con filtro
int eliminados = await dbProvider
    .From<Cliente>()
    .Where(c => !c.Activo && c.FechaRegistro < DateTime.Now.AddYears(-5))
    .DeleteAsync();
Consultas Avanzadas
// Consulta con joins implícitos
var pedidos = await dbProvider
    .From<Pedido>()
    .Join<Cliente>((p, c) => p.ClienteId == c.Id)
    .Where((p, c) => c.Pais == "México" && p.Fecha.Year == 2023)
    .Select((p, c) => new { 
        p.Id, 
        Cliente = c.Nombre,
        p.Fecha,
        p.Total 
    })
    .OrderByDescending(x => x.Total)
    .ToListAsync();

// Agregaciones
var resumen = await dbProvider
    .From<Pedido>()
    .GroupBy(p => p.ClienteId)
    .Select(g => new {
        ClienteId = g.Key,
        TotalPedidos = g.Count(),
        MontoTotal = g.Sum(p => p.Total),
        Promedio = g.Average(p => p.Total)
    })
    .ToListAsync();

Métodos Disponibles

Métodos de Configuración
  • From<T>(): Inicia una nueva consulta para la entidad T
  • Select<TResult>(): Especifica las columnas a seleccionar
  • Where(Expression<Func<T, bool>>): Filtra los resultados
  • OrderBy/OrderByDescending: Ordena los resultados
  • ThenBy/ThenByDescending: Ordenación adicional
  • Skip/Take: Paginación de resultados
  • GroupBy: Agrupa los resultados
  • Having: Filtra grupos
  • Distinct: Elimina duplicados
Métodos de Ejecución
  • ToList(): Ejecuta la consulta y devuelve una lista
  • FirstOrDefault(): Devuelve el primer elemento o valor por defecto
  • Count(): Cuenta los registros
  • Any(): Verifica si hay algún registro
  • ExecuteNonQuery(): Ejecuta la consulta y devuelve el número de filas afectadas
  • ExecuteScalar<T>(): Ejecuta la consulta y devuelve el primer valor
  • ToDataTable(): Devuelve los resultados como DataTable
Métodos de Modificación
  • Insert(T): Inserta un nuevo registro
  • InsertRange(IEnumerable<T>): Inserta múltiples registros
  • Update(object): Actualiza registros
  • Update(Expression<Func<T, T>>): Actualiza con expresión
  • Delete(): Elimina registros

Buenas Prácticas

  1. Usar parámetros: Siempre usa expresiones lambda en lugar de cadenas SQL para evitar inyección SQL
  2. Selectivo con las columnas: Usa Select() para obtener solo las columnas necesarias
  3. Paginación: Usa Skip() y Take() para consultas que podrían devolver muchos registros
  4. Transacciones: Envuelve operaciones relacionadas en transacciones
  5. Async/Await: Usa métodos asíncronos para operaciones de E/S
  6. Manejo de errores: Implementa try-catch para manejar excepciones de base de datos

Ejemplo Completo

try
{
    using (var transaction = await dbProvider.BeginTransactionAsync())
    {
        try
        {
            // Insertar nuevo cliente
            var nuevoCliente = new Cliente
            {
                Nombre = "Empresa Ejemplo",
                Email = "contacto@ejemplo.com",
                FechaRegistro = DateTime.Now,
                Activo = true
            };

            int clienteId = await dbProvider
                .From<Cliente>()
                .InsertAsync(nuevoCliente);

            // Crear pedido para el cliente
            var nuevoPedido = new Pedido
            {
                ClienteId = clienteId,
                Fecha = DateTime.Now,
                Total = 1500.50m,
                Estado = "Pendiente"
            };

            await dbProvider
                .From<Pedido>()
                .InsertAsync(nuevoPedido);

            // Confirmar transacción
            await transaction.CommitAsync();
        }
        catch
        {
            await transaction.RollbackAsync();
            throw;
        }
    }
}
catch (Exception ex)
{
    // Manejar error
    Console.WriteLine($"Error al procesar la transacción: {ex.Message}");
}

Manejo de errores

try
{
    // Código que interactúa con la base de datos
}
catch (SqlException sqlEx) when (sqlEx.Number == 2627) // Violación de restricción única
{
    throw new DuplicateEntryException("Ya existe un registro con la misma clave", sqlEx);
}
catch (SqlException sqlEx) when (sqlEx.Number == 547) // Violación de clave foránea
{
    throw new InvalidOperationException("No se puede eliminar el registro porque tiene registros relacionados", sqlEx);
}
catch (SqlException sqlEx) when (sqlEx.Number == 1205) // Deadlock
{
    // Reintentar la operación
    await Task.Delay(500);
    await ExecuteOperationWithRetry();
}
catch (Exception ex)
{
    _logger.LogError(ex, "Error al acceder a la base de datos");
    throw new DatabaseOperationException("Error al procesar la operación", ex);
}

Mejores prácticas

  1. Seguridad

    • Siempre usar parámetros en las consultas para evitar inyección SQL
    • No concatenar valores directamente en las consultas SQL
    • Usar los permisos mínimos necesarios para la conexión a la base de datos
    • Validar y limpiar todos los datos de entrada
  2. Rendimiento

    • Usar operaciones asíncronas para operaciones de E/S
    • Implementar paginación para consultas que devuelven grandes conjuntos de datos
    • Usar operaciones bulk para inserciones/actualizaciones masivas
    • Mantener las transacciones lo más cortas posibles
  3. Mantenibilidad

    • Usar procedimientos almacenados para lógica de negocio compleja
    • Implementar un patrón de repositorio para centralizar el acceso a datos
    • Usar DTOs para transferir datos entre capas
    • Documentar consultas complejas
  4. Manejo de Errores

    • Implementar un manejo de errores consistente
    • Registrar errores con suficiente contexto para diagnóstico
    • Proporcionar mensajes de error amigables al usuario final
    • Manejar adecuadamente las transacciones fallidas
  5. Patrones de Diseño

    • Usar el patrón Unit of Work para operaciones atómicas
    • Implementar el patrón Repository para abstraer el acceso a datos
    • Usar el patrón Specification para construir consultas complejas
    • Aplicar el principio de responsabilidad única

Manejo de Errores

try
{
    await _sqlRepository.ExecuteQueryAsync(query);
}
catch (SqlException ex)
{
    // Manejo específico de errores de SQL
    if (ex.Number == 2627) // Violación de restricción única
    {
        throw new DuplicateEntryException("Ya existe un registro con esta clave");
    }
    throw;
}

Optimización de Consultas

  1. Usar parámetros en lugar de concatenación de strings
  2. Implementar timeout razonable
  3. Usar índices apropiados
  4. Evitar SELECT * cuando no es necesario
  5. Usar procedimientos almacenados para consultas complejas
  6. Implementar paginación para listados grandes
  7. Usar transacciones solo cuando sea necesario

Mapa de métodos públicos y ubicación

Resumen rápido de métodos principales y dónde están implementados.

Sincrónicos — Implementations/MSSqlRepository.cs

  • Conexión y transacciones: ConnectionString, ConnectionState, ConnectionClose(), BeginTransaction(), CommitTransaction(), RollbackTransaction()
  • Consultas: ExecuteQueryAsTable(...), ExecuteProcedureAsTable(...)
  • Diccionarios: ExecuteProcedureAsDictionary(...)
  • Inserts compactos: ExecuteInsert<T>(string table, T entity), ExecuteInsert<T>(string table, ICollection<T> entities)

Asíncronos — Implementations/MSSqlRepository.Async.cs

  • Consultas: ExecuteQueryAsTableAsync(...), ExecuteProcedureAsTableAsync(...)
  • Diccionarios: ExecuteQueryAsDictionaryAsync(...), ExecuteProcedureAsDictionaryAsync(...)
  • Proyección única/listas: ExecuteQueryAsSingleAsync<T>(...), ExecuteProcedureAsSingleAsync<T>(...), ExecuteQueryAsListAsync<T>(...), ExecuteProcedureAsListAsync<T>(...)
  • Múltiples conjuntos de resultados: ExecuteMultiResultQueryAsync(...)
  • Comandos: ExecuteProcedureCommandAsync(...), ExecuteNonQueryAsync(...)
  • Inserts compactos: ExecuteInsertAsync<T>(string table, T entity), ExecuteInsertAsync<T>(string table, ICollection<T> entities)
  • Bulk Copy: ExecuteBulkCopyToTableAsync(...), ExecuteBulkCopyAsync(DataTable, ...), ExecuteBulkCopyAsync(IDataReader, ...), ExecuteBulkCopyAsync<T>(IEnumerable<T>, ...)
  • Utilidades: GetCurrentDateTimeAsync()

Notas:

  • Los métodos async usan ConfigureAwait(false) por ser librería.
  • Las operaciones Bulk aceptan BulkOperationsConfiguration o builder fluido.

Soporte

Para reportar errores o solicitar características, por favor abre un issue en el repositorio de GitHub.

Licencia

Este proyecto está bajo licencia MIT. Consulta el archivo LICENSE para más detalles.

  • void CommitTransaction()
    Confirma la transacción activa y cierra la conexión.

  • void RollbackTransaction()
    Revierte la transacción activa y cierra la conexión.

Consultas y ejecución

  • DataTable GetTableFromQuery(string query, Action<IDataParameterCollection> dbParameters = null)
    Ejecuta una consulta SQL y devuelve un DataTable con los resultados.

  • DataTable GetTableFromStoredProcedure(string storedProcedure, Action<IDataParameterCollection> dbParameters = null)
    Ejecuta un procedimiento almacenado y devuelve un DataTable.

  • T GetItemFromQuery<T>(string query, Func<IDataReader, T> expression, Action<IDataParameterCollection> dbParameters = null)
    Ejecuta una consulta y transforma la primera fila a un objeto del tipo T.

  • T GetItemFromStoredProcedure<T>(string storedProcedure, Func<IDataReader, T> expression, Action<IDataParameterCollection> dbParameters = null)
    Igual que el anterior, pero con procedimiento almacenado.

  • ICollection<Dictionary<string, object>> GetDictionaryFromQuery(string query, Action<IDataParameterCollection> dbParameters = null)
    Obtiene resultados de consulta como una colección de diccionarios con nombre/valor.

  • ICollection<Dictionary<string, object>> GetDictionaryFromStoredProcedure(string storedProcedure, Action<IDataParameterCollection> dbParameters = null)
    Igual que el anterior, pero para procedimientos almacenados.

  • ICollection<T> GetItemsFromStoredProcedure<T>(string storedProcedure, Func<IDataReader, T> expression, Action<IDataParameterCollection> dbParameters = null)
    Obtiene una colección de objetos tipo T a partir de un procedimiento almacenado.

  • ICollection<T> GetItemsFromQuery<T>(string query, Func<IDataReader, T> expression, Action<IDataParameterCollection> dbParameters = null)
    Obtiene una colección de objetos tipo T a partir de una consulta SQL.

Ejecución de comandos

  • void ExecuteNonQuery(string command, Action<IDataParameterCollection> dbParameters = null)
    Ejecuta comandos SQL que no retornan datos (INSERT, UPDATE, DELETE, etc.).

  • void ExecuteInsert<T>(string tableName, T entity) where T : class, new()
    Inserta un único objeto en la tabla especificada usando reflexión.

  • void ExecuteInsert<T>(string tableName, ICollection<T> collection) where T : class, new()
    Inserta una colección de objetos en la tabla especificada.

  • void ExecuteStoredProcedureCommand(string storedProcedure, Action<IDataParameterCollection> dbParameters = null)
    Ejecuta un procedimiento almacenado que no devuelve resultados.

Operaciones Bulk Copy

  • void ExecuteBulkCopyToTable(DataTable source, string destinationTable)
    Borra la tabla destino, la crea según la estructura de la tabla fuente y realiza la inserción masiva.

  • void ExecuteBulkCopy(DataTable source, string destinationTable)
    Inserción masiva desde un DataTable.

  • void ExecuteBulkCopy(IDataReader source, string destinationTable)
    Inserción masiva desde un IDataReader.

Otros métodos útiles

  • DateTime GetCurrentDateTime()
    Obtiene la fecha y hora actual del servidor SQL.

  • Métodos para crear parámetros SQL para consultas y procedimientos almacenados (AddParameter).

  • Métodos para configurar la conexión (SetDatabaseLogon).

Métodos Asíncronos de IMSSqlRepository

La interfaz también define métodos asíncronos para mejorar el rendimiento y escalabilidad de las operaciones de base de datos.

Métodos asíncronos principales

  • Task<T> GetItemFromQueryAsync<T>(string query, Func<IDataReader, T> expression, Action<IDataParameterCollection> dbParameters = null)
    Ejecuta una consulta SQL asíncrona y transforma la primera fila en un objeto T.

  • Task<T> GetItemFromStoredProcedureAsync<T>(string storedProcedure) where T : new()
    Ejecuta un procedimiento almacenado de forma asíncrona y devuelve un objeto T por defecto.

  • Task<T> GetItemFromStoredProcedureAsync<T>(string storedProcedure, Func<IDataReader, T> expression, Action<IDataParameterCollection> dbParameters = null)
    Ejecuta un procedimiento almacenado de forma asíncrona y mapea la primera fila a un objeto T.

  • Task<DataTable> GetTableFromQueryAsync(string query, Action<IDataParameterCollection> dbParameters = null)
    Ejecuta una consulta asíncrona y devuelve un DataTable con los resultados.

  • Task<DataTable> GetTableFromStoredProcedureAsync(string storedProcedure, Action<IDataParameterCollection> dbParameters = null)
    Ejecuta un procedimiento almacenado de forma asíncrona y devuelve un DataTable.

  • Task<ICollection<Dictionary<string, object>>> GetDictionaryFromQueryAsync(string query, Action<IDataParameterCollection> dbParameters = null)
    Ejecuta una consulta asíncrona y devuelve una colección de diccionarios.

  • Task<ICollection<Dictionary<string, object>>> GetDictionaryFromStoredProcedureAsync(string storedProcedure, Action<IDataParameterCollection> dbParameters = null)
    Igual que el anterior, pero para procedimientos almacenados.

  • Task<ICollection<T>> GetItemsFromQueryAsync<T>(string query, Func<IDataReader, T> expression, Action<IDataParameterCollection> dbParameters = null)
    Obtiene una colección de objetos T de una consulta asíncrona.

  • Task<ICollection<T>> GetItemsFromStoredProcedureAsync<T>(string storedProcedure) where T : new()
    Obtiene una colección de objetos T de un procedimiento almacenado asíncrono.

  • Task<ICollection<T>> GetItemsFromStoredProcedureAsync<T>(string storedProcedure, Func<IDataReader, T> expression, Action<IDataParameterCollection> dbParameters = null)
    Igual que el anterior, pero con mapeo personalizado.

Comandos asíncronos

  • Task<int> ExecuteStoredProcedureCommandAsync(string storedProcedure, Action<IDataParameterCollection> dbParameters = null)
    Ejecuta un procedimiento almacenado asíncrono que devuelve el número de filas afectadas.

  • Task ExecuteNonQueryAsync(string command, Action<IDataParameterCollection> dbParameters = null)
    Ejecuta un comando SQL que no retorna datos de forma asíncrona.

  • Task ExecuteBulkCopyToTableAsync(DataTable source, string destinationTable)
    Realiza una inserción masiva asíncrona creando la tabla destino.

  • Task ExecuteBulkCopyAsync(DataTable source, string destinationTable)
    Inserción masiva asíncrona desde un DataTable.

  • Task ExecuteBulkCopyAsync(IDataReader source, string destinationTable)
    Inserción masiva asíncrona desde un IDataReader.

  • Task ExecuteInsertAsync<T>(string tableName, T entity)
    Inserta un objeto de forma asíncrona en la tabla especificada.

  • Task ExecuteInsertAsync<T>(string tableName, ICollection<T> collection)
    Inserta una colección de objetos de forma asíncrona.

  • Task<DateTime> GetCurrentDateTimeAsync()
    Obtiene la fecha y hora actual del servidor SQL de forma asíncrona.

Métodos para manejo de tablas en IMSSqlRepository

Además de los métodos para manipulación de datos, la interfaz incluye operaciones para gestionar tablas directamente:

  • void DropTable(string tableName)
    Elimina la tabla especificada de la base de datos.

  • void CreateTable(DataTable source, string destinationTable)
    Crea una tabla en la base de datos a partir de la estructura de un DataTable.

  • void CreateTable(IDataReader reader, string destinationTable)
    Crea una tabla en la base de datos basándose en la estructura del IDataReader.

Interfaz IServiceProviderKeyed

Esta interfaz proporciona un mecanismo para obtener servicios basados en una clave específica:

public interface IServiceProviderKeyed
{
    TService GetKeyedService<TService, TKeyed>(TKeyed key);
}

public void ConfigureServices(IServiceCollection services)
{
    services.AddMicrosoftSQL(provider =>
    {
        // Aquí configuras la instancia concreta de IMSSqlRepository
        IMSSqlRepository repo = new MSSqlRepository();
        repo.SetDatabaseLogon("Server=myServer;Database=myDB;User Id=myUser;Password=myPassword;");
        return repo;
    }, ServiceLifetime.Scoped);
}
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  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.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Framework net48 is compatible.  net481 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
2025.9.5 45 9/5/2025
2025.9.3 121 9/3/2025
2025.9.2 115 9/3/2025
2025.8.30 158 8/30/2025
2025.8.26 173 8/26/2025
2025.8.20 121 8/20/2025
2025.8.19 120 8/19/2025
2025.8.14 126 8/13/2025
2025.8.13 128 8/13/2025
2025.8.10 117 8/11/2025
2025.8.7 185 8/8/2025
2025.7.24 454 7/24/2025
2025.7.21 495 7/22/2025
2025.7.20 209 7/20/2025
2025.7.19 210 7/20/2025
2025.7.18 58 7/18/2025
2025.7.17 122 7/17/2025
2025.7.14 129 7/14/2025
2025.7.13 129 7/14/2025
2025.7.9 135 7/8/2025
2025.7.8 129 7/8/2025
2025.7.3 361 7/4/2025
2025.7.2 360 7/4/2025
2025.6.11 282 6/11/2025
2025.5.26 98 5/24/2025
2025.5.25 64 5/24/2025
2025.5.24 63 5/24/2025
2025.5.23 67 5/23/2025
2025.5.20 134 5/21/2025
2025.5.15 224 5/15/2025
2025.5.8 156 5/8/2025
2025.5.6 140 5/6/2025
2025.5.4 145 5/4/2025
2025.5.1 149 5/1/2025
2025.4.30 142 4/30/2025
2025.4.29 131 4/27/2025
2025.4.28 90 4/26/2025
2025.4.27 96 4/26/2025
2025.4.26 96 4/26/2025
2025.4.12 127 4/12/2025
2025.4.4 105 4/5/2025
2025.3.24 492 3/25/2025
2025.3.22 161 3/22/2025
2025.3.11 169 3/11/2025
2025.3.9 173 3/10/2025
2025.3.8 137 3/9/2025
2025.2.16 219 2/17/2025
2025.2.3 111 2/3/2025
2025.2.2 121 2/2/2025
2025.2.1 113 2/1/2025
2025.1.31 101 1/29/2025
2025.1.30 104 1/28/2025
2025.1.29 103 1/28/2025
2025.1.28 103 1/28/2025
2025.1.18 106 1/15/2025
2025.1.17 96 1/13/2025
2025.1.16 88 1/13/2025
2025.1.15 98 1/13/2025
2025.1.14 89 1/13/2025
2025.1.13 93 1/12/2025
2025.1.12 91 1/12/2025
2025.1.7 117 1/7/2025
2024.12.29 116 12/28/2024
2024.12.28 112 12/28/2024
2024.12.19 117 12/19/2024
2024.12.18 116 12/19/2024 2024.12.18 is deprecated because it is no longer maintained and has critical bugs.
2024.12.4 129 12/4/2024
2024.12.2 112 12/2/2024
2024.11.30 118 12/1/2024
2024.11.13 123 11/13/2024
2024.11.12 118 11/12/2024
2024.10.10 131 10/11/2024
2024.10.9 118 10/9/2024
2024.10.7 127 10/4/2024
2024.10.6 109 10/4/2024
2024.10.4 120 10/3/2024
2024.9.8 110 9/9/2024
2024.9.5 129 9/6/2024
2024.8.13 145 8/14/2024
2024.8.2 107 8/1/2024
2024.8.1 102 8/1/2024
2024.7.24 130 7/24/2024
2024.6.9 117 6/10/2024
2024.4.22 140 4/23/2024
2024.4.16 134 4/15/2024
2024.4.15 115 4/15/2024
2024.4.14 141 4/14/2024
2024.4.13 119 4/13/2024
2024.4.12 124 4/13/2024
2024.4.7 127 4/8/2024
2024.2.18 137 2/24/2024
2024.1.2 205 1/2/2024
2023.12.26 143 12/26/2023
2023.11.30 159 12/11/2023
2023.11.28 133 11/27/2023
2023.11.15 179 11/16/2023
2023.11.10 160 11/10/2023
2023.10.29 136 10/30/2023
2023.8.17 171 8/18/2023
2023.8.14 153 8/14/2023
2023.8.9 157 8/9/2023
2023.8.1 181 7/31/2023
2023.7.31 176 7/31/2023
2023.7.30 179 7/31/2023
2023.7.26 186 7/26/2023
2023.7.25 185 7/25/2023
2023.7.11 192 7/12/2023
2023.7.6 179 7/7/2023
2023.6.19 186 6/20/2023
2023.6.18 180 6/19/2023
2023.5.20 190 5/16/2023
2023.5.19 188 5/13/2023
2023.5.14 185 5/8/2023
2023.5.10 192 5/4/2023
2023.5.6 207 4/29/2023
2023.4.28 264 4/26/2023