EV.Infrastructure.EntityFramework
1.0.0
dotnet add package EV.Infrastructure.EntityFramework --version 1.0.0
NuGet\Install-Package EV.Infrastructure.EntityFramework -Version 1.0.0
<PackageReference Include="EV.Infrastructure.EntityFramework" Version="1.0.0" />
<PackageVersion Include="EV.Infrastructure.EntityFramework" Version="1.0.0" />
<PackageReference Include="EV.Infrastructure.EntityFramework" />
paket add EV.Infrastructure.EntityFramework --version 1.0.0
#r "nuget: EV.Infrastructure.EntityFramework, 1.0.0"
#:package EV.Infrastructure.EntityFramework@1.0.0
#addin nuget:?package=EV.Infrastructure.EntityFramework&version=1.0.0
#tool nuget:?package=EV.Infrastructure.EntityFramework&version=1.0.0
EV.Infrastructure.EntityFramework
Arquitectura Domain-Driven Design: Implementación completa de patrones Repository y Unit of Work con soporte multi-database y ASP.NET Core Identity.
✨ Características
- 🏗️ Clean Architecture: Implementación completa de patrones Repository y Unit of Work
- 🗃️ Multi-Database: Soporte para SQL Server, PostgreSQL, MySQL y más
- 🔐 ASP.NET Core Identity: Integración completa con configuración flexible
- 📄 Soft Delete: Soporte para eliminación lógica con auditoría completa
- 🔍 Auditoría Automática: Seguimiento automático de creación, modificación y eliminación
- 🎯 Entidades Base Genéricas: BaseEntity con propiedades de auditoría predefinidas
- 🗂️ Entidades de Dominio Opcionales: DomainType/DomainValue para gestión de catálogos
- ⚙️ Configuración Flexible: Contextos modulares según necesidades
- 🚀 Operaciones Asíncronas: Todas las operaciones optimizadas para async/await
- 📄 Paginación Incluida: Métodos de paginación listos para usar
- 🔍 Búsquedas Avanzadas: Soporte para predicados y includes
- 🎭 Dependency Injection: Configuración lista para inyección de dependencias
📦 Instalación
Package Manager Console
Install-Package EV.Infrastructure.EntityFramework
.NET CLI
dotnet add package EV.Infrastructure.EntityFramework
🚀 Inicio Rápido
1. Configuración en appsettings.json
{
"DatabaseConfiguration": {
"ConnectionString": "Data Source=.\\SQLExpress,1433;Initial Catalog=MyDatabase;Persist Security Info=True;User ID=sa;Password=P@ssw0rd;Encrypt=True;TrustServerCertificate=true;",
"Provider": "SqlServer",
"CommandTimeout": 30,
"EnableSensitiveDataLogging": false,
"EnableDetailedErrors": false,
"EnableMigration": true
},
"IdentityConfiguration": {
"RequiredDigit": true,
"RequiredLength": 10,
"RequireLowercase": true,
"RequiredUniqueChars": 3,
"RequireUppercase": true,
"MaxFailedAttempts": 3,
"LockoutTimeSpanInDays": 1,
"RequireNonAlphanumeric": true,
"AllowedForNewUsers": true,
"IterationCount": 18000,
"RequireConfirmedEmail": true
}
}
2. Configuración en Program.cs
var builder = WebApplication.CreateBuilder(args);
// Obtener configuraciones
var databaseConfig = builder.Configuration.GetSection("DatabaseConfiguration").Get<DatabaseConfiguration>()!;
var identityConfig = builder.Configuration.GetSection("IdentityConfiguration").Get<IdentityConfiguration>()!;
// Opción A: Solo DataAccess (sin Identity)
builder.Services.AddDataAccessServices<MyAppDbContext>(databaseConfig);
// Opción B: DataAccess + Identity (RECOMENDADO)
builder.Services.AddDataAccessWithIdentityServices<IdentityUserApp, IdentityRole, MyAppDbContext>(databaseConfig, identityConfig);
var app = builder.Build();
3. Crear tu DbContext
// ApplicationDbContext.cs
using EV.Infrastructure.EntityFramework.Contexts;
// Opción 1: Solo tus entidades personalizadas
public class MyAppDbContext : BaseDbContext
{
public MyAppDbContext(DbContextOptions<MyAppDbContext> options) : base(options) { }
public DbSet<Product> Products { get; set; }
public DbSet<Order> Orders { get; set; }
}
// Opción 2: Con entidades de dominio (DomainType/DomainValue)
public class MyAppDbContext : BaseDbContextWithDomainEntities
{
public MyAppDbContext(DbContextOptions<MyAppDbContext> options) : base(options) { }
public DbSet<Product> Products { get; set; }
// DomainTypes y DomainValues incluidos automáticamente
}
// Opción 3: Con Identity (sin dominio)
public class MyAppDbContext : BaseDbContextWithIdentityEntities
{
public MyAppDbContext(DbContextOptions<MyAppDbContext> options) : base(options) { }
public DbSet<Product> Products { get; set; }
// Entidades de Identity incluidas automáticamente
}
// Opción 4: Completo (Identity + Domain)
public class MyAppDbContext : BaseDbContextWithDomainAndIdentityEntities
{
public MyAppDbContext(DbContextOptions<MyAppDbContext> options) : base(options) { }
public DbSet<Product> Products { get; set; }
public DbSet<Order> Orders { get; set; }
// DomainTypes, DomainValues e Identity incluidos automáticamente
}
🗃️ Contextos Disponibles
Contexto | Domain Entities | Identity | Uso Recomendado |
---|---|---|---|
BaseDbContext |
❌ | ❌ | Testing, contextos minimalistas |
BaseDbContextWithDomainEntities |
✅ | ❌ | Apps sin autenticación |
BaseDbContextWithIdentityEntities |
❌ | ✅ | Microservicios de autenticación |
BaseDbContextWithDomainAndIdentityEntities |
✅ | ✅ | Aplicaciones completas |
🌐 Soporte Multi-Database
SQL Server (por defecto)
{
"DatabaseConfiguration": {
"Provider": "SqlServer",
"ConnectionString": "Data Source=.\\SQLExpress;Initial Catalog=MyDB;Integrated Security=true;"
}
}
PostgreSQL
{
"DatabaseConfiguration": {
"Provider": "PostgreSql",
"ConnectionString": "Host=localhost;Database=mydb;Username=postgres;Password=pass"
}
}
MySQL
{
"DatabaseConfiguration": {
"Provider": "MySql",
"ConnectionString": "Server=localhost;Database=mydb;Uid=root;Pwd=password;"
}
}
🎯 Entidades Base
BaseEntity<TKey>
Todas las entidades heredan de BaseEntity<TKey>
:
public abstract class BaseEntity<TKey>
{
public TKey Id { get; set; } // Clave primaria
public DateTime CreatedAt { get; set; } // Fecha de creación
public DateTime? UpdatedAt { get; set; } // Fecha de actualización
public string CreatedBy { get; set; } // Usuario que creó
public string? UpdatedBy { get; set; } // Usuario que actualizó
public string? DeletedBy { get; set; } // Usuario que eliminó
public DateTime? DeletedAt { get; set; } // Fecha de eliminación
public bool IsDeleted { get; set; } // Marcador de eliminación
// Métodos para gestión de eliminación lógica
public virtual void SoftDelete(string deletedBy);
public virtual void Restore(string restoredBy);
}
Entidad de Usuario Personalizada
// Tu entidad personalizada que extiende IdentityUser
public class IdentityUserApp : IdentityUser
{
public string FullName { get; set; } = string.Empty;
public string Phone { get; set; } = string.Empty;
public string DocumentType { get; set; } = string.Empty;
public string DocumentNumber { get; set; } = string.Empty;
public string Token { get; set; } = string.Empty;
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime? UpdatedAt { get; set; }
public DateTime? DeletedAt { get; set; }
public string CreatedBy { get; set; } = string.Empty;
public string? UpdatedBy { get; set; }
public string? DeletedBy { get; set; }
public bool IsDeleted { get; set; }
}
Proveedor de Base de Datos
public enum DatabaseProvider
{
SqlServer,
PostgreSql,
MySql,
Oracle, (T.O.D.O.)
SQLite (T.O.D.O.)
}
🗂️ Entidades de Dominio
Cuando usas contextos con entidades de dominio, obtienes:
// Para gestión de catálogos y configuraciones
public class DomainType : BaseEntity<int>
{
public string Code { get; set; } // Código único
public string Name { get; set; } // Nombre descriptivo
public string? Description { get; set; } // Descripción opcional
public virtual ICollection<DomainValue> DomainValues { get; set; }
}
public class DomainValue : BaseEntity<int>
{
public string Code { get; set; } // Código del valor
public string Name { get; set; } // Nombre del valor
public string? Description { get; set; } // Descripción opcional
public int DomainTypeId { get; set; } // FK a DomainType
public virtual DomainType DomainType { get; set; }
}
📋 API Reference
IRepository<T, TKey>
Método | Descripción | Parámetros |
---|---|---|
GetByIdAsync() |
Obtiene entidad por ID | id , cancellationToken? |
GetByIdAsync() |
Obtiene entidad con includes | id , includes[] |
GetByIdIncludingDeletedAsync() |
Obtiene incluyendo eliminados | id , cancellationToken? |
GetAllAsync() |
Obtiene todas las entidades | cancellationToken? |
GetAllAsync() |
Obtiene todas con includes | includes[] |
GetPagedAsync() |
Paginación básica | pageNumber , pageSize , cancellationToken? |
GetPagedAsync() |
Paginación con filtro | pageNumber , pageSize , filter? , includes[] |
FindAsync() |
Buscar con predicado | predicate , cancellationToken? |
FirstOrDefaultAsync() |
Primer elemento o nulo | predicate , cancellationToken? |
AddAsync() |
Agregar entidad | entity , cancellationToken? |
AddRangeAsync() |
Agregar múltiples | entities , cancellationToken? |
UpdateAsync() |
Actualizar entidad | entity , cancellationToken? |
UpdateRangeAsync() |
Actualizar múltiples | entities , cancellationToken? |
SoftDeleteAsync() |
Eliminación lógica | id/entity , deletedBy , cancellationToken? |
HardDeleteAsync() |
Eliminación física | id/entity , cancellationToken? |
RestoreAsync() |
Restaurar eliminado | id , restoredBy , cancellationToken? |
ExistsAsync() |
Verificar existencia | id , cancellationToken? |
CountAsync() |
Contar registros | predicate? , cancellationToken? |
IUnitOfWork
Método | Descripción | Parámetros |
---|---|---|
CreateRepository<T, TKey>() |
Crear repositorio | Tipos genéricos |
SaveChangesAsync() |
Guardar cambios | cancellationToken? |
BeginTransactionAsync() |
Iniciar transacción | cancellationToken? |
CommitTransactionAsync() |
Confirmar transacción | cancellationToken? |
RollbackTransactionAsync() |
Revertir transacción | cancellationToken? |
💻 Ejemplos de Uso
Crear una Entidad Personalizada
using EV.Infrastructure.EntityFramework.Entities;
public class Product : BaseEntity<int>
{
public string Name { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public decimal Price { get; set; }
public int CategoryId { get; set; }
public string SKU { get; set; } = string.Empty;
// Propiedades de navegación
public virtual Category Category { get; set; } = null!;
public virtual ICollection<OrderItem> OrderItems { get; set; } = new List<OrderItem>();
}
Operaciones Básicas en Servicios
public class ProductService
{
private readonly IUnitOfWork _unitOfWork;
public ProductService(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public async Task<Product> CreateProductAsync(CreateProductDto dto, string userId)
{
var repository = _unitOfWork.CreateRepository<Product, int>();
var product = new Product
{
Name = dto.Name,
Description = dto.Description,
Price = dto.Price,
CategoryId = dto.CategoryId,
SKU = dto.SKU,
CreatedBy = userId
};
await repository.AddAsync(product);
await _unitOfWork.SaveChangesAsync();
return product;
}
public async Task<(IEnumerable<Product> Items, int TotalCount)> GetProductsPagedAsync(
int pageNumber, int pageSize, string? searchTerm = null)
{
var repository = _unitOfWork.CreateRepository<Product, int>();
if (string.IsNullOrEmpty(searchTerm))
{
return await repository.GetPagedAsync(pageNumber, pageSize);
}
return await repository.GetPagedAsync(
pageNumber,
pageSize,
filter: p => p.Name.Contains(searchTerm) || p.Description.Contains(searchTerm),
includes: p => p.Category);
}
public async Task<bool> SoftDeleteProductAsync(int productId, string deletedBy)
{
var repository = _unitOfWork.CreateRepository<Product, int>();
var exists = await repository.ExistsAsync(productId);
if (!exists) return false;
await repository.SoftDeleteAsync(productId, deletedBy);
await _unitOfWork.SaveChangesAsync();
return true;
}
}
Uso en Controladores
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
private readonly IUnitOfWork _unitOfWork;
public ProductsController(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
[HttpGet]
public async Task<IActionResult> GetProducts(
[FromQuery] int pageNumber = 1,
[FromQuery] int pageSize = 10,
[FromQuery] string? search = null)
{
var repository = _unitOfWork.CreateRepository<Product, int>();
var (items, totalCount) = string.IsNullOrEmpty(search)
? await repository.GetPagedAsync(pageNumber, pageSize)
: await repository.GetPagedAsync(
pageNumber,
pageSize,
filter: p => p.Name.Contains(search),
includes: p => p.Category);
return Ok(new { items, totalCount, pageNumber, pageSize });
}
[HttpPost]
public async Task<IActionResult> CreateProduct([FromBody] CreateProductDto dto)
{
var repository = _unitOfWork.CreateRepository<Product, int>();
var product = new Product
{
Name = dto.Name,
Description = dto.Description,
Price = dto.Price,
CategoryId = dto.CategoryId,
SKU = dto.SKU,
CreatedBy = User.Identity?.Name ?? "System"
};
await repository.AddAsync(product);
await _unitOfWork.SaveChangesAsync();
return CreatedAtAction(nameof(GetProductById), new { id = product.Id }, product);
}
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteProduct(int id)
{
var repository = _unitOfWork.CreateRepository<Product, int>();
var exists = await repository.ExistsAsync(id);
if (!exists)
return NotFound();
await repository.SoftDeleteAsync(id, User.Identity?.Name ?? "System");
await _unitOfWork.SaveChangesAsync();
return NoContent();
}
}
🔐 ASP.NET Core Identity
Configuración con Identity
// Usar tu entidad personalizada de usuario
builder.Services.AddIdentityWithDataAccess<IdentityUserApp, IdentityRole, ApplicationDbContext>(
databaseConfig,
identityConfig
);
Tu Usuario Personalizado
// La librería incluye IdentityUserApp con campos adicionales
public class IdentityUserApp : IdentityUser
{
public string FullName { get; set; } = string.Empty;
public string Phone { get; set; } = string.Empty;
public string DocumentType { get; set; } = string.Empty;
public string DocumentNumber { get; set; } = string.Empty;
public string Token { get; set; } = string.Empty;
// + Propiedades de auditoría estándar
}
🗂️ Gestión de Entidades de Dominio
public async Task SetupDomainDataAsync()
{
var domainTypeRepo = _unitOfWork.CreateRepository<DomainType, int>();
var domainValueRepo = _unitOfWork.CreateRepository<DomainValue, int>();
// Crear tipo de dominio
var productStatusType = new DomainType
{
Code = "PRODUCT_STATUS",
Name = "Estados de Producto",
Description = "Estados disponibles para productos",
CreatedBy = "System"
};
await domainTypeRepo.AddAsync(productStatusType);
await _unitOfWork.SaveChangesAsync();
// Crear valores de dominio
var statusValues = new[]
{
new DomainValue { Code = "ACTIVE", Name = "Activo", DomainTypeId = productStatusType.Id, CreatedBy = "System" },
new DomainValue { Code = "INACTIVE", Name = "Inactivo", DomainTypeId = productStatusType.Id, CreatedBy = "System" }
};
await domainValueRepo.AddRangeAsync(statusValues);
await _unitOfWork.SaveChangesAsync();
}
🔄 Transacciones
public async Task TransferProductsAsync(int fromCategoryId, int toCategoryId, string userId)
{
await _unitOfWork.BeginTransactionAsync();
try
{
var productRepo = _unitOfWork.CreateRepository<Product, int>();
var products = await productRepo.FindAsync(p => p.CategoryId == fromCategoryId);
foreach (var product in products)
{
product.CategoryId = toCategoryId;
product.UpdatedBy = userId;
await productRepo.UpdateAsync(product);
}
await _unitOfWork.SaveChangesAsync();
await _unitOfWork.CommitTransactionAsync();
}
catch
{
await _unitOfWork.RollbackTransactionAsync();
throw;
}
}
📊 Esquemas de Base de Datos
La librería organiza las tablas en esquemas lógicos:
auth
: Todas las tablas de Identity (Users, Roles, UserRoles, etc.)core
: Entidades de dominio (DomainTypes, DomainValues)dbo
: Tus entidades personalizadas (por defecto)
⚙️ Configuración Avanzada
Configuración Personalizada de Entidades
public class ApplicationDbContext : BaseDbContextWithDomainAndIdentity
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { }
public DbSet<Product> Products { get; set; }
protected override void ApplyConfigurations(ModelBuilder modelBuilder)
{
// Tus configuraciones específicas
modelBuilder.ApplyConfiguration(new ProductConfiguration());
base.ApplyConfigurations(modelBuilder);
}
}
Deshabilitar Query Filters (para ver eliminados)
public async Task<IEnumerable<Product>> GetDeletedProductsAsync()
{
var repository = _unitOfWork.CreateRepository<Product, int>();
// Temporalmente deshabilitar filtros
_unitOfWork.DisableGlobalQueryFilters();
try
{
return await repository.FindAsync(p => p.IsDeleted);
}
finally
{
_unitOfWork.EnableGlobalQueryFilters();
}
}
🛠 Troubleshooting
Problemas Comunes
- ✅ Migraciones: Ejecuta
Add-Migration
yUpdate-Database
después de cambios - ✅ Contexto: Verifica que heredas del contexto correcto según tus necesidades
- ✅ Registro de servicios: Confirma que
AddDataAccessServices<T>()
esté registrado - ✅ Auditoría: Siempre establece
CreatedBy
yUpdatedBy
en operaciones - ✅ Provider: Asegúrate de que el
DatabaseProvider
en configuración sea correcto
Validación de Configuración
// Validar configuración en startup
public static void ValidateConfiguration(IServiceProvider services)
{
using var scope = services.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
if (context.Database.CanConnect())
Console.WriteLine("✅ Conexión a base de datos exitosa");
else
throw new InvalidOperationException("❌ No se puede conectar a la base de datos");
}
📋 Requisitos
- .NET 8.0 o superior
- Microsoft.EntityFrameworkCore 8.0+
- Microsoft.AspNetCore.Identity.EntityFrameworkCore 8.0+ (si usas Identity)
- Microsoft.EntityFrameworkCore.SqlServer 8.0+ (para SQL Server)
- Npgsql.EntityFrameworkCore.PostgreSQL 8.0+ (para PostgreSQL)
- Pomelo.EntityFrameworkCore.MySql 8.0+ (para MySQL)
📄 License
Copyright © 2025 EVI. Todos los derechos reservados.
Product | Versions 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. |
-
net8.0
- Microsoft.AspNetCore.Identity (>= 2.3.1)
- Microsoft.AspNetCore.Identity.EntityFrameworkCore (>= 8.0.17)
- Microsoft.EntityFrameworkCore (>= 8.0.17)
- Microsoft.EntityFrameworkCore.Relational (>= 8.0.17)
- Microsoft.EntityFrameworkCore.SqlServer (>= 8.0.17)
- Microsoft.Extensions.Configuration.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Configuration.Binder (>= 8.0.2)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.2)
- Npgsql.EntityFrameworkCore.PostgreSQL (>= 8.0.11)
- Pomelo.EntityFrameworkCore.MySql (>= 8.0.3)
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.0.0 | 182 | 8/24/2025 |
🎉 Lanzamiento Inicial 1.0.0
✨ Características:
• Soporte multi-base de datos (SQL Server, PostgreSQL, MySQL)
• Implementación completa de patrones Repository y Unit of Work
• Integración ASP.NET Core Identity con entidad de usuario personalizada
• Eliminación lógica con funcionalidad de auditoría completa
• Entidades de dominio (DomainType/DomainValue) para gestión de catálogos
• Jerarquía de contextos flexible (Base, Dominio, Identity, Completo)
• Operaciones asíncronas comprehensivas
• Paginación integrada y consultas avanzadas
• Soporte de transacciones
• Configurable a través de appsettings.json
🏗️ Arquitectura:
• Cumplimiento Clean Architecture
• Patrones Domain-Driven Design
• Listo para Inyección de Dependencias
• Optimizado para .NET 8+
📖 Documentación completa con ejemplos y mejores prácticas