예제 #1
0
        /// <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);
            }
        }