private void DoTest <T1, T2>(Expression <Func <T1, T2> > exp, T1 data, string expected) { ParameterExpression[] currentIndexes; var body = new LinqEliminator().Eliminate(exp.Body, out currentIndexes); var paths = ExpressionPathsBuilder.BuildPaths(exp.Body, currentIndexes); var resolved = Expression.Block(currentIndexes, new[] { body, paths }); ParameterExpression[] parameters = resolved.ExtractParameters(); Expression <Func <T1, string[][]> > lambda = Expression.Lambda <Func <T1, string[][]> >(resolved, parameters); Assert.AreEqual(expected, string.Join(".", LambdaCompiler.Compile(lambda, CompilerOptions.All) /*.Compile()*/ (data)[0])); }
private static void BuildNodeValidator(IPathFormatter pathFormatter, Expression path, List <MutatorConfiguration> mutators, ModelConfigurationNode root, List <KeyValuePair <Expression, Expression> > aliases, Dictionary <ParameterExpression, ExpressionPathsBuilder.SinglePaths> paths, ParameterExpression result, Type treeRootType, ParameterExpression priority, List <Expression> validationResults) { if (mutators.All(mutator => !(mutator is ValidatorConfiguration))) { return; } Expression isDisabled = null; foreach (var mutator in mutators) { var disableIfConfiguration = mutator as DisableIfConfiguration; if (disableIfConfiguration == null) { continue; } CheckDependencies(root, disableIfConfiguration); var current = disableIfConfiguration.GetCondition(aliases); if (current != null) { isDisabled = isDisabled == null ? current : Expression.OrElse(current, isDisabled); } } var firstAlias = new List <KeyValuePair <Expression, Expression> > { aliases.First() }; var aliasesInTermsOfFirst = aliases.Count > 1 ? aliases.Skip(1).ToList() : new List <KeyValuePair <Expression, Expression> >(); aliasesInTermsOfFirst = aliasesInTermsOfFirst.Select(pair => new KeyValuePair <Expression, Expression>(pair.Key, pair.Value.ResolveAliases(firstAlias))).ToList(); //var eachesResolver = new EachesResolver(new int[aliasesInTermsOfFirst.Count / 2].Select((x, i) => aliasesInTermsOfFirst[i * 2 + 1].Key).ToArray()); // Replace LINQ methods with cycles to obtain indexes //var resolvedPath = eachesResolver.Visit(path.ResolveAliases(firstAlias)); var currentPath = path.ResolveAliases(aliases); var value = Expression.Parameter(typeof(object)); Expression valueAssignment = Expression.Assign(value, Expression.Convert(new LinqEliminator().Eliminate(currentPath, out var currentIndexes), typeof(object))); var currentPaths = ExpressionPathsBuilder.BuildPaths(currentPath, currentIndexes, paths); var resolvedArrayIndexes = currentPaths.paths.Select(p => new ResolvedArrayIndexes { path = p }).ToArray(); var chains = path.CutToChains(true, true).GroupBy(exp => new ExpressionWrapper(exp, false)).Select(grouping => grouping.Key.Expression.ResolveAliases(firstAlias)).ToArray(); //Expression cutChains = Expression.NewArrayInit(typeof(string[]), chains.Select(expression => eachesResolver.Visit(expression).ResolveArrayIndexes())); Expression formattedChains = null; if (pathFormatter != null) { formattedChains = pathFormatter.GetFormattedPath(chains); if (formattedChains != null) { formattedChains = formattedChains.ResolveAliases(aliasesInTermsOfFirst); } } if (formattedChains == null) { // Default path formatting - simply list all the paths along the object tree if (!(pathFormatter is PathFormatterWrapper)) { formattedChains = FormatPaths(currentPaths); } else { formattedChains = Expression.Constant(null, typeof(MultiLanguagePathText)); } } var localResults = new List <Expression> { valueAssignment }; foreach (var validator in mutators.Where(mutator => mutator is ValidatorConfiguration).Cast <ValidatorConfiguration>()) { CheckDependencies(root, validator); var appliedValidator = validator.Apply(root.ConfiguratorType, aliases).EliminateLinq(); if (appliedValidator == null) { continue; } var currentValidationResult = Expression.Variable(typeof(ValidationResult)); if (validator.Priority < 0) { throw new PriorityOutOfRangeException("Validator's priority cannot be less than zero"); } if (validator.Priority >= PriorityShift) { throw new PriorityOutOfRangeException("Validator's priority must be less than " + PriorityShift); } var validatorPriority = Expression.Constant(validator.Priority); Expression currentPriority = Expression.AddChecked(Expression.MultiplyChecked(priority, Expression.Constant(PriorityShift)), validatorPriority); // todo вызывать один раз var targetValidationResults = SelectTargetNode(result, treeRootType, resolvedArrayIndexes); var listAddMethod = HackHelpers.GetMethodDefinition <ValidationResultTreeNode>(x => x.AddValidationResult(null)); var currentFormattedValidationResult = Expression.New(formattedValidationResultConstructor, currentValidationResult, value, formattedChains, currentPriority); Expression addValidationResult = Expression.Call(targetValidationResults, listAddMethod, currentFormattedValidationResult); Expression validationResultIsNotNull = Expression.NotEqual(currentValidationResult, Expression.Constant(null, typeof(ValidationResult))); Expression validationResultIsNotOk = Expression.NotEqual(Expression.Property(currentValidationResult, typeof(ValidationResult).GetProperty("Type", BindingFlags.Instance | BindingFlags.Public)), Expression.Constant(ValidationResultType.Ok)); Expression condition = Expression.IfThen(Expression.AndAlso(validationResultIsNotNull, validationResultIsNotOk), addValidationResult); var localResult = Expression.IfThen(Expression.Not(Expression.Call(MutatorsHelperFunctions.DynamicMethod.MakeGenericMethod(typeof(bool)), Expression.Property(result, validationResultTreeNodeExhaustedProperty))), Expression.Block(new[] { currentValidationResult }, Expression.Assign(currentValidationResult, appliedValidator), condition)); localResults.Add(localResult); } var appliedValidators = Expression.Block(new[] { value }.Concat(currentIndexes), localResults); if (isDisabled == null) { validationResults.Add(appliedValidators); } else { Expression test = Expression.NotEqual(Expression.Convert(isDisabled, typeof(bool?)), Expression.Constant(true, typeof(bool?))); validationResults.Add(Expression.IfThen(test, appliedValidators)); } }
public void BuildValidator(IPathFormatter pathFormatter, ModelConfigurationNode root, List <KeyValuePair <Expression, Expression> > aliases, Dictionary <ParameterExpression, ExpressionPathsBuilder.SinglePaths> paths, ParameterExpression result, Type treeRootType, ParameterExpression priority, List <Expression> validationResults) { foreach (var pair in mutators) { BuildNodeValidator(pathFormatter, pair.Key, pair.Value, root, aliases, paths, result, treeRootType, priority, validationResults); } foreach (var pair in children) { var edge = pair.Key.Expression; var child = pair.Value; var array = ((MethodCallExpression)edge).Arguments[0]; LambdaExpression predicate = null; var resolvedArray = array.ResolveAliases(aliases); var itemType = resolvedArray.Type.GetItemType(); var item = Expression.Call(null, MutatorsHelperFunctions.EachMethod.MakeGenericMethod(itemType), array); var index = Expression.Call(null, MutatorsHelperFunctions.CurrentIndexMethod.MakeGenericMethod(itemType), item); if (!resolvedArray.Type.IsArray) { // Filtered array if (resolvedArray.NodeType == ExpressionType.Call) { var methodCallExpression = (MethodCallExpression)resolvedArray; if (methodCallExpression.Method.IsWhereMethod()) { resolvedArray = methodCallExpression.Arguments[0]; predicate = (LambdaExpression)methodCallExpression.Arguments[1]; } } } ParameterExpression[] indexes = null; var parameter = Expression.Parameter(itemType); var adjustedResolvedArray = Expression.Call(selectMethod.MakeGenericMethod(itemType, itemType), resolvedArray, Expression.Lambda(parameter, parameter)); adjustedResolvedArray = Expression.Call(MutatorsHelperFunctions.EachMethod.MakeGenericMethod(itemType), adjustedResolvedArray); var monster = new LinqEliminator().EliminateAndEnumerate(adjustedResolvedArray, (current, currentIndex, currentIndexes) => { indexes = currentIndexes; aliases.Add(new KeyValuePair <Expression, Expression>(current, item)); aliases.Add(new KeyValuePair <Expression, Expression>(currentIndex, index)); var currentPaths = ExpressionPathsBuilder.BuildPaths(adjustedResolvedArray, currentIndexes, paths); //currentPaths.Add(currentIndex); paths.Add(current, currentPaths); var childValidationResults = new List <Expression>(); child.BuildValidator(pathFormatter, root, aliases, paths, result, treeRootType, priority, childValidationResults); aliases.RemoveAt(aliases.Count - 1); aliases.RemoveAt(aliases.Count - 1); paths.Remove(current); if (predicate != null) { var condition = Expression.Lambda(current, current).Merge(predicate).Body; for (var i = 0; i < childValidationResults.Count; ++i) { childValidationResults[i] = Expression.IfThen( Expression.Equal( Expression.Convert(condition, typeof(bool?)), Expression.Constant(true, typeof(bool?))), childValidationResults[i]); } } for (var i = 0; i < childValidationResults.Count; ++i) { childValidationResults[i] = childValidationResults[i].ExtractLoopInvariantFatExpressions(aliases.Where(p => p.Key is ParameterExpression).Select(p => (ParameterExpression)p.Key), e => e); } return(Expression.Block(childValidationResults.SplitToBatches())); }); if (indexes != null && indexes.Length > 0) { monster = Expression.Block(indexes, monster); } validationResults.Add(monster); } }