# ExpressiveAnnotations **Repository Path**: lanicon/ExpressiveAnnotations ## Basic Information - **Project Name**: ExpressiveAnnotations - **Description**: Annotation-based conditional validation library. - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-10-19 - **Last Updated**: 2025-05-01 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ![logo](logo.png) # ExpressiveAnnotations[annotation-based conditional validation] [![Build status](https://img.shields.io/appveyor/ci/jwaliszko/ExpressiveAnnotations.svg)](https://ci.appveyor.com/project/jwaliszko/ExpressiveAnnotations) [![Coverage status](https://img.shields.io/codecov/c/github/jwaliszko/ExpressiveAnnotations.svg)](https://codecov.io/github/jwaliszko/ExpressiveAnnotations) [![Release version](https://img.shields.io/github/release/jwaliszko/ExpressiveAnnotations.svg)](https://github.com/jwaliszko/ExpressiveAnnotations/releases/latest) [![License](http://img.shields.io/badge/license-MIT-blue.svg)](http://opensource.org/licenses/MIT) A small .NET and JavaScript library which provides full-stack, annotation-based, conditional validation mechanisms. Given attributes, powered by expressions engine, allow to forget about imperative way of step-by-step implementation of validation conditions in many cases. Since fields validation requirements are applied as metadata, domain-related code is more condensed. ### Table of contents - [What is the context behind this work?](#what-is-the-context-behind-this-implementation) - [RequiredIf vs. AssertThat - where is the difference?](#requiredif-vs-assertthat---where-is-the-difference) - [Sample projects + demo](#sample-projects-+-demo) - [What are a brief examples of usage?](#what-are-a-brief-examples-of-usage) - [Declarative vs. imperative programming - what is it about?](#declarative-vs-imperative-programming---what-is-it-about) - [EA expressions specification](#expressions-specification) - [Grammar definition](#grammar-definition) - [Where syntax meets semantics](#where-syntax-meets-semantics) - [Operators precedence and associativity](#operators-precedence) - [Built-in functions (methods ready to be used by expressions)](#built-in-functions) - [How to construct conditional validation attributes?](#how-to-construct-conditional-validation-attributes) - [Signatures description](#signatures) - [Implementation details outline](#implementation) - [Traps (discrepancies between server- and client-side expressions evaluation)](#traps) - [What about the support of ASP.NET MVC client-side validation?](#what-about-the-support-of-aspnet-mvc-client-side-validation) - [Frequently asked questions](#frequently-asked-questions) - [Is it possible to compile all usages of annotations at once?](#is-it-possible-to-compile-all-usages-of-annotations-at-once) (re server-side) - [What if there is no built-in function I need?](#what-if-there-is-no-built-in-function-i-need) (re client and server-side) - [Can I have custom utility-like functions outside of my models?](#can-I-have-custom-utility-like-functions-outside-of-my-models) (re server-side) - [How to cope with values of custom types?](#how-to-cope-with-values-of-custom-types) (re client-side) - [How to cope with dates given in non-standard formats?](#how-to-cope-with-dates-given-in-non-standard-formats) (re client-side) - [What if "ea" variable is already used by another library?](#what-if-ea-variable-is-already-used-by-another-library) (re client-side) - [How to control frequency of dependent fields validation?](#how-to-control-frequency-of-dependent-fields-validation) (re client-side) - [Can I increase web console verbosity for debug purposes?](#can-i-increase-web-console-verbosity-for-debug-purposes) (re client-side) - [I prefer enum values to be rendered as numbers, is it allowed?](#i-prefer-enum-values-to-be-rendered-as-numbers-is-it-allowed) (re client-side) - [How to access one method from another?](#how-to-access-one-method-from-another) (re client-side) - [How to access current control from inside a method?](#how-to-access-current-control-from-inside-a-method) (re client-side) - [How to fetch field's value or its display name in error message?](#how-to-fetch-fields-value-or-its-display-name-in-error-message) (re client and server-side) - [Is there any event raised when validation is done?](#is-there-any-event-raised-when-validation-is-done) (re client-side) - [Can I decorate conditionally required fields with asterisks?](#can-i-decorate-conditionally-required-fields-with-asterisks) (re client-side) - [How to handle validation based on parent model field?](#how-to-handle-validation-based-on-parent-model-field) (re client and server-side) - [RequiredIf attribute is not working, what is wrong?](#requiredif-attribute-is-not-working-what-is-wrong) (re client and server-side) - [Few fields seem to be bypassed during validation, any clues?](#few-fields-seem-to-be-bypassed-during-validation-any-clues) (re client-side) - [Client-side validation doesn't work, how to troubleshoot it?](#client---side-validation-doesnt-work-how-to-troubleshoot-it) (re client-side) - [Is there a possibility to perform asynchronous validation?](#is-there-a-possibility-to-perform-asynchronous-validation) (re client-side, experimental) - [What if my question is not covered by FAQ section?](#what-if-my-question-is-not-covered-by-faq-section) - [Installation instructions](#installation) - [Contributors](#contributors) - [License](#license) ### What is the context behind this work? There are number of cases where the concept of metadata is used for justified reasons. Attributes are one of the ways to associate complementary information with existing data. Such annotations may also define the correctness of data. Declarative validation when [compared](#declarative-vs-imperative-programming---what-is-it-about) to imperative approach seems to be more convenient in many cases. Clean, compact code - all validation logic defined within the model scope. Simple to write, obvious to read. ### RequiredIf vs. AssertThat - where is the difference? * `RequiredIf` - if value is not yet provided, check whether it is required (annotated field is required to be non-null, when given condition is satisfied), * `AssertThat` - if value is already provided, check whether the condition is met (non-null annotated field is considered as valid, when given condition is satisfied). ### Sample projects + demo * [**ASP.NET MVC web sample**](src/ExpressiveAnnotations.MvcWebSample), * [**WPF MVVM desktop sample**](src/ExpressiveAnnotations.MvvmDesktopSample). ASP.NET MVC web sample is also hosted online - http://expressiveannotations.net/. ### What are a brief examples of usage? This section presents few exemplary code snippets. Sample projects in the section above contain much more comprehensive set of use cases. ```C# using ExpressiveAnnotations.Attributes; [RequiredIf("GoAbroad == true")] public string PassportNumber { get; set; } ``` Above we are saying, that annotated field is required when condition given in the logical expression is satisfied (passport number is required, if go abroad field has true boolean value). Simple enough, let's move to another variation: ```C# [AssertThat("ReturnDate >= Today()")] public DateTime? ReturnDate { get; set; } ``` By the usage of this attribute type, we are not validating field requirement as before - its value is allowed to be null this time. Nevertheless, if some value is already given, provided restriction needs to be satisfied (return date needs to be greater than or equal to the date returned by `Today()` [built-in function](#built-in-functions)). As shown below, both types of attributes may be combined (moreover, the same type can be applied multiple times for a single field): ```C# [RequiredIf("Details.Email != null")] [RequiredIf("Details.Phone != null")] [AssertThat("AgreeToContact == true")] public bool? AgreeToContact { get; set; } ``` Literal translation means, that if either email or phone is provided, you are forced to authorize someone to contact with you (boolean value indicating contact permission has to be true). What is more, we can see that nested properties are supported by [the expressions parser](#implementation). The complexity of expressions may be arbitrarily increased, e.g. take a brief look at the following construction: ```C# [RequiredIf(@"GoAbroad == true && ( (NextCountry != 'Other' && NextCountry == Country) || (Age > 24 && Age <= 55) )")] public string ReasonForTravel { get; set; } ``` Restriction above, despite being more specific than its predecessors, still can be quickly understood (reason for travel has to be provided if you plan to go abroad and, either want to visit the same definite country twice, or are between 25 and 55). Conditional operations are supported (ternary operator defined). You can imply various assertions on specific field based on certain condition (or nested conditions), e.g. ```C# [AssertThat("Switch == 'ON' ? Voltage1 == Voltage2 : true")] public int Voltage1 { get; set; } ``` Here, when switch is ON, voltages must be equal - otherwise everything is OK. You could express the same statement without conditional operator, i.e. ```C# [AssertThat("Switch == 'ON' && (Voltage1 == Voltage2) || (Switch != 'ON')")] ``` but it is less verbose. ### Declarative vs. imperative programming - what is it about? With **declarative** programming you write logic that expresses *what* you want, but not necessarily *how* to achieve it. You declare your desired results, but not step-by-step. In our case, this concept is materialized by attributes, e.g. ```C# [RequiredIf("GoAbroad == true && NextCountry != 'Other' && NextCountry == Country", ErrorMessage = "If you plan to travel abroad, why visit the same country twice?")] public string ReasonForTravel { get; set; } ``` Here, we're saying "ensure the field is required according to given condition". With **imperative** programming you define the control flow of the computation which needs to be done. You tell the compiler what you want, exactly step by step. If we choose this way instead of model fields decoration, it has negative impact on the complexity of the code. Logic responsible for validation is now implemented somewhere else in our application, e.g. inside controllers actions instead of model class itself: ```C# if (!model.GoAbroad) return View("Success"); if (model.NextCountry == "Other") return View("Success"); if (model.NextCountry != model.Country) return View("Success"); ModelState.AddModelError("ReasonForTravel", "If you plan to travel abroad, why visit the same country twice?"); return View("Home", model); } ``` Here instead, we're saying "if condition is met, return some view; otherwise, add error message to state container, return other view". ### EA expressions specification ##### Grammar definition Expressions handled by EA parser must comply with the following grammar: ``` exp => cond-exp cond-exp => l-or-exp ['?' exp ':' exp] // right associative (right recursive) l-or-exp => l-and-exp ('||' l-and-exp)* // left associative (non-recursive alternative to left recursion) l-and-exp => b-and-exp ('&&' b-and-exp)* b-or-exp => xor-exp ('|' xor-exp)* xor-exp => b-and-exp ('^' b-and-exp)* b-and-exp => eq-exp ('&' eq-exp)* eq-exp => rel-exp (('==' | '!=') rel-exp)* rel-exp => shift-exp (('>' | '>=' | '<' | '<=') shift-exp)* shift-exp => add-exp (('<<' | '>>') add-exp)* add-exp => mul-exp (('+' | '-') mul-exp)* mul-exp => unary-exp (('*' | '/' | '%') unary-exp)* unary-exp => ('+' | '-' | '!' | '~') unary-exp | primary-exp primary-exp => null-lit | bool-lit | num-lit | string-lit | arr-access | id-access | '(' exp ')' arr-access => arr-lit | arr-lit '[' exp ']' ('[' exp ']' | '.' identifier)* id-access => identifier | identifier ('[' exp ']' | '.' identifier)* | func-call ('[' exp ']' | '.' identifier)* func-call => identifier '(' [exp-list] ')' null-lit => 'null' bool-lit => 'true' | 'false' num-lit => int-lit | float-lit int-lit => dec-lit | bin-lit | hex-lit array-lit => '[' [exp-list] ']' exp-list => exp (',' exp)* ``` Terminals are expressed in quotes. Each nonterminal is defined by a rule in the grammar except for *dec-lit*, *bin-lit*, *hex-lit*, *float-lit*, *string-lit* and *identifier*, which are assumed to be implicitly defined (*identifier* specifies names of functions, properties, constants and enums). Expressions are built of Unicode letters and numbers (i.e. `[L*]` and `[N*]` [categories](https://en.wikipedia.org/wiki/Unicode_character_property) respectively) with the usage of the following components: * logical operators: `!a`, `a||b`, `a&&b`, * comparison operators: `a==b`, `a!=b`, `ab`, `a>=b`, * arithmetic operators: `+a`, `-a`, `a+b`, `a-b`, `a*b`, `a/b`, `a%b`, `~a`, `a&b`, `a^b`, `a|b`, `a<>b`, * other operators: `a()`, `a[]`, `a.b`, `a?b:c`, * literals: * null, i.e. `null`, * boolean, i.e. `true` and `false`, * decimal integer, e.g. `123`, * binary integer (with `0b` prefix), e.g. `0b1010`, * hexadecimal integer (with `0x` prefix), e.g. `0xFF`, * float, e.g. `1.5` or `0.3e-2`, * string, e.g. `'in single quotes'` (internal quote escape sequence is `\'`, character representing new line is `\n`), * array (comma separated items within square brackets), e.g. `[1,2,3]`, * identifier, i.e. names of functions, properties, constants and enums. ##### Where syntax meets semantics EA expressions syntax is defined by the grammar shown above. Valid expressions must follow not only lexical and syntax level rules, but must have appropriate semantics as well. Grammars, as already mentioned, naturally define the language syntax - but not only. Grammars in general contribute to the semantics a little, by defining the parse tree - thus denoting the precedence and associativity of operators. This being said, it is not just about the legal string being produced - the structure of parse tree corresponds to the order, in which various parts of the expression are to be evaluated. Such contribution to semantics is not enough though. Grammar says nothing about the types of operands. For the expressions to have valid semantics, all the requirements on the types of operands must be realized as well. Prior to execution of an operation, type checks and eventual type conversions are made. The result type, e.g. of a valid binary operation is, most of the time but not always, the same as the most general of the input types. When a binary operation is used then a generalization of operands is performed, to make the two operands the same (most general) type, before the operation is done. The order of generalization, from most general to most specific, is briefly described as follows: `string` (generalization done using the current locale) -> `double` -> `int`. Also nullable types are more general than their non-nullable counterparts. ##### Operators precedence and associativity The following table lists the precedence and associativity of operators (listed top to bottom, in descending precedence):
Precedence Operator Description Associativity
1 ()
Function call (postfix) Left to right
[]
Subscript (postfix)
. Member access (postfix)
2 + - Unary plus and minus Right to left
! ~ Logical NOT and bitwise NOT (one's complement)
3 * / % Multiplication, division, and remainder Left to right
4 + - Addition and subtraction
5 << >> Bitwise left shift and right shift
6 < <= Relational operators < and ≤ respectively
> >= Relational operators > and ≥ respectively
7 == != Equality operators = and ≠ respectively
8 & Bitwise AND
9 ^ Bitwise XOR (exclusive OR)
10 | Bitwise OR (inclusive OR)
11 && Logical AND
12 || Logical OR
13 ?: Ternary conditional Right to left
##### Built-in functions (methods ready to be used by expressions) As already noted, there is an option to reinforce expressions with functions, e.g. ```C# [AssertThat("StartsWith(CodeName, 'abc.') || EndsWith(CodeName, '.xyz')")] public string CodeName { get; set; } ``` Toolchain functions available out of the box at server- and client-side: * `DateTime Now()` * Gets the current local date and time (client-side returns the number of milliseconds since January 1, 1970, 00:00:00 UTC). * `DateTime Today()` * Gets the current date with the time component set to 00:00:00 (client-side returns the number of milliseconds since January 1, 1970, 00:00:00 UTC). * `DateTime Date(int year, int month, int day)` * Initializes a new date to a specified year, month (months are 1-based), and day, with the time component set to 00:00:00 (client-side returns the number of milliseconds since January 1, 1970, 00:00:00 UTC). * `DateTime Date(int year, int month, int day, int hour, int minute, int second)` * Initializes a new date to a specified year, month (months are 1-based), day, hour, minute, and second (client-side returns the number of milliseconds since January 1, 1970, 00:00:00 UTC). * `DateTime ToDate(string dateString)` * Converts the specified string representation of a date and time to its equivalents: `DateTime` at server-side - uses .NET [`DateTime.Parse(string dateString)`](https://msdn.microsoft.com/en-us/library/vstudio/1k1skd40(v=vs.100).aspx), and number of milliseconds since January 1, 1970, 00:00:00 UTC at client-side - uses JavaScript [`Date.parse(dateString)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse). * `TimeSpan TimeSpan(int days, int hours, int minutes, int seconds)` * Initializes a new time period according to specified days, hours, minutes, and seconds (client-side period is expressed in milliseconds). * `int Length(str)` * Gets the number of characters in a specified string (null-safe). * `string Trim(string str)` * Removes all leading and trailing white-space characters from a specified string (null-safe). * `string Concat(string strA, string strB)` * Concatenates two specified strings (null-safe). * `string Concat(string strA, string strB, strC)` * Concatenates three specified strings (null-safe). * `int CompareOrdinal(string strA, string strB)` * Compares strings using ordinal sort rules. An integer that indicates the lexical relationship between the two comparands is returned (null-safe): * -1 - strA is less than strB, *  0 - strA and strB are equal, *  1 - strA is greater than strB. * `int CompareOrdinalIgnoreCase(string strA, string strB)` * Compares strings using ordinal sort rules and ignoring the case of the strings being compared (null-safe). * `bool StartsWith(string str, string prefix)` * Determines whether the beginning of specified string matches a specified prefix (null-safe). * `bool StartsWithIgnoreCase(string str, string prefix)` * Determines whether the beginning of specified string matches a specified prefix, ignoring the case of the strings (null-safe). * `bool EndsWith(string str, string suffix)` * Determines whether the end of specified string matches a specified suffix (null-safe). * `bool EndsWithIgnoreCase(string str, string suffix)` * Determines whether the end of specified string matches a specified suffix, ignoring the case of the strings (null-safe). * `bool Contains(string str, string substr)` * Returns a value indicating whether a specified substring occurs within a specified string (null-safe). * `bool ContainsIgnoreCase(string str, string substr)` * Returns a value indicating whether a specified substring occurs within a specified string, ignoring the case of the strings (null-safe). * `bool IsNullOrWhiteSpace(string str)` * Indicates whether a specified string is null, empty, or consists only of white-space characters (null-safe). * `bool IsDigitChain(string str)` * Indicates whether a specified string represents a sequence of digits (ASCII characters only, null-safe). * `bool IsNumber(string str)` * Indicates whether a specified string represents integer or float number (ASCII characters only, null-safe). * `bool IsEmail(string str)` * Indicates whether a specified string represents valid e-mail address (null-safe). * `bool IsPhone(string str)` * Indicates whether a specified string represents valid phone number (null-safe). * `bool IsUrl(string str)` * Indicates whether a specified string represents valid url (null-safe). * `bool IsRegexMatch(string str, string regex)` * Indicates whether the regular expression finds a match in the input string (null-safe). * `Guid Guid(string str)` * Initializes a new instance of the Guid structure by using the value represented by a specified string. * `double Min(params double[] values)` * Returns the minimum value in a sequence of numeric values. * `double Max(params double[] values)` * Returns the maximum value in a sequence of numeric values. * `double Sum(params double[] values)` * Computes the sum of the numeric values in a sequence. * `double Average(params double[] values)` * Computes the average of the numeric values in a sequence. ### How to construct conditional validation attributes? ##### Signatures description ```C# RequiredIfAttribute( string expression, [bool AllowEmptyStrings], [int Priority] [string ErrorMessage] ...) /* Validation attribute, executed for null-only annotated * field, which indicates that such a field is required * to be non-null, when computed result of given logical * expression is true. */ AssertThatAttribute( string expression, [int Priority] [string ErrorMessage] ...) /* Validation attribute, executed for non-null annotated * field, which indicates that assertion given in logical * expression has to be satisfied, for such a field to be * considered as valid. */ ``` ``` expression - The logical expression based on which specified condition is computed. AllowEmptyStrings - Gets or sets a flag indicating whether the attribute should allow empty or whitespace strings. False by default. Priority - Gets or sets the hint, available for any concerned external components, indicating the order in which this attribute should be executed among others of its kind. Value is optional and not set by default, which means that execution order is undefined. ErrorMessage - Gets or sets an explicit error message string. A difference to default behavior is awareness of new format items, i.e. {fieldPath[:indicator]}. Given in curly brackets, can be used to extract values of specified fields, e.g. {field}, {field.field}, within current model context or display names of such fields, e.g. {field:n}. Braces can be escaped by double-braces, i.e. to output a { use {{ and to output a } use }}. The same logic works for messages provided in resources. ``` Note above covers almost exhaustively what is actually needed to work with EA. Nevertheless, the full API documentation, generated with [Sandcastle](https://sandcastle.codeplex.com/) (with the support of [SHFB](http://shfb.codeplex.com/)), can be downloaded (in the form of compiled HTML help file) from [here](doc/api/api.chm?raw=true) (includes only C# API, no JavaScript part there). ##### Implementation details outline Implementation core is based on [expressions parser](src/ExpressiveAnnotations/Analysis/Parser.cs?raw=true), which runs on the grammar [shown above](#grammar-definition). Firstly, at the lexical analysis stage, character stream of the expression is converted into token stream (whitespaces ignored, characters grouped into tokens and associated with position in the text). Next, at the syntax analysis level, abstract syntax tree is constructed according to the rules defined by the grammar. While the tree is being built, also the 3rd stage, mainly semantic analysis, is being performed. This stage is directly related to operands type checking (and eventual type conversions according to type generalization rules, when incompatible types are detected). Based on valid expression string [expression tree](http://msdn.microsoft.com/en-us/library/bb397951.aspx) structure is finally being built. A delegate containing compiled version of the lambda expression (defined by the expression tree) is returned as a result of the parsersing mechanism. Such delegate can be then invoked for specified model object. When expression is provided to the attribute, it should be of a boolean type. The result of its evaluation indicates whether the assertion or requirement condition is satisfied or not. For the sake of performance optimization, expressions provided to attributes are compiled only once. Such compiled lambdas are then cached inside attributes instances and invoked for any subsequent validation requests without recompilation. When working with ASP.NET MVC stack, unobtrusive client-side validation mechanism is [additionally available](#what-about-the-support-of-aspnet-mvc-client-side-validation). Client receives unchanged expression string from server. Such an expression is then evaluated using JavaScript [`eval()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval) method within the context of reflected model object. Such a model, analogously to the server-side one, is basically deserialized DOM form (with some type-safety assurances and registered toolchain methods). ##### Traps (discrepancies between server- and client-side expressions evaluation) Because client-side handles expressions in its unchanged form (as provided to attribute), attention is needed when dealing with `null` keyword - there are discrepancies between EA parser (mostly follows C# rules) and JavaScript, e.g. * `null + "text"` - in C# `"text"`, in JS `"nulltext"`, * `2 * null` - in C# `null` , in JS `0`, * `null > -1` - in C# `false` , in JS `true`, * and more... ### What about the support of ASP.NET MVC client-side validation? Client-side validation is fully supported. Enable it for your web project within the next few steps: 1. Reference both assemblies to your project: core [**ExpressiveAnnotations.dll**](src/ExpressiveAnnotations) (defines validation attributes driven by expressions) and subsidiary [**ExpressiveAnnotations.MvcUnobtrusive.dll**](src/ExpressiveAnnotations.MvcUnobtrusive) (defines model validators for ASP.NET MVC). 2. In Global.asax register required validators (`IClientValidatable` interface is not directly implemented by the attributes, to avoid coupling of ExpressionAnnotations assembly with System.Web.Mvc dependency): ```C# using ExpressiveAnnotations.Attributes; using ExpressiveAnnotations.MvcUnobtrusive.Validators; protected void Application_Start() { DataAnnotationsModelValidatorProvider.RegisterAdapter( typeof (RequiredIfAttribute), typeof (RequiredIfValidator)); DataAnnotationsModelValidatorProvider.RegisterAdapter( typeof (AssertThatAttribute), typeof (AssertThatValidator)); ``` Alternatively, use predefined `ExpressiveAnnotationsModelValidatorProvider` (recommended): ```C# using ExpressiveAnnotations.MvcUnobtrusive.Providers; protected void Application_Start() { ModelValidatorProviders.Providers.Remove( ModelValidatorProviders.Providers .FirstOrDefault(x => x is DataAnnotationsModelValidatorProvider)); ModelValidatorProviders.Providers.Add( new ExpressiveAnnotationsModelValidatorProvider()); ``` Despite the fact this provider automatically registers adapters for expressive validation attributes, it additionally respects their processing priorities when validation is performed (i.e. the [`Priority`](#signatures) property actually means something in practice). 3. Include [**expressive.annotations.validate.js**](src/expressive.annotations.validate.js?raw=true) script (makes client-side validation to work out of the box) in your page. It should be included in bundle below jQuery validation files: ```JavaScript ... ``` For supplementary reading visit the [installation section](#installation). ### Frequently asked questions ##### Is it possible to compile all usages of annotations at once? Yes, a complete list of types with annotations can be retrieved and compiled collectively. It can be useful, e.g. during unit testing phase, when without the necessity of your main application startup, all the compile-time errors (syntax errors, type checking errors) done to your expressions can be discovered. The following extension is helpful: ```C# public static IEnumerable CompileExpressiveAttributes(this Type type) { var properties = type.GetProperties() .Where(p => Attribute.IsDefined(p, typeof (ExpressiveAttribute))); var attributes = new List(); foreach (var prop in properties) { var attribs = prop.GetCustomAttributes().ToList(); attribs.ForEach(x => x.Compile(prop.DeclaringType)); attributes.AddRange(attribs); } return attributes; } ``` with the succeeding usage manner: ```C# // compile all expressions for specified model: var compiled = typeof (SomeModel).CompileExpressiveAttributes().ToList(); // ... or for current assembly: compiled = Assembly.GetExecutingAssembly().GetTypes() .SelectMany(t => t.CompileExpressiveAttributes()).ToList(); // ... or for all assemblies within current domain: compiled = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(a => a.GetTypes() .SelectMany(t => t.CompileExpressiveAttributes())).ToList(); ``` Notice that such compiled lambdas will be cached inside attributes instances stored in `compiled` list. That means that subsequent compilation requests: ```C# compiled.ForEach(x => x.Compile(typeof (SomeModel)); ``` do nothing (due to optimization purposes), unless invoked with enabled recompilation switch: ```C# compiled.ForEach(x => x.Compile(typeof (SomeModel), force: true); ``` Finally, this solution reveals compile-time errors only, you can still can get runtime errors though, e.g.: ```C# var parser = new Parser(); parser.AddFunction("CastToBool", obj => (bool) obj); parser.Parse("CastToBool(null)"); // compilation succeeds parser.Parse("CastToBool(null)").Invoke(null); // invocation fails (type casting err) ``` ##### What if there is no built-in function I need? Create it yourself. Any custom function defined within the model class scope at server-side is automatically recognized and can be used inside expressions, e.g. ```C# class Model { public bool IsBloodType(string group) { return Regex.IsMatch(group, @"^(A|B|AB|0)[\+-]$"); } [AssertThat("IsBloodType(BloodType)")] // method known here (context aware expressions) public string BloodType { get; set; } ``` If client-side validation is needed as well, function of the same signature (name and the number of parameters) must be available there. JavaScript corresponding implementation should be registered by the following instruction: ```JavaScript ``` Please note, that this slightly hacky workaround is only needed for such a specific cases, where back-references are required to be handled by client-side EA logic. ##### RequiredIf attribute is not working, what is wrong? Make sure `RequiredIf` is applied to a field which *accepts null values*. In the other words, it is redundant to apply this attribute to a field of non-nullable [value type](https://msdn.microsoft.com/en-us/library/s1ax56ch.aspx), like e.g. `int`, which is a struct representing integral numeric type, `DateTime`, etc. Because the value of such a type is always non-null, requirement demand is constantly fulfilled. Instead, for value types use their nullable forms, e.g. `int?`, `DateTime?`, etc. ```C# [RequiredIf("true")] // no effect... public int Value { get; set; } // ...unless int? is used ``` ```C# [RequiredIf("true")] // no effect... public DateTime Value { get; set; } // ...unless DateTime? is used ``` ##### Few fields seem to be bypassed during validation, any clues? Most likely these input fields are hidden, and `:hidden` fields validation is off by default. Such a fields are ignored by default by jquery-validation plugin, and EA follows analogic rules. If this is true, try to enable validation for hidden fields (empty-out the ignore filter), i.e.: ```JavaScript