UnitOfWorkContext.Core 1.9.0

dotnet add package UnitOfWorkContext.Core --version 1.9.0
                    
NuGet\Install-Package UnitOfWorkContext.Core -Version 1.9.0
                    
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="UnitOfWorkContext.Core" Version="1.9.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="UnitOfWorkContext.Core" Version="1.9.0" />
                    
Directory.Packages.props
<PackageReference Include="UnitOfWorkContext.Core" />
                    
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 UnitOfWorkContext.Core --version 1.9.0
                    
#r "nuget: UnitOfWorkContext.Core, 1.9.0"
                    
#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 UnitOfWorkContext.Core@1.9.0
                    
#: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=UnitOfWorkContext.Core&version=1.9.0
                    
Install as a Cake Addin
#tool nuget:?package=UnitOfWorkContext.Core&version=1.9.0
                    
Install as a Cake Tool

UnitOfWork Context Core

NuGet NuGet .NET

Librería .NET para implementar el patrón Unit of Work y Repository con Entity Framework Core.

Abstrae las transacciones de base de datos y proporciona una interfaz limpia para trabajar con múltiples contextos y esquemas en proyectos modulares.


📋 Características

Patrón Unit of Work - Gestión centralizada de transacciones ✅ Patrón Repository - Operaciones CRUD genéricas ✅ Múltiples Contextos - Soporte para múltiples DbContext en el mismo proyecto ✅ Múltiples Esquemas - Trabaja con diferentes esquemas de base de datos (account, catalog, payment, etc.) ✅ Factory Pattern - Resolución dinámica de contextos ✅ Paginación - Sistema integrado de paginación con IPaginate<T> ✅ LINQ Support - Expresiones lambda para filtros, ordenamiento e includes ✅ Transacciones - Manejo automático de commit/rollback ✅ Inyección de Dependencias - Integración nativa con DI de .NET ✅ Type-Safe - Fuertemente tipado con genéricos


📦 Instalación

NuGet Package Manager

Install-Package UnitOfWorkContext.Core
Install-Package UnitOfWorkContext.DependencyInjection

.NET CLI

dotnet add package UnitOfWorkContext.Core
dotnet add package UnitOfWorkContext.DependencyInjection

PackageReference

<PackageReference Include="UnitOfWorkContext.Core" Version="1.9.0" />
<PackageReference Include="UnitOfWorkContext.DependencyInjection" Version="1.9.0" />

🚀 Inicio Rápido

1. Configura tu DbContext

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options)
        : base(options) { }

    public DbSet<Product> Products { get; set; }
    public DbSet<Category> Categories { get; set; }
}

2. Registra en Program.cs

using UnitOfWorkContextCore.DependencyInjection;

var builder = WebApplication.CreateBuilder(args);

// Registrar DbContext
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(connectionString));

// Registrar UnitOfWork
builder.Services.AddUnitOfWork<AppDbContext>();

var app = builder.Build();

3. Usa en tus Servicios

public class ProductService
{
    private readonly IUnitOfWork<AppDbContext> _unitOfWork;

    public ProductService(IUnitOfWork<AppDbContext> unitOfWork)
    {
        _unitOfWork = unitOfWork;
    }

    public Product CreateProduct(Product product)
    {
        _unitOfWork.OpenTransaction();
        try
        {
            var repo = _unitOfWork.GetRepository<Product>();
            repo.Insert(product);
            _unitOfWork.Commit(); // SaveChanges + Commit
            return product;
        }
        catch
        {
            _unitOfWork.Dispose(); // Rollback automático
            throw;
        }
    }

    public IPaginate<Product> GetProducts(int page = 1)
    {
        var repo = _unitOfWork.GetRepository<Product>();
        return repo.Get(
            predicate: p => p.IsActive,
            orderBy: q => q.OrderBy(p => p.Name),
            index: page - 1,
            size: 20
        );
    }
}

🏭 Múltiples Esquemas (Nuevo en v1.9)

¿Necesitas trabajar con múltiples esquemas en la misma base de datos? Usa el Factory Pattern.

Escenario:

MiBaseDatos
├── [account]  → Usuarios, Roles
├── [catalog]  → Productos, Categorías
└── [payment]  → Facturas, Pagos

1. Define tus Contextos con Esquemas

public class AccountContext : DbContext
{
    public AccountContext(DbContextOptions<AccountContext> options) : base(options) { }

    public DbSet<User> Users { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.HasDefaultSchema("account"); // ← Esquema específico
        base.OnModelCreating(modelBuilder);
    }
}

public class CatalogContext : DbContext
{
    public CatalogContext(DbContextOptions<CatalogContext> options) : base(options) { }

    public DbSet<Product> Products { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.HasDefaultSchema("catalog"); // ← Esquema específico
        base.OnModelCreating(modelBuilder);
    }
}

2. Registra con Claves Únicas

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");

// Registrar DbContext
builder.Services.AddDbContext<AccountContext>(options =>
    options.UseSqlServer(connectionString));
builder.Services.AddDbContext<CatalogContext>(options =>
    options.UseSqlServer(connectionString));

// Registrar UnitOfWork con CLAVES
builder.Services.AddUnitOfWork<AccountContext>("account");
builder.Services.AddUnitOfWork<CatalogContext>("catalog");

// Registrar la Factory
builder.Services.AddUnitOfWorkFactory();

3. Usa la Factory

Opción A: Resolución Dinámica (por clave)
public class MultiSchemaService
{
    private readonly IUnitOfWorkFactory _factory;

    public MultiSchemaService(IUnitOfWorkFactory factory)
    {
        _factory = factory;
    }

    public void ProcessBySchema(string schemaName)
    {
        // Resolver dinámicamente: "account", "catalog", etc.
        var uow = _factory.GetUnitOfWork(schemaName);

        uow.OpenTransaction();
        try
        {
            // Trabajar con el esquema...
            uow.Commit();
        }
        catch
        {
            uow.Dispose();
            throw;
        }
    }
}
Opción B: Type-Safe (con genéricos)
public class OrderService
{
    private readonly IUnitOfWorkFactory _factory;

    public OrderService(IUnitOfWorkFactory factory)
    {
        _factory = factory;
    }

    public void CreateOrder(int userId, int productId)
    {
        // Obtener UnitOfWork tipados
        var accountUoW = _factory.GetUnitOfWork<AccountContext>();
        var catalogUoW = _factory.GetUnitOfWork<CatalogContext>();

        // Validar usuario (esquema account)
        var userRepo = accountUoW.GetRepository<User>();
        var user = userRepo.Find(u => u.Id == userId);

        // Validar producto (esquema catalog)
        var productRepo = catalogUoW.GetRepository<Product>();
        var product = productRepo.Find(p => p.Id == productId);

        // Procesar orden...
    }
}

📚 Ver Guía Completa de Múltiples Esquemas → 🚀 Ver Inicio Rápido con Ejemplos →


📖 Uso Detallado

Operaciones CRUD

var repo = _unitOfWork.GetRepository<Product>();

// CREATE
var product = new Product { Name = "Laptop", Price = 999.99m };
repo.Insert(product);

// READ
var product = repo.Find(p => p.Id == 1);
var products = repo.Get(
    predicate: p => p.Price > 100,
    orderBy: q => q.OrderBy(p => p.Name),
    include: q => q.Include(p => p.Category)
);

// UPDATE
product.Price = 899.99m;
repo.Update(product);

// DELETE
repo.Remove(product);

// Commit cambios
_unitOfWork.Commit();

Operaciones en Lote

var repo = _unitOfWork.GetRepository<Product>();

// Insertar múltiples
var products = new List<Product>
{
    new Product { Name = "Product 1" },
    new Product { Name = "Product 2" }
};
repo.InsertRange(products);

// Actualizar múltiples
repo.UpdateRange(updatedProducts);

// Eliminar múltiples
repo.RemoveRange(productsToDelete);

_unitOfWork.Commit();

Paginación

var repo = _unitOfWork.GetRepository<Product>();

var result = repo.Get(
    predicate: p => p.IsActive,
    orderBy: q => q.OrderByDescending(p => p.CreatedAt),
    index: 0,      // Página 0 (primera página)
    size: 20       // 20 items por página
);

Console.WriteLine($"Total: {result.Count}");
Console.WriteLine($"Páginas: {result.Pages}");
Console.WriteLine($"Tiene anterior: {result.HasPrevious}");
Console.WriteLine($"Tiene siguiente: {result.HasNext}");

foreach (var product in result.Items)
{
    Console.WriteLine(product.Name);
}

Includes y Proyecciones

var repo = _unitOfWork.GetRepository<Product>();

// Eager Loading
var products = repo.Get(
    include: q => q.Include(p => p.Category)
                   .ThenInclude(c => c.ParentCategory)
);

// Proyecciones (Select)
var productDtos = repo.Get(
    selector: p => new ProductDto
    {
        Id = p.Id,
        Name = p.Name,
        CategoryName = p.Category.Name
    }
);

Tracking Control

var repo = _unitOfWork.GetRepository<Product>();

// Sin tracking (mejor rendimiento para consultas)
var products = repo.Get(enableTracking: false);

// Con tracking (necesario para updates)
var product = repo.Find(p => p.Id == 1, enableTracking: true);
product.Price = 999.99m;
repo.Update(product);

Transacciones Manuales

_unitOfWork.OpenTransaction();
try
{
    var productRepo = _unitOfWork.GetRepository<Product>();
    var categoryRepo = _unitOfWork.GetRepository<Category>();

    // Múltiples operaciones
    productRepo.Insert(newProduct);
    categoryRepo.Update(category);

    // Commit todo junto
    _unitOfWork.Commit();
}
catch (Exception)
{
    // Rollback automático
    _unitOfWork.Dispose();
    throw;
}

🎨 Integración con DataTables

Soporte nativo para jQuery DataTables:

[HttpPost("datatable")]
public IActionResult GetDataTable([FromBody] DataTableRequest request)
{
    var repo = _unitOfWork.GetRepository<Product>();

    var result = repo.Get(
        predicate: p => p.Name.Contains(request.Search),
        orderBy: q => q.OrderBy(p => p.Name),
        index: request.Start / request.Length,
        size: request.Length
    );

    return Ok(result.ToDataTableResponse(request.Draw));
}

📚 API Reference

IUnitOfWork<TContext>

Método Descripción
GetRepository<T>() Obtiene un repositorio para la entidad T
OpenTransaction() Inicia una transacción de base de datos
Commit() Guarda cambios y confirma la transacción
Dispose() Libera recursos y hace rollback si es necesario
Context Acceso al DbContext subyacente

IRepository<T>

Escritura
Método Descripción
Insert(T entity) Inserta una entidad
Update(T entity) Actualiza una entidad
Remove(T entity) Elimina una entidad
InsertRange(ICollection<T>) Inserta múltiples entidades
UpdateRange(ICollection<T>) Actualiza múltiples entidades
RemoveRange(ICollection<T>) Elimina múltiples entidades
Lectura
Método Descripción
Find(Expression<Func<T, bool>>) Busca una entidad por predicado
Get(...) Obtiene colección paginada con filtros

Parámetros de Get():

  • predicate - Filtro LINQ (Where)
  • orderBy - Ordenamiento (OrderBy/ThenBy)
  • include - Eager loading (Include/ThenInclude)
  • selector - Proyección (Select)
  • index - Índice de página (0-based)
  • size - Tamaño de página
  • enableTracking - Habilitar tracking de EF Core

IUnitOfWorkFactory (Nuevo)

Método Descripción
GetUnitOfWork(string key) Obtiene UnitOfWork por clave
GetUnitOfWork<TContext>() Obtiene UnitOfWork tipado
HasContext(string key) Verifica si existe un contexto

Extensiones de DI

Método Descripción
AddUnitOfWork<TContext>() Registra UnitOfWork tradicional
AddUnitOfWork<TContext>(string key) Registra UnitOfWork con clave
AddUnitOfWorkFactory() Registra la factory de contextos
GetRegisteredContexts() Obtiene contextos registrados

🗂️ Estructura del Proyecto

UnitOfWorkContextCore/
├── UnitOfWork.cs                      - Implementación del patrón
├── UnitOfWorkFactory.cs               - Factory para múltiples contextos
├── Repository.cs                      - Repositorio de escritura
├── ReadRepository.cs                  - Repositorio de lectura
├── Interfaces/
│   ├── IUnitOfWork.cs                - Interfaz principal
│   ├── IUnitOfWorkFactory.cs         - Interfaz de factory
│   ├── IRepository.cs                - Interfaz de repositorio
│   └── IReadRepository.cs            - Interfaz de lectura
├── Paging/
│   ├── IPaginate.cs                  - Interfaz de paginación
│   ├── Paginate.cs                   - Implementación de paginación
│   └── PaginateExtensions.cs         - Extensiones de paginación
└── Helpers/
    ├── PredicateBuilder.cs           - Constructor de predicados LINQ
    └── OrderingHelper.cs             - Helper de ordenamiento

UnitOfWorkContextCore.DependencyInjection/
└── InjectUnitOfWorkExtension.cs      - Extensiones para DI

🔧 Requisitos

  • .NET 8.0 o superior
  • Entity Framework Core 8.0 o superior
  • Microsoft.Extensions.DependencyInjection

📝 Ejemplos Completos

Ejemplo 1: Servicio Simple

public class CategoryService
{
    private readonly IUnitOfWork<AppDbContext> _unitOfWork;

    public CategoryService(IUnitOfWork<AppDbContext> unitOfWork)
    {
        _unitOfWork = unitOfWork;
    }

    public Category Create(string name)
    {
        _unitOfWork.OpenTransaction();
        try
        {
            var repo = _unitOfWork.GetRepository<Category>();
            var category = new Category { Name = name };
            repo.Insert(category);
            _unitOfWork.Commit();
            return category;
        }
        catch
        {
            _unitOfWork.Dispose();
            throw;
        }
    }

    public List<Category> GetAll()
    {
        var repo = _unitOfWork.GetRepository<Category>();
        return repo.Get(orderBy: q => q.OrderBy(c => c.Name))
                   .Items.ToList();
    }
}

Ejemplo 2: Servicio con Relaciones

public class ProductService
{
    private readonly IUnitOfWork<AppDbContext> _unitOfWork;

    public ProductService(IUnitOfWork<AppDbContext> unitOfWork)
    {
        _unitOfWork = unitOfWork;
    }

    public Product CreateWithCategory(string productName, int categoryId)
    {
        _unitOfWork.OpenTransaction();
        try
        {
            // Validar categoría existe
            var categoryRepo = _unitOfWork.GetRepository<Category>();
            var category = categoryRepo.Find(c => c.Id == categoryId);
            if (category == null)
                throw new Exception("Categoría no encontrada");

            // Crear producto
            var productRepo = _unitOfWork.GetRepository<Product>();
            var product = new Product
            {
                Name = productName,
                CategoryId = categoryId
            };
            productRepo.Insert(product);

            _unitOfWork.Commit();
            return product;
        }
        catch
        {
            _unitOfWork.Dispose();
            throw;
        }
    }

    public IPaginate<ProductDto> SearchProducts(string searchTerm, int page)
    {
        var repo = _unitOfWork.GetRepository<Product>();

        return repo.Get(
            selector: p => new ProductDto
            {
                Id = p.Id,
                Name = p.Name,
                Price = p.Price,
                CategoryName = p.Category.Name
            },
            predicate: p => p.Name.Contains(searchTerm),
            orderBy: q => q.OrderBy(p => p.Name),
            include: q => q.Include(p => p.Category),
            index: page - 1,
            size: 20
        );
    }
}

🆕 Novedades v1.9

Factory Pattern para Múltiples Esquemas

  • Nuevo IUnitOfWorkFactory para resolución dinámica de contextos
  • Soporte para múltiples DbContext con esquemas diferentes
  • Métodos AddUnitOfWork(string key) y AddUnitOfWorkFactory()
  • Validación de contextos con HasContext()
  • Thread-safe con locks en registro de contextos
  • Método GetRegisteredContexts() para inspección y debugging

🔧 Mejoras

  • Mensajes de error más descriptivos con contextos disponibles
  • Validación de claves duplicadas al registrar contextos
  • Documentación completa con 3 guías especializadas
  • Backward compatible con versiones anteriores (sin breaking changes)

📖 Documentación Adicional


🤝 Contribuciones

Las contribuciones son bienvenidas. Por favor:

  1. Fork el proyecto
  2. Crea una rama para tu feature (git checkout -b feature/AmazingFeature)
  3. Commit tus cambios (git commit -m 'Add some AmazingFeature')
  4. Push a la rama (git push origin feature/AmazingFeature)
  5. Abre un Pull Request

📄 Licencia

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


💡 Soporte

Si encuentras algún problema o tienes sugerencias:


🙏 Agradecimientos

Basado en el patrón Unit of Work descrito por Martin Fowler y las mejores prácticas de arquitectura de software .NET.


Hecho con ❤️ para la comunidad .NET

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

NuGet packages (1)

Showing the top 1 NuGet packages that depend on UnitOfWorkContext.Core:

Package Downloads
UnitOfWorkContext.DependencyInjection

Extensiones de inyección de dependencias para UnitOfWorkContextCore. Proporciona métodos de extensión para IServiceCollection que facilitan el registro de UnitOfWork, múltiples contextos con claves únicas, y Factory Pattern para resolución dinámica. Incluye AddUnitOfWork, AddUnitOfWorkFactory y GetRegisteredContexts.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.9.0 231 12/14/2025
1.8.0.13 165 2/12/2025
1.6.0.10 391 3/18/2023

v1.9.0 - Factory Pattern para Múltiples Esquemas
- Nuevo IUnitOfWorkFactory para resolución dinámica de contextos
- Soporte para múltiples DbContext con esquemas diferentes
- Métodos AddUnitOfWork(string key) y AddUnitOfWorkFactory()
- Método GetRegisteredContexts() para debugging
- Validación de contextos con HasContext()
- Thread-safe con locks en registro de contextos
- Backward compatible con versiones anteriores
- Documentación completa con 3 guías (README, MultiSchema, QuickStart)

Ver CHANGELOG.md para historial completo.