Compile a rule into a delegate¶
This operation turns a NotificationRule's string metadata (member name, operator name, target value) into a LINQ expression tree over the model type T, then compiles it into a runnable delegate. It is the reflection-and-expression core of the engine and the location of the library's principal performance finding: nothing here is cached.
| Field | Value |
|---|---|
| Operation ID | OP-CIA-03 |
| Trigger | A caller request (public static method), or Evaluate one rule. |
| Inputs | r (NotificationRule). |
| Outputs | A compiled Func<T,TResult>. |
| Primary components | NotificationRuleEngine.CompileRule<T,TResult>, NotificationRuleEngine.BuildExpr<T> |
| Entry point | Services/SAIF.Services.ClaimInfoApi.Core/NotificationRuleEngine.cs (lines 73–119) |
Steps¶
- Create a parameter expression of type
T. - Build the expression body with
BuildExpr<T>, choosing the first matching branch: - Method branch: if
typeof(T).GetMethod(r.MemberName)finds a public method, convertTargetValueto the method's first parameter type withConvert.ChangeType, and emitmodel.Method(value). The literal string"null"is treated as a null value (CIA-BR-008). (CIA-BR-009) - Property-read branch: if
OperatorNameis null, emitmodel.Property(CIA-BR-010). - Binary-operator branch: if
OperatorNameparses as aSystem.Linq.Expressions.ExpressionTypename (for exampleEqual,GreaterThan), convertTargetValueto the property type and emitExpression.MakeBinary(op, model.Property, value)(CIA-BR-011). - Member-method branch: otherwise, treat
OperatorNameas a public method on the property's type (for exampleContains); convertTargetValueto that method's first parameter type and emitmodel.Property.Method(value)(CIA-BR-012). - Wrap the body in
Expression.Lambda<Func<T,TResult>>and call.Compile(). - No caching is performed: the call
Expression.Lambda<Func<T, TResult>>(body, parameter).Compile()runs once per rule, per model, per call to this operation — see Architecture: Design constraints. - Failure behavior: there is no validation or
try/catch. - An unknown member name produces a
NullReferenceException,ArgumentNullException, orArgumentExceptionfrom theExpressionfactories*. - A non-convertible
TargetValueproducesInvalidCastExceptionorFormatExceptionfromConvert.ChangeType. - A delegate whose type does not match
TResultproducesArgumentException. - All of these propagate to the caller.
Process flow¶
The following diagram visualizes the expression-building and compilation flow.
flowchart TD
Start([Trigger]) --> Param["Create parameter<br>expression of type T"]
Param --> Branch{"BuildExpr branch"}
Branch -->|Method match| Method["model.Method(value)"]
Branch -->|No operator| PropRead["model.Property"]
Branch -->|Operator is ExpressionType| Binary["Expression.MakeBinary(op,<br>model.Property, value)"]
Branch -->|Other operator| MemberMethod["model.Property.Method(value)"]
Method --> Lambda["Expression.Lambda<Func<T,TResult>><br>.Compile() — not cached"]
PropRead --> Lambda
Binary --> Lambda
MemberMethod --> Lambda
Lambda --> Done([Return compiled Func delegate])
Failure and edge-case behavior¶
- No delegate cache exists: every call recompiles the expression tree from scratch, even for the same rule/model-type pair evaluated repeatedly — a tech-debt/performance constraint, not a mitigated design choice*.
- No validation of
MemberName,OperatorName, orTargetValueoccurs before expression construction; malformed metadata surfaces as a runtime exception at compile or invoke time rather than a checked error.
Note: Statements marked with
*were inferred from code structure, naming conventions, or contextual evidence where direct confirmation was not available in source artifacts.
Related documentation¶
Sources¶
Services/SAIF.Services.ClaimInfoApi.Core/NotificationRuleEngine.cs