eQuantic LINQ Extensions
Advanced LINQ extensions and query utilities
Version: 2.1.0
Downloads: 46K+
Status: stable
Category: utilities
Installation
dotnet add package eQuantic.Linq
Overview
eQuantic.Linq es una librería moderna y poderosa que extiende las capacidades de LINQ para aplicaciones .NET, proporcionando herramientas avanzadas para consultas de datos, filtrado, ordenamiento y composición de lógica de negocio. La librería implementa patrones de diseño sofisticados como el Patrón de Especificación y los patrones Entity Filter/Sorter, permitiendo a los desarrolladores crear código mantenible y testeable.
Esta librería soporta tanto implementaciones .NET (C#) como TypeScript, manteniendo paridad de características entre ambas plataformas. Incluye características avanzadas como generadores de código fuente para generación de código en tiempo de compilación, caché de expresiones para optimización de rendimiento, soporte asíncrono para aplicaciones modernas, y un sistema completo de casting type-safe.
eQuantic.Linq está diseñado con aplicaciones empresariales en mente, proporcionando soluciones robustas para operaciones de datos complejas mientras mantiene principios de arquitectura limpia. Permite la separación de responsabilidades delegando la lógica de filtrado y ordenamiento desde la capa de presentación a la capa de servicio, promoviendo una mejor organización del código y testabilidad.
Quick Start
1. Instalación Básica y Configuración
Instale el paquete eQuantic.Linq en su proyecto .NET y configure las dependencias necesarias. El paquete soporta múltiples versiones de .NET incluyendo .NET Standard 2.1, .NET 6, 7, y 8.
// Instalar vía Package Manager Console
Install-Package eQuantic.Linq
// O vía .NET CLI
dotnet add package eQuantic.Linq
// Declaraciones using básicas
using eQuantic.Linq.Specification;
using eQuantic.Linq.Filter;
using eQuantic.Linq.Sorter;
using eQuantic.Linq.Extensions;2. Creando y Usando Especificaciones
Las especificaciones le permiten encapsular reglas de negocio en componentes reutilizables. Puede crear especificaciones personalizadas heredando de la clase base Specification<T> y combinarlas usando operadores lógicos.
// Implementación de especificación personalizada
public class ActiveUserSpecification : Specification<User>
{
public override Expression<Func<User, bool>> SatisfiedBy()
{
return user => user.IsActive && user.EmailVerified;
}
}
// Usando especificaciones con operadores lógicos
var activeAdminSpec = new ActiveUserSpecification() &&
new AdminUserSpecification();
// Aplicando especificaciones a consultas
var activeAdminUsers = users.Where(activeAdminSpec.SatisfiedBy());
// Uso directo de especificaciones
var isActiveAdmin = activeAdminSpec.IsSatisfiedBy(user);3. Implementando Entity Filters para Arquitectura Limpia
Los filtros de entidad proporcionan una forma limpia de separar la lógica de filtrado de sus controladores. Permiten pasar criterios de filtrado complejos desde la capa de presentación a la capa de servicio sin acoplamiento.
// Método de capa de servicio con filtro de entidad
public async Task<List<Product>> GetProductsAsync(IEntityFilter<Product> filter)
{
var query = dbContext.Products.AsQueryable();
if (filter != null)
{
query = filter.Filter(query);
}
return await query.ToListAsync();
}
// Uso en controlador con API fluida
var filter = EntityFilter<Product>
.Where(p => p.Category == "Electrónicos")
.And(p => p.Price > 100)
.And(p => p.InStock);
var products = await productService.GetProductsAsync(filter);
// Filtrado complejo con condiciones anidadas
var complexFilter = EntityFilter<Product>
.Where(p => p.Category == "Electrónicos" || p.Category == "Computadoras")
.And(p => p.Price >= minPrice && p.Price <= maxPrice)
.And(p => p.Reviews.Any(r => r.Rating >= 4));4. Usando Entity Sorters para Ordenamiento Flexible
Los ordenadores de entidad proporcionan una forma flexible de manejar lógica de ordenamiento, permitiendo definir criterios de ordenamiento complejos que pueden modificarse y probarse fácilmente. Soportan ordenamiento multi-columna y expresiones de ordenamiento personalizadas.
// Método de capa de servicio con ordenador de entidad
public async Task<List<Product>> GetSortedProductsAsync(IEntitySorter<Product> sorter)
{
var query = dbContext.Products.AsQueryable();
if (sorter != null)
{
query = sorter.Sort(query);
}
return await query.ToListAsync();
}
// Creando ordenamiento multi-nivel
var sorter = EntitySorter<Product>
.OrderByDescending(p => p.Featured)
.ThenBy(p => p.Price)
.ThenByDescending(p => p.CreatedDate);
var sortedProducts = await productService.GetSortedProductsAsync(sorter);
// Ordenamiento dinámico basado en entrada del usuario
var dynamicSorter = EntitySorter<Product>.OrderBy(p => p.Name);
if (sortByPrice)
{
dynamicSorter = dynamicSorter.ThenBy(p => p.Price);
}
if (sortByRating)
{
dynamicSorter = dynamicSorter.ThenByDescending(p => p.AverageRating);
}5. Casting Type-safe con FluentCastBuilder
El sistema de casting permite transformar objetos de forma segura entre diferentes tipos con reglas de mapeo personalizables. Soporta mapeo automático de propiedades, transformaciones personalizadas y validación.
// Casting básico con mapeo automático de propiedades
var productDto = product.Cast<ProductDto>();
// Configuración de mapeo personalizada
var productDto = product.Cast<ProductDto>(options =>
{
options.Map(src => src.ProductName, dest => dest.Name);
options.Map(src => src.UnitPrice, dest => dest.Price);
options.CustomMap(src => src.Category.Name, dest => dest.CategoryName);
options.ExcludeUnmapped();
});
// Casting de colecciones
var productDtos = products.Cast<ProductDto>(options =>
{
options.Map(src => src.ProductName, dest => dest.Name);
options.Map(src => src.CreatedDate, dest => dest.CreatedAt);
});
// Mapeo complejo con objetos anidados
var orderDto = order.Cast<OrderDto>(options =>
{
options.Map(src => src.Customer.FullName, dest => dest.CustomerName);
options.Map(src => src.OrderItems.Sum(oi => oi.Total), dest => dest.TotalAmount);
options.CustomMap(src => src.OrderDate.ToString("yyyy-MM-dd"), dest => dest.FormattedDate);
});6. Generadores de Código Fuente para Generación Automática de Código
Use generadores de código fuente para crear automáticamente especificaciones, filtros y clases de ordenamiento en tiempo de compilación. Esto reduce código repetitivo y mejora el rendimiento eliminando reflexión.
// Modelo con atributos de generador de código fuente
[GenerateSpecifications]
public class Product
{
public int Id { get; set; }
[SpecProperty(FilterOperator.Equal | FilterOperator.Contains)]
public string Name { get; set; }
[SpecProperty(FilterOperator.Equal | FilterOperator.GreaterThan | FilterOperator.LessThan)]
public decimal Price { get; set; }
[SpecProperty(FilterOperator.Equal)]
public bool InStock { get; set; }
[SpecProperty(FilterOperator.Equal | FilterOperator.Contains)]
public string Category { get; set; }
}
// Uso de código generado (creado automáticamente por el generador de código fuente)
var nameSpec = ProductSpecifications.NameContains("laptop");
var priceSpec = ProductSpecifications.PriceGreaterThan(500m);
var categorySpec = ProductSpecifications.CategoryEqual("Electrónicos");
// Combinar especificaciones generadas
var combinedSpec = nameSpec && priceSpec && categorySpec;
var filteredProducts = products.Where(combinedSpec.SatisfiedBy());7. Operaciones Asíncronas Avanzadas
Aproveche el soporte asíncrono para operaciones no bloqueantes en aplicaciones modernas. La librería proporciona versiones asíncronas de todas las operaciones principales para mejor escalabilidad.
// Uso de filtro de entidad asíncrono
public class ProductService
{
public async Task<PagedResult<Product>> GetProductsAsync(
IAsyncEntityFilter<Product> filter,
int page = 1,
int pageSize = 10)
{
var query = dbContext.Products.AsQueryable();
if (filter != null)
{
query = await filter.FilterAsync(query);
}
var totalCount = await query.CountAsync();
var items = await query
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToListAsync();
return new PagedResult<Product>
{
Items = items,
TotalCount = totalCount,
CurrentPage = page,
PageSize = pageSize
};
}
}
// Evaluación asíncrona de especificaciones
var activeUserSpec = new ActiveUserSpecification();
var user = await dbContext.Users.FirstOrDefaultAsync(activeUserSpec.SatisfiedBy());8. Filtrado de Colecciones (Any/All) - v2.1+ Consultas Avanzadas en Colecciones
La funcionalidad Any/All (nueva en v2.1+) revoluciona cómo trabajas con colecciones en tus consultas. Imagina escenarios donde necesitas filtrar usuarios que tienen CUALQUIER rol de administrador, o encontrar proyectos donde TODAS las tareas estén completas. Esta característica trae el poder de LINQ Any() y All() a tus consultas dinámicas, permitiendo expresar condiciones complejas en relaciones uno-a-muchos de manera type-safe y eficiente.
// ===== MODELOS DE EJEMPLO =====
// Estructuras típicas de aplicaciones del mundo real con relaciones
public class User
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public List<Role> Roles { get; set; } = new();
public List<Project> Projects { get; set; } = new();
}
public class Role
{
public string Name { get; set; } = string.Empty;
public bool IsActive { get; set; }
public List<string> Permissions { get; set; } = new();
}
public class Project
{
public string Name { get; set; } = string.Empty;
public string Status { get; set; } = string.Empty;
public int Priority { get; set; }
public bool IsCompleted { get; set; }
public DateTime AssignedAt { get; set; }
}
// ===== ANY - CUALQUIER ELEMENTO SATISFACE LA CONDICIÓN =====
// Escenario 1: Usuarios con CUALQUIER rol administrativo
// Útil para sistemas de autorización
var adminOrManagerUsers = new CompositeFiltering<User>(
CompositeOperator.Any,
u => u.Roles,
"Name:eq(Admin)",
"Name:eq(Manager)",
"Name:eq(SuperAdmin)"
);
// Escenario 2: Usuarios con CUALQUIER proyecto de alta prioridad
// Ideal para paneles de gestión
var highPriorityUsers = new CompositeFiltering<User>(
CompositeOperator.Any,
u => u.Projects,
"Priority:gte(8)",
"Status:eq(Critical)"
);
// ===== ALL - TODOS LOS ELEMENTOS DEBEN SATISFACER =====
// Escenario 3: Usuarios donde TODOS los proyectos están completos
// Perfecto para informes de productividad
var usersWithCompletedProjects = new CompositeFiltering<User>(
CompositeOperator.All,
u => u.Projects,
"Status:eq(Completed)",
"IsCompleted:eq(true)"
);
// Escenario 4: Usuarios donde TODOS los roles están activos
// Importante para auditoría de seguridad
var usersWithActiveRoles = new CompositeFiltering<User>(
CompositeOperator.All,
u => u.Roles,
"IsActive:eq(true)"
);
// ===== CONSULTAS COMPLEJAS CON CONDICIONES ANIDADAS =====
// Escenario 5: Usuarios con cualquier rol que tenga permisos de escritura
var writePermissionUsers = new CompositeFiltering<User>(
CompositeOperator.Any,
u => u.Roles,
"Permissions:ct(write)",
"Permissions:ct(admin)",
"and(Name:eq(Editor),IsActive:eq(true))"
);
// Escenario 6: Usuarios con todos los proyectos activos y de alta prioridad
var qualityFocusedUsers = new CompositeFiltering<User>(
CompositeOperator.All,
u => u.Projects,
"Status:neq(Cancelled)",
"Priority:gte(5)",
"AssignedAt:gte(2024-01-01)"
);
// ===== APLICACIÓN PRÁCTICA EN SERVICIOS =====
public class UserManagementService
{
private readonly IRepository<User> _userRepository;
/// <summary>
/// Busca administradores activos del sistema
/// </summary>
public async Task<List<User>> GetActiveAdministratorsAsync()
{
var adminFilter = new CompositeFiltering<User>(
CompositeOperator.Any,
u => u.Roles,
"Name:eq(Admin)",
"Name:eq(SuperAdmin)",
"and(Name:eq(Manager),IsActive:eq(true))"
);
return await adminFilter.Filter(_userRepository.Query())
.Where(u => u.IsActive)
.ToListAsync();
}
/// <summary>
/// Busca usuarios que completaron todos sus proyectos (para bonos)
/// </summary>
public async Task<List<User>> GetTopPerformersAsync()
{
var completedProjectsFilter = new CompositeFiltering<User>(
CompositeOperator.All,
u => u.Projects,
"Status:eq(Completed)",
"IsCompleted:eq(true)"
);
return await completedProjectsFilter.Filter(_userRepository.Query())
.Where(u => u.Projects.Count > 0) // Solo usuarios con proyectos
.Include(u => u.Projects)
.ToListAsync();
}
}
// ===== FORMATO DE CONSULTA STRING - ANÁLISIS DINÁMICO =====
// La funcionalidad soporta análisis de consultas en string para APIs dinámicas
// Formato: "Propiedad:operador(condiciones)"
// Análisis de consulta Any
var anyRoleFilter = CompositeFiltering.ParseComposite(
"Roles:any(Name:eq(Admin),IsActive:eq(true),Permissions:ct(write))"
);
// Análisis de consulta All
var allProjectFilter = CompositeFiltering.ParseComposite(
"Projects:all(Status:neq(Cancelled),Priority:gte(5),IsCompleted:eq(false))"
);
// ===== INTEGRACIÓN CON OPERACIONES ASÍNCRONAS =====
// Funciona perfectamente con operaciones asíncronas
var users = await Query.For<User>()
.Where(u => u.IsActive)
.Where(adminOrManagerUsers) // Aplica filtro de colección
.OrderByDescending(u => u.LastLoginAt)
.ApplyToAsync(dbContext.Users, cancellationToken);
// Procesamiento de streams para grandes volúmenes
await foreach (var user in Query.For<User>()
.Where(u => u.CreatedAt > DateTime.Now.AddYears(-1))
.Where(usersWithCompletedProjects)
.ApplyToAsyncEnumerable(dbContext.Users, cancellationToken))
{
await ProcessUserAsync(user);
}9. Sistema CastWith() Moderno - v2.0+
Esta es una de las funcionalidades más revolucionarias de la versión 2.0+. El método CastWith() con FluentCastBuilder permite transformar filtros, sortings y objetos entre diferentes modelos con rendimiento optimizado a través de caché de expresiones. Esto es especialmente útil en arquitecturas de microservicios, APIs públicas e integraciones donde recibes datos en un formato y necesitas aplicarlos a otro modelo.
// ===== CASTING DE FILTROS =====
// Transformar filtros genéricos (de APIs externas, frontend, etc.)
// en filtros type-safe para tus entidades
var dtoFilters = new IFiltering[]
{
new Filtering("productName", "Laptop", FilterOperator.Contains),
new Filtering("minPrice", "500", FilterOperator.GreaterThan),
new Filtering("category", "Electrónicos", FilterOperator.Equals),
new Filtering("isAvailable", "true", FilterOperator.Equals)
};
// CastWith() realiza transformación type-safe con FluentCastBuilder
var entityFilters = dtoFilters.CastWith<Product>(builder => builder
.MapFilter("productName", p => p.Name) // Mapea "productName" -> Product.Name
.MapFilter("minPrice", p => p.Price, value => decimal.Parse(value)) // + conversión
.MapFilter("category", p => p.Category)
.MapFilter("isAvailable", p => p.IsActive, value => bool.Parse(value))
.ExcludeUnmapped()); // Ignora filtros no mapeados (seguridad)
// Aplica los filtros transformados
var products = await repository.GetAsync(entityFilters.ToSpecification());
// ===== CASTING DE SORTING =====
// Mismo concepto para ordenamiento
var dtoSortings = new ISorting[]
{
new Sorting("displayName", SortDirection.Ascending),
new Sorting("createdDate", SortDirection.Descending)
};
var entitySortings = dtoSortings.CastWith<Product>(builder => builder
.MapSorting("displayName", p => p.Name)
.MapSorting("createdDate", p => p.CreatedAt));
// ===== CASTING DE OBJETOS SIMPLES =====
// Transformar DTOs a entidades o viceversa
var productDto = new ProductDto
{
ProductName = "Gaming Laptop",
UnitPrice = 1299.99m,
CategoryName = "Electrónicos",
Available = true
};
// CastWith() para objetos simples también
var product = productDto.CastWith<Product>(builder => builder
.Map(dto => dto.ProductName, entity => entity.Name)
.Map(dto => dto.UnitPrice, entity => entity.Price)
.Map(dto => dto.Available, entity => entity.IsActive)
.CustomMap(dto => dto.CategoryName, entity => entity.Category,
categoryName => new Category { Name = categoryName })
.ExcludeUnmapped());
// ===== BENEFICIOS DE CASTWITH() =====
// 1. RENDIMIENTO: Expresiones se almacenan automáticamente en caché
// 2. SEGURIDAD DE TIPOS: Errores detectados en tiempo de compilación
// 3. EFICIENCIA DE MEMORIA: Menor asignación de memoria
// 4. API INTUITIVA: Interfaz fluida y descubrible
// ===== COMPARANDO CON MÉTODO TRADICIONAL =====
// El método Cast() tradicional aún funciona (retrocompatibilidad)
var productLegacy = productDto.Cast<Product>(options =>
{
options.Map(dto => dto.ProductName, entity => entity.Name);
options.Map(dto => dto.UnitPrice, entity => entity.Price);
options.ExcludeUnmapped();
});
// Pero CastWith() ofrece mejor rendimiento y API más limpiaAPI Reference
ISpecification<T>
Interfaz central para implementar el Patrón de Especificación. Proporciona métodos para componer reglas de negocio que pueden combinarse y reutilizarse en toda su aplicación.
public class ExpensiveProductSpecification : ISpecification<Product>
{
public Expression<Func<Product, bool>> SatisfiedBy()
{
return product => product.Price > 1000;
}
public bool IsSatisfiedBy(Product entity)
{
return entity.Price > 1000;
}
}Specification<T>
Clase base abstracta para crear especificaciones personalizadas. Proporciona soporte integrado para operaciones lógicas (AND, OR, NOT) y métodos de composición para construir reglas de negocio complejas.
public class ActiveUserSpecification : Specification<User>
{
public override Expression<Func<User, bool>> SatisfiedBy()
{
return user => user.IsActive && !user.IsDeleted;
}
}
// Uso con operadores lógicos
var activeAdminSpec = new ActiveUserSpecification() && new AdminUserSpecification();
var result = activeAdminSpec.IsSatisfiedBy(user);IEntityFilter<TEntity>
Interfaz para implementar lógica de filtrado de entidades. Permite separación limpia entre lógica de presentación y negocio al permitir que controladores pasen criterios de filtrado a métodos de servicio.
public async Task<List<Product>> GetFilteredProductsAsync(IEntityFilter<Product> filter)
{
var query = dbContext.Products.AsQueryable();
if (filter != null)
{
query = filter.Filter(query);
}
return await query.ToListAsync();
}EntityFilter<TEntity>
Implementación concreta de IEntityFilter<TEntity> que proporciona una API fluida para construir expresiones de filtrado complejas. Soporta encadenamiento de métodos y operaciones lógicas para crear consultas sofisticadas.
// API fluida para filtrado complejo
var filter = EntityFilter<Product>
.Where(p => p.Category == "Electrónicos")
.And(p => p.Price >= 100 && p.Price <= 1000)
.And(p => p.InStock)
.And(p => p.Reviews.Any(r => r.Rating >= 4));
var products = await productService.GetProductsAsync(filter);IEntitySorter<TEntity>
Interfaz para implementar lógica de ordenamiento de entidades. Proporciona una forma limpia de manejar operaciones de ordenamiento mientras mantiene separación de responsabilidades entre capas de presentación y negocio.
public async Task<List<Product>> GetSortedProductsAsync(IEntitySorter<Product> sorter)
{
var query = dbContext.Products.AsQueryable();
if (sorter != null)
{
query = sorter.Sort(query);
}
return await query.ToListAsync();
}EntitySorter<TEntity>
Implementación concreta de IEntitySorter<TEntity> que proporciona una API fluida para construir expresiones de ordenamiento complejas. Soporta ordenamiento multi-nivel con operaciones OrderBy, ThenBy y lógica de ordenamiento personalizada.
// Ordenamiento multi-nivel con API fluida
var sorter = EntitySorter<Product>
.OrderByDescending(p => p.Featured)
.ThenBy(p => p.Category)
.ThenByDescending(p => p.Price)
.ThenBy(p => p.Name);
var sortedProducts = await productService.GetSortedProductsAsync(sorter);FluentCastBuilder<TSource, TDestination>
Sistema de casting moderno (v2.0+) que proporciona transformación type-safe con rendimiento optimizado, caché de expresiones y API fluida intuitiva. Reemplaza el sistema de casting tradicional con mejoras significativas de rendimiento y seguridad de tipos.
// Uso moderno con CastWith() y FluentCastBuilder (v2.0+)
var orderDto = order.CastWith<OrderDto>(builder => builder
.Map(src => src.Customer.FullName, dest => dest.CustomerName)
.Map(src => src.CreatedDate, dest => dest.OrderDate)
.CustomMap(src => src.OrderItems.Sum(oi => oi.Total), dest => dest.TotalAmount)
.ExcludeUnmapped());
// Casting de filtros - nueva funcionalidad
var entityFilters = dtoFilters.CastWith<User>(builder => builder
.MapFilter("name", u => u.FullName)
.MapFilter("age", u => u.Age, value => int.Parse(value))
.ExcludeUnmapped());
// Casting de sorting - nueva funcionalidad
var entitySortings = dtoSortings.CastWith<User>(builder => builder
.MapSorting("created", u => u.CreatedDate)
.MapSorting("displayName", u => u.FullName));IExpressionCache
Interfaz para cachear expresiones lambda compiladas para mejorar el rendimiento en escenarios con operaciones de consulta repetidas. Almacena expresiones en memoria y proporciona mecanismos de recuperación eficientes.
// Caché de expresiones para rendimiento
public class ProductRepository
{
private readonly IExpressionCache _expressionCache;
public IQueryable<Product> GetActiveProducts()
{
var cachedExpression = _expressionCache.GetOrAdd(
"ActiveProducts",
() => (Expression<Func<Product, bool>>)(p => p.IsActive));
return dbContext.Products.Where(cachedExpression);
}
}GenerateSpecificationsAttribute
Atributo de generador de código fuente que crea automáticamente clases de especificación en tiempo de compilación. Reduce código repetitivo y mejora el rendimiento eliminando operaciones basadas en reflexión.
[GenerateSpecifications]
public class User
{
[SpecProperty(FilterOperator.Equal | FilterOperator.Contains)]
public string Name { get; set; }
[SpecProperty(FilterOperator.Equal)]
public bool IsActive { get; set; }
}
// Uso de especificaciones generadas
var nameSpec = UserSpecifications.NameContains("Juan");
var activeSpec = UserSpecifications.IsActiveEqual(true);
var combinedSpec = nameSpec && activeSpec;SpecPropertyAttribute
Atributo usado con generadores de código fuente para especificar qué operaciones de filtro deben generarse para una propiedad. Soporta varios operadores de filtro incluyendo Equal, Contains, GreaterThan, LessThan, y más.
public class Product
{
[SpecProperty(FilterOperator.Equal | FilterOperator.Contains)]
public string Name { get; set; }
[SpecProperty(FilterOperator.GreaterThan | FilterOperator.LessThan | FilterOperator.Equal)]
public decimal Price { get; set; }
[SpecProperty(FilterOperator.Equal)]
public ProductCategory Category { get; set; }
}FilterOperator
Enumeración que define las operaciones de filtro disponibles para atributos de generador de código fuente. Soporta combinaciones bitwise para especificar múltiples operaciones para una sola propiedad.
// Operadores de filtro disponibles
FilterOperator.Equal // Genera comparaciones de igualdad
FilterOperator.NotEqual // Genera comparaciones de desigualdad
FilterOperator.Contains // Genera operaciones de string contains
FilterOperator.StartsWith // Genera operaciones de string starts with
FilterOperator.EndsWith // Genera operaciones de string ends with
FilterOperator.GreaterThan // Genera comparaciones mayor que
FilterOperator.LessThan // Genera comparaciones menor que
FilterOperator.GreaterEqual // Genera comparaciones mayor o igual que
FilterOperator.LessEqual // Genera comparaciones menor o igual queIAsyncEntityFilter<TEntity>
Versión asíncrona de IEntityFilter que proporciona operaciones de filtrado no bloqueantes. Esencial para aplicaciones modernas que requieren patrones de acceso a datos escalables e interfaces de usuario responsivas.
public class AsyncProductService
{
public async Task<PagedResult<Product>> SearchProductsAsync(
IAsyncEntityFilter<Product> filter,
CancellationToken cancellationToken = default)
{
var query = dbContext.Products.AsQueryable();
if (filter != null)
{
query = await filter.FilterAsync(query, cancellationToken);
}
return await query.ToPagedResultAsync(cancellationToken);
}
}IAsyncEntitySorter<TEntity>
Versión asíncrona de IEntitySorter que proporciona operaciones de ordenamiento no bloqueantes. Permite ordenamiento eficiente de datos en aplicaciones de alto rendimiento sin bloquear el hilo principal de ejecución.
public class AsyncProductService
{
public async Task<List<Product>> GetSortedProductsAsync(
IAsyncEntitySorter<Product> sorter,
CancellationToken cancellationToken = default)
{
var query = dbContext.Products.AsQueryable();
if (sorter != null)
{
query = await sorter.SortAsync(query, cancellationToken);
}
return await query.ToListAsync(cancellationToken);
}
}CastWith<T>()
Método moderno (v2.0+) para casting type-safe entre diferentes tipos con rendimiento optimizado a través de caché de expresiones. Reemplaza el método Cast() tradicional ofreciendo mejor API fluida, mayor seguridad de tipos y capacidad de transformar filtros, sortings y objetos. Utiliza FluentCastBuilder para configurar mapeos.
// Casting de filtros con CastWith
var entityFilters = dtoFilters.CastWith<Product>(builder => builder
.MapFilter("name", p => p.FullName)
.MapFilter("price", p => p.UnitPrice, value => decimal.Parse(value))
.ExcludeUnmapped());
// Casting de objetos simples
var product = dto.CastWith<Product>(builder => builder
.Map(src => src.Name, dest => dest.FullName)
.Map(src => src.Price, dest => dest.UnitPrice)
.CustomMap(src => src.CategoryName, dest => dest.Category,
name => new Category { Name = name }));
// Casting de sorting
var entitySortings = dtoSortings.CastWith<Product>(builder => builder
.MapSorting("created", p => p.CreatedAt)
.MapSorting("name", p => p.DisplayName));CompositeOperator
Enumera los operadores compuestos disponibles para combinar múltiples condiciones de filtro o aplicar filtros a colecciones. La versión 2.1+ introdujo los operadores Any y All para consultas avanzadas en relaciones uno-a-muchos.
// Operadores lógicos regulares
CompositeOperator.And // Todas las condiciones deben ser verdaderas
CompositeOperator.Or // Al menos una condición debe ser verdadera
// Operadores para colecciones (v2.1+)
CompositeOperator.Any // Cualquier elemento de la colección satisface las condiciones
CompositeOperator.All // Todos los elementos de la colección satisfacen las condiciones
// Ejemplo de uso
var adminUsers = new CompositeFiltering<User>(
CompositeOperator.Any, // Cualquier rol del usuario
u => u.Roles, // En la colección Roles
"Name:eq(Admin)", // Que tenga nombre igual a Admin
"Name:eq(SuperAdmin)" // O nombre igual a SuperAdmin
);CompositeFiltering<T>
Clase que implementa filtros compuestos para combinar múltiples condiciones usando operadores lógicos (And/Or) y operadores de colección (Any/All v2.1+). Permite crear consultas complejas de manera type-safe con soporte para parsing de cadenas e integración con Entity Framework.
// Filtro con operador And (todas las condiciones)
var activeProducts = CompositeFiltering.and<Product>(
"IsActive:eq(true)",
"Price:gt(0)",
"Category:neq(Discontinued)"
);
// Filtro con operador Or (cualquier condición)
var discountedProducts = CompositeFiltering.or<Product>(
"IsOnSale:eq(true)",
"Price:lt(50)"
);
// Filtro Any en colecciones (v2.1+) - usuarios con cualquier rol de admin
var adminUsers = new CompositeFiltering<User>(
CompositeOperator.Any,
u => u.Roles,
"Name:eq(Admin)",
"Name:eq(Manager)"
);
// Filtro All en colecciones (v2.1+) - usuarios con todos los proyectos completos
var completedUsers = new CompositeFiltering<User>(
CompositeOperator.All,
u => u.Projects,
"Status:eq(Completed)",
"IsActive:eq(false)"
);
// Parsing de consulta string
var parsedFilter = CompositeFiltering.ParseComposite(
"Roles:any(Name:eq(Admin),IsActive:eq(true))"
);
// Aplicación en consultas
var users = adminUsers.Filter(dbContext.Users).ToList();Key Features
Generadores de Código Fuente para Generación en Tiempo de Compilación
Genera automáticamente especificaciones, filtros y clases de ordenamiento en tiempo de compilación usando el GenerateSpe...
Implementación Avanzada del Patrón de Especificación
Proporciona una implementación completa del Patrón de Especificación con especificaciones compuestas, permitiendo crear ...
Patrones Entity Filter y Sorter
Implementa patrones sofisticados de filtrado y ordenamiento que permiten una separación limpia entre lógica de presentac...
Sistema CastWith() Moderno - Transformación Type-safe v2.0+
La versión 2.0+ introdujo un sistema de casting revolucionario con el método CastWith() y FluentCastBuilder. Este nuevo ...
Caché de Expresiones v2.0+ - Rendimiento Revolucionario
El sistema de caché de expresiones fue completamente rediseñado en v2.0+ con uso de ConcurrentDictionary y optimizacione...
Soporte Asíncrono para Aplicaciones Modernas
Proporciona soporte asíncrono completo con interfaces IAsyncEntityFilter y IAsyncEntitySorter, habilitando operaciones d...