Skip to content

CHR0019: Projection expression lambda must only access members

Expression lambdas passed to projection builder methods (e.g. Set(x => ...), To(e => ...), IdentifiedBy(x => ...), UsingKey(e => ...)) must only contain simple member access. They cannot contain method calls, arithmetic, conditional expressions, or any other executable code.

Error

using Cratis.Chronicle.Projections;
public class UserReadModel { public string Name { get; set; } }
public class UserRegistered { public string Name { get; set; } }
public class UserProjection : IProjectionFor<UserReadModel>
{
public void Define(IProjectionBuilderFor<UserReadModel> builder) =>
builder.From<UserRegistered>(evt => evt
.Set(x => x.Name)
.To(e => e.Name.ToUpper())); // ❌ method call — never executed
}
using Cratis.Chronicle.Projections;
public class UserReadModel { public string Name { get; set; } }
public class UserRegistered { public string Name { get; set; } }
public class UserProjection : IProjectionFor<UserReadModel>
{
public void Define(IProjectionBuilderFor<UserReadModel> builder) =>
builder.From<UserRegistered>(evt => evt
.Set(x => x.Name)
.To(e => e.Name)); // ✅ simple member access
}

In Chronicle’s projection builder, any parameter typed as Expression<Func<...>> is parsed at startup to extract a property path for the read-model mapping—the lambda body is never executed at runtime. Writing method calls, arithmetic (e.Count + 1), conditional expressions (e.Flag ? e.A : e.B), or similar executable code in such a lambda will be silently ignored, producing an incorrect or incomplete projection definition that is very difficult to debug.

The only valid lambda body is a direct property access chain, for example x => x.Property or x => x.Parent.Child.

  • CHR0016: Projection Define() must not contain imperative code
  • CHR0020: Constraint expression lambda must only access members