/// <summary> /// Constructor for expressive model validator. /// </summary> /// <param name="metadata">The model metadata instance.</param> /// <param name="context">The controller context instance.</param> /// <param name="attribute">The expressive attribute instance.</param> /// <exception cref="System.ComponentModel.DataAnnotations.ValidationException"></exception> protected ExpressiveValidator(ModelMetadata metadata, ControllerContext context, T attribute) : base(metadata, context, attribute) { try { var fieldId = $"{metadata.ContainerType.FullName}.{metadata.PropertyName}".ToLowerInvariant(); AttributeFullId = $"{attribute.TypeId}.{fieldId}".ToLowerInvariant(); AttributeWeakId = $"{typeof (T).FullName}.{fieldId}".ToLowerInvariant(); FieldName = metadata.PropertyName; ResetSuffixAllocation(); var item = MapCache.GetOrAdd(AttributeFullId, _ => // map cache is based on static dictionary, set-up once for entire application instance { // (by design, no reason to recompile once compiled expressions) var parser = new Parser(); parser.RegisterMethods(); parser.Parse(metadata.ContainerType, attribute.Expression); FieldsMap = parser.GetFields().ToDictionary(x => x.Key, x => Helper.GetCoarseType(x.Value)); ConstsMap = parser.GetConsts(); ParsersMap = metadata.ContainerType.GetProperties() .Where(p => FieldsMap.Keys.Contains(p.Name) || metadata.PropertyName == p.Name) // narrow down number of parsers sent to client .Select(p => new { PropertyName = p.Name, ParserAttribute = p.GetAttributes <ValueParserAttribute>().SingleOrDefault() }).Where(x => x.ParserAttribute != null) .ToDictionary(x => x.PropertyName, x => x.ParserAttribute.ParserName); AssertNoNamingCollisionsAtCorrespondingSegments(); attribute.Compile(metadata.ContainerType); // compile expressions in attributes (to be cached for subsequent invocations) return(new CacheItem { FieldsMap = FieldsMap, ConstsMap = ConstsMap, ParsersMap = ParsersMap }); }); FieldsMap = item.FieldsMap; ConstsMap = item.ConstsMap; ParsersMap = item.ParsersMap; Expression = attribute.Expression; IDictionary <string, Guid> errFieldsMap; FormattedErrorMessage = attribute.FormatErrorMessage(metadata.GetDisplayName(), attribute.Expression, metadata.ContainerType, out errFieldsMap); // fields names, in contrast to values, do not change in runtime, so will be provided in message (less code in js) ErrFieldsMap = errFieldsMap; } catch (Exception e) { throw new ValidationException( $"{GetType().Name}: validation applied to {metadata.PropertyName} field failed.", e); } }
private Thread[] MakeThreads(string[] keys) { var threads = keys.Select(key => new Thread(load => { var storage = (ConcurrentStack <TestItem>)((object[])load)[0]; var counter = (ConcurrentStack <int>)((object[])load)[1]; var item = MapCache <string, TestItem> .GetOrAdd(key.ToString(), _ => { Debug.WriteLine($"{key} :: {Thread.CurrentThread.ManagedThreadId}"); counter.Push(Thread.CurrentThread.ManagedThreadId); // we want to test that this value factory delegate is invoked only once, even if map is accessed concurrently for the same key Thread.Sleep(500); return(new TestItem { Id = key }); }); storage.Push(item); })).ToArray(); return(threads); }
/// <summary> /// Constructor for expressive model validator. /// </summary> /// <param name="metadata">The model metadata instance.</param> /// <param name="context">The controller context instance.</param> /// <param name="attribute">The expressive attribute instance.</param> /// <exception cref="System.ComponentModel.DataAnnotations.ValidationException"></exception> protected ExpressiveValidator(ModelMetadata metadata, ControllerContext context, T attribute) : base(metadata, context, attribute) { try { Debug.WriteLine($"[ctor entry] process: {Process.GetCurrentProcess().Id}, thread: {Thread.CurrentThread.ManagedThreadId}"); var fieldId = $"{metadata.ContainerType.FullName}.{metadata.PropertyName}".ToLowerInvariant(); AttributeFullId = $"{attribute.TypeId}.{fieldId}".ToLowerInvariant(); AttributeWeakId = $"{typeof (T).FullName}.{fieldId}".ToLowerInvariant(); FieldName = metadata.PropertyName; ResetSuffixAllocation(); var item = MapCache <string, CacheItem> .GetOrAdd(AttributeFullId, _ => // map cache is based on static dictionary, set-up once for entire application instance { // (by design, no reason to recompile once compiled expressions) Debug.WriteLine($"[cache add] process: {Process.GetCurrentProcess().Id}, thread: {Thread.CurrentThread.ManagedThreadId}"); var parser = new Parser(); parser.RegisterToolchain(); parser.Parse <bool>(metadata.ContainerType, attribute.Expression); var fields = parser.GetFields(); FieldsMap = fields.ToDictionary(x => x.Key, x => Helper.GetCoarseType(x.Value.Type)); ConstsMap = parser.GetConsts(); ParsersMap = fields .Select(kvp => new { FullName = kvp.Key, ParserAttribute = ((MemberExpression)kvp.Value).Member.GetAttributes <ValueParserAttribute>().SingleOrDefault() }).Where(x => x.ParserAttribute != null) .ToDictionary(x => x.FullName, x => x.ParserAttribute.ParserName); if (!ParsersMap.ContainsKey(metadata.PropertyName)) { var currentField = metadata.ContainerType .GetProperties().Single(p => metadata.PropertyName == p.Name); var valueParser = currentField.GetAttributes <ValueParserAttribute>().SingleOrDefault(); if (valueParser != null) { ParsersMap.Add(new KeyValuePair <string, string>(metadata.PropertyName, valueParser.ParserName)); } } AssertNoNamingCollisionsAtCorrespondingSegments(); attribute.Compile(metadata.ContainerType); // compile expressions in attributes (to be cached for subsequent invocations) return(new CacheItem { FieldsMap = FieldsMap, ConstsMap = ConstsMap, ParsersMap = ParsersMap }); }); FieldsMap = item.FieldsMap; ConstsMap = item.ConstsMap; ParsersMap = item.ParsersMap; Expression = attribute.Expression; IDictionary <string, Guid> errFieldsMap; FormattedErrorMessage = attribute.FormatErrorMessage(metadata.GetDisplayName(), attribute.Expression, metadata.ContainerType, out errFieldsMap); // fields names, in contrast to values, do not change in runtime, so will be provided in message (less code in js) ErrFieldsMap = errFieldsMap; } catch (Exception e) { throw new ValidationException( $"{GetType().Name}: validation applied to {metadata.PropertyName} field failed.", e); } }