CHR0020: Constraint expression lambda must only access members
Rule Description
Section titled “Rule Description”Expression lambdas passed to constraint builder methods (e.g. On<TEvent>(e => ...)) must only contain simple member access. They cannot contain method calls, arithmetic, conditional expressions, or any other executable code.
Severity
Section titled “Severity”Error
Example
Section titled “Example”Violation
Section titled “Violation”using Cratis.Chronicle.Events;using Cratis.Chronicle.Events.Constraints;
[EventType]public record Chr0020OrderPlaced(string OrderId);
public class Chr0020OrderConstraint : IConstraint{ public void Define(IConstraintBuilder builder) => builder.Unique(u => u.On<Chr0020OrderPlaced>(e => e.OrderId.ToLower())); // CHR0020: method call - never executed}using Cratis.Chronicle.Events;using Cratis.Chronicle.Events.Constraints;
[EventType]public record Chr0020OrderPlacedFixed(string OrderId);
public class Chr0020OrderConstraintFixed : IConstraint{ public void Define(IConstraintBuilder builder) => builder.Unique(u => u.On<Chr0020OrderPlacedFixed>(e => e.OrderId)); // Now valid - simple member access}Why This Rule Exists
Section titled “Why This Rule Exists”In Chronicle’s constraint builder, any parameter typed as Expression<Func<...>> is parsed at startup to extract a property name for the constraint rule—the lambda body is never executed at runtime. Writing method calls, arithmetic, conditional expressions, or similar executable code in such a lambda will be silently ignored, producing an incorrect or incomplete constraint definition.
The only valid lambda body is a direct property access, for example e => e.Email or e => e.OrderId.
If case-insensitive comparison is needed, use builder.Unique(u => u.On<OrderPlaced>(e => e.Email).IgnoreCasing()) instead of transforming the value in the lambda.