Greencore.Platform.Rules
0.0.1-alpha.2
dotnet add package Greencore.Platform.Rules --version 0.0.1-alpha.2
NuGet\Install-Package Greencore.Platform.Rules -Version 0.0.1-alpha.2
<PackageReference Include="Greencore.Platform.Rules" Version="0.0.1-alpha.2" />
<PackageVersion Include="Greencore.Platform.Rules" Version="0.0.1-alpha.2" />
<PackageReference Include="Greencore.Platform.Rules" />
paket add Greencore.Platform.Rules --version 0.0.1-alpha.2
#r "nuget: Greencore.Platform.Rules, 0.0.1-alpha.2"
#:package Greencore.Platform.Rules@0.0.1-alpha.2
#addin nuget:?package=Greencore.Platform.Rules&version=0.0.1-alpha.2&prerelease
#tool nuget:?package=Greencore.Platform.Rules&version=0.0.1-alpha.2&prerelease
Reglas técnicas
Reglas puras y reutilizables que no dependen del dominio ni de persistencia. Incluye presencia, texto, comparación, composición y colecciones.
Mejoras implementadas de navegación, composición e inclusión: plan de usabilidad, coordinado con platform.validators.
API recomendada: ámbitos e inclusión
var addressRules = Rules.For<Address>()
.Property(address => address.City, city => city.Required())
.Build();
var tree = Rules.For<Order, OrderContext>()
.Property(order => order.BillingAddress, address => address.Required().Include(addressRules))
.Property(order => order.ShippingAddress, address => address.Optional().Include(addressRules))
.EnumerableProperty(order => order.Lines)
.Required().NotEmpty()
.EachTree(line => line.Property(value => value.Quantity, quantity => quantity.Range(1, 100)))
.Root()
.Build();
Los callbacks retornan builders, no árboles; al terminar regresan al padre. Root() conserva las reglas acumuladas y dentro de un callback se refiere a la raíz local. Encadena o reasigna siempre los retornos: los builders son inmutables.
Include(tree) admite un árbol del mismo candidato/contexto y permite elevar uno local al contexto exterior. Conserva condiciones, estructura y rutas; cada inclusión y cada elemento tienen identidades de aparición independientes. No admite árboles de validación ni contextos incompatibles. Las rutas de Elements son relativas al elemento: el índice y la ruta de colección permiten proyectar Lines[2].Quantity sin prefijos duplicados.
Required, Optional, NotNull y RequiredWhen protegen el ámbito aunque se declaren después de sus reglas. Ausencia no requerida en RequiredWhen produce Skipped. NotEmpty y EachTree comparten una enumeración perezosa, dispuesta al terminar o cancelar. Los nuevos ámbitos omiten null por defecto; las invariantes explícitas de la raíz conservan su contrato anterior.
Operadores lógicos y asincronía
Se conservan &, | y !, así como And, Or y Not. No se añaden conversiones a bool ni operadores &&/||.
var positive = Rule<int>.Create(value => value > 0);
var available = Rule<int>.CreateAsync(async (value, token) =>
await service.IsAvailableAsync(value, token));
var condition = positive & available;
var tree = Rules.For<int>().Rule(condition).Build();
var result = await RuleTreeEvaluator.EvaluateAsync(tree, 5, default,
new RuleEvaluationOptions(RuleExecution.ShortCircuit, RuleDetail.Trace));
La evaluación directa síncrona sin opciones conserva el recorrido de ambas ramas. Dentro de árboles, y en EvaluateDetailed[Async] con opciones, el motor neutral gobierna los cortes: fallo en AND, éxito en OR. Las ramas omitidas mantienen estado y motivo; no se pierde evidencia al insertar una composición. Una regla async exige evaluación async. Sequence y Then permiten dependencias ordenadas; When/Unless tienen variantes de predicado local/contextual.
Consultar reglas y árboles
IsSatisfiedBy devuelve true solo cuando la evaluación termina en Satisfied; un árbol omitido devuelve false. Las reglas y los árboles async usan IsSatisfiedByAsync. Para obtener ramas, rutas y motivos de omisión, usa EvaluateDetailed o RuleTreeEvaluator.Evaluate[Async].
var condition = Rule<int>.Create(value => value > 0);
var tree = Rules.For<int>().Rule(condition).Build();
bool accepted = condition.IsSatisfiedBy(5);
bool treeAccepted = tree.IsSatisfiedBy(5);
IRule<int> reusable = tree.AsRule();
var asyncTree = Rules.For<int>().Rule(condition).RuleAsync(async (value, token) =>
await service.IsAllowedAsync(value, token)).Build();
bool asyncAccepted = await asyncTree.IsSatisfiedByAsync(5, cancellationToken);
IAsyncRule<int> asyncReusable = asyncTree.AsAsyncRule();
AsRule() rechaza un árbol async al crear el adaptador. AsAsyncRule() acepta árboles sync y async y propaga cancelación. Las reglas contextuales síncronas implementan ISynchronousContextRule<T, C>; IContextRule<T, C> conserva su contrato async por compatibilidad.
Las cadenas antiguas se mantienen: en reglas, un segundo .Property(...) selecciona desde el objeto raíz, mientras que en validación desciende al hijo. Usa callbacks para compartir el mismo estilo de navegación entre ambos proyectos.
Construcción fluida
Rules.For<T>() crea un constructor local; Rules.For<T, TContext>() añade un contexto que se recibe en cada evaluación. Cada operación fluida conserva una instantánea independiente y Build() devuelve el árbol inmutable RuleNode<T, TContext>.
var tree = Rules.For<Customer, CustomerContext>()
.Property(customer => customer.Email)
.Required()
.Email()
.Property(customer => customer.Address)
.Optional()
.Properties(address => address
.Property(value => value.City)
.Required()
.Build())
.EnumerableProperty(customer => customer.Phones)
.Each(phone => phone
.Property(value => value.Number)
.Required()
.Format("000")
.Build())
.Build();
Required() rechaza ausencia y, para cadenas, texto vacío o en blanco; las reglas posteriores dependen de su éxito. Optional() omite las reglas posteriores cuando el valor está ausente. Si no se declara una regla de presencia, las reglas normales omiten null. NotNull() solo comprueba null; NotEmpty() comprueba contenido de cadenas o colecciones.
Property(...) selecciona una propiedad del objeto del ámbito actual y Field(...) selecciona un campo. Properties(...) abre un objeto anidado y devuelve un árbol de reglas para ese tipo. EnumerableProperty(...) selecciona una colección; Each(...) acepta reglas compactas síncronas o un árbol completo por elemento, que puede usar el contexto y reglas asíncronas. Los fallos de elemento guardan índice y evaluación anidada como evidencia; sus rutas incluyen el índice, por ejemplo Phones[2].Number.
Desde un builder de propiedad o colección, Root() vuelve al builder del objeto raíz sin descartar las reglas ya configuradas:
var tree = Rules.For<Order, OrderContext>()
.Property(order => order.Quantity)
.Rule(quantity => quantity > 0)
.Root()
.Rule((order, context) => order.Total <= context.MaximumTotal)
.Build();
El constructor raíz admite Rule(...) para invariantes que comparan varios miembros del objeto, con una variante contextual y otra asíncrona:
var tree = Rules.For<Customer, CustomerContext>()
.Rule(customer => customer.Email != customer.Address)
.Rule((customer, context) => customer.Quantity <= context.MaximumQuantity)
.RuleAsync(async (customer, context, token) => await context.IsAllowedAsync(customer.Email, token))
.Build();
Las extensiones Email(), Format(...) y Matches(...) solo están disponibles para valores string. En una máscara, cada 0 representa un dígito ASCII obligatorio. platform.rules contiene el modelo neutral y su evaluación; platform.validators ofrece el árbol de validación y sus diagnósticos de aplicación.
Los operadores de composición también se pueden declarar desde el constructor fluido; cada rama recibe un constructor independiente y devuelve su árbol:
var tree = Rules.For<Customer, CustomerContext>()
.AnyOf(
branch => branch.Property(value => value.Email).Rule((email, context) => context.IsAllowed(email)).Build(),
branch => branch.Property(value => value.Phone).Required().Build())
.When(
condition => condition.Property(value => value.IsBusiness).Rule(value => value).Build(),
dependent => dependent.Property(value => value.TaxId).Required().Build())
.Build();
AllOf(...) exige todas las ramas, AnyOf(...) exige al menos una, Not(...) invierte una rama, When(...) evalúa la dependiente solo cuando se satisface la condición, y Require(...) la declara dependiente obligatoria de un prerrequisito.
Estados de evaluación
La evaluación devuelve uno de tres estados: Satisfied, Unsatisfied o Skipped. Una selección opcional ausente queda omitida y no cuenta como una alternativa satisfecha en AnyOf. When omite su dependiente cuando la condición no se cumple; Require conserva el incumplimiento del prerrequisito y marca la rama dependiente como omitida.
RuleEvaluationOptions controla la ejecución de grupos con Accumulate o ShortCircuit y el detalle con Minimal o Trace. Las opciones se propagan a los subárboles de Each. La evaluación síncrona rechaza árboles que contienen reglas asíncronas; EvaluateAsync propaga el contexto y el token de cancelación.
Presencia contextual y comparaciones
RequiredWhen((candidate, context) => ...) exige que una selección tenga valor solo cuando la condición contextual es verdadera. Cuando la condición es falsa, un valor presente continúa sujeto a las reglas posteriores.
Las reglas reutilizables se pueden aplicar directamente a una propiedad:
var tree = Rules.For<Order, OrderContext>()
.Property(order => order.Quantity)
.Range(1, 100)
.Property(order => order.Code)
.RequiredWhen((order, context) => context.RequiresCode)
.Build();
Las reglas del mismo tipo pueden componerse con And/Or/Not o con los operadores &, | y !. El contexto, cuando existe, se mantiene al recorrer todo el árbol:
var validQuantity = Rule<int, OrderContext>.Create((quantity, context) => quantity >= context.MinimumQuantity);
var boundedQuantity = validQuantity & Rule<int, OrderContext>.Create((quantity, context) => quantity <= context.MaximumQuantity);
var tree = Rules.For<Order, OrderContext>()
.Property(order => order.Quantity)
.Rule(boundedQuantity & !validQuantity)
.Build();
Range es inclusivo. GreaterThan y LessThan reutilizan las reglas de comparación existentes. Los límites invertidos se rechazan al configurar el árbol.
Colecciones y cancelación
Each conserva los fallos por índice y compone las rutas de las colecciones anidadas. Un árbol de elemento completamente síncrono puede evaluarse con Evaluate; si contiene una regla asíncrona se debe usar EvaluateAsync. La enumeración asíncrona comprueba la cancelación entre elementos y libera el enumerador incluso cuando ocurre una excepción o cancelación.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. 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. |
-
net10.0
- No dependencies.
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Greencore.Platform.Rules:
| Package | Downloads |
|---|---|
|
Greencore.Platform.Validators
Resultados explícitos, errores estructurados, validación y concurrencia asíncrona para .NET. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.0.1-alpha.2 | 39 | 9/26/2026 |
Siguiente versión alpha.2 de Greencore.