Ejemplo n.º 1
0
        /// <inheritdoc />
        public IModelBinder CreateBinder(ModelBinderFactoryContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            // We perform caching in CreateBinder (not in CreateBinderCore) because we only want to
            // cache the top-level binder.
            IModelBinder binder;

            if (context.CacheToken != null && _cache.TryGetValue(context.CacheToken, out binder))
            {
                return(binder);
            }

            var providerContext = new DefaultModelBinderProviderContext(this, context);

            binder = CreateBinderCore(providerContext, context.CacheToken);
            if (binder == null)
            {
                var message = Resources.FormatCouldNotCreateIModelBinder(providerContext.Metadata.ModelType);
                throw new InvalidOperationException(message);
            }

            if (context.CacheToken != null)
            {
                _cache.TryAdd(context.CacheToken, binder);
            }

            return(binder);
        }
Ejemplo n.º 2
0
        public IIndexModelBinder CreateBinder(ModelBinderFactoryContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (_providers.Length == 0)
            {
                throw new InvalidOperationException($"Missing required services FormatModelBinderProvidersAreRequired");
            }

            IIndexModelBinder binder;

            if (TryGetCachedBinder(context.Metadata, context.CacheToken, out binder))
            {
                return(binder);
            }

            var providerContext = new DefaultModelBinderProviderContext(this, context);

            binder = CreateBinderCoreUncached(providerContext, context.CacheToken);
            if (binder == null)
            {
                var message = "FormatCouldNotCreateIModelBinder";
                throw new InvalidOperationException(message);
            }

            Debug.Assert(!(binder is PlaceholderBinder));
            AddToCache(context.Metadata, context.CacheToken, binder);

            return(binder);
        }
Ejemplo n.º 3
0
        /// <inheritdoc />
        public IModelBinder CreateBinder(ModelBinderFactoryContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            IModelBinder binder;

            if (TryGetCachedBinder(context.Metadata, context.CacheToken, out binder))
            {
                return(binder);
            }

            // Perf: We're calling the Uncached version of the API here so we can:
            // 1. avoid allocating a context when the value is already cached
            // 2. avoid checking the cache twice when the value is not cached
            var providerContext = new DefaultModelBinderProviderContext(this, context);

            binder = CreateBinderCoreUncached(providerContext, context.CacheToken);
            if (binder == null)
            {
                var message = Resources.FormatCouldNotCreateIModelBinder(providerContext.Metadata.ModelType);
                throw new InvalidOperationException(message);
            }

            Debug.Assert(!(binder is PlaceholderBinder));
            AddToCache(context.Metadata, context.CacheToken, binder);

            return(binder);
        }
Ejemplo n.º 4
0
        /// <inheritdoc />
        public IModelBinder CreateBinder(ModelBinderFactoryContext context)
        {
            if (TryGetCachedBinder(context.Metadata, context.CacheToken, out var binder))
            {
                return(binder);
            }

            var providerContext = new DefaultModelBinderProviderContext(this, context);

            foreach (var provider in _providers)
            {
                binder = provider.GetBinder(providerContext);
                if (binder != null)
                {
                    break;
                }
            }

            if (binder == null)
            {
                throw new InvalidOperationException(
                          $"Could not find binder for model type {context.Metadata.ModelType}");
            }

            AddToCache(context.Metadata, context.CacheToken, binder);
            return(binder);
        }
Ejemplo n.º 5
0
            private DefaultModelBinderProviderContext(
                DefaultModelBinderProviderContext parent,
                ModelMetadata metadata,
                BindingInfo bindingInfo)
            {
                Metadata = metadata;

                _factory         = parent._factory;
                MetadataProvider = parent.MetadataProvider;
                Visited          = parent.Visited;
                BindingInfo      = bindingInfo;
            }
Ejemplo n.º 6
0
        private IIndexModelBinder CreateBinderCoreUncached(DefaultModelBinderProviderContext providerContext, object token)
        {
            if (!providerContext.Metadata.IsBindingAllowed)
            {
                return(IndexNoOpBinder.Instance);
            }

            var key = new Key(providerContext.Metadata, token);

            var visited = providerContext.Visited;

            IIndexModelBinder binder;

            if (visited.TryGetValue(key, out binder))
            {
                if (binder != null)
                {
                    return(binder);
                }

                binder       = new IndexPlaceholderBinder();
                visited[key] = binder;
                return(binder);
            }

            visited.Add(key, null);

            IIndexModelBinder result = null;

            for (var i = 0; i < _providers.Length; i++)
            {
                var provider = _providers[i];
                result = provider.GetBinder(providerContext);
                if (result != null)
                {
                    break;
                }
            }

            var placeholderBinder = visited[key] as IndexPlaceholderBinder;

            if (placeholderBinder != null)
            {
                placeholderBinder.Inner = result ?? IndexNoOpBinder.Instance;
            }

            if (result != null)
            {
                visited[key] = result;
            }

            return(result);
        }
Ejemplo n.º 7
0
            public override IIndexModelBinder CreateBinder(ModelMetadata metadata)
            {
                if (metadata == null)
                {
                    throw new ArgumentNullException(nameof(metadata));
                }

                var token = metadata;

                var nestedContext = new DefaultModelBinderProviderContext(this, metadata);

                return(_factory.CreateBinderCoreCached(nestedContext, token));
            }
Ejemplo n.º 8
0
            public override IModelBinder CreateBinder(ModelMetadata metadata)
            {
                if (metadata == null)
                {
                    throw new ArgumentNullException(nameof(metadata));
                }

                // For non-root nodes we use the ModelMetadata as the cache token. This ensures that all non-root
                // nodes with the same metadata will have the the same binder. This is OK because for an non-root
                // node there's no opportunity to customize binding info like there is for a parameter.
                var token = metadata;

                var nestedContext = new DefaultModelBinderProviderContext(this, metadata);

                return(_factory.CreateBinderCoreCached(nestedContext, token));
            }
Ejemplo n.º 9
0
            private DefaultModelBinderProviderContext(
                DefaultModelBinderProviderContext parent,
                ModelMetadata metadata)
            {
                Metadata = metadata;

                _factory         = parent._factory;
                MetadataProvider = parent.MetadataProvider;
                Visited          = parent.Visited;

                BindingInfo = new BindingInfo()
                {
                    BinderModelName        = metadata.BinderModelName,
                    BinderType             = metadata.BinderType,
                    BindingSource          = metadata.BindingSource,
                    PropertyFilterProvider = metadata.PropertyFilterProvider,
                };
            }
Ejemplo n.º 10
0
        // Called by the DefaultModelBinderProviderContext when we're recursively creating a binder
        // so that all intermediate results can be cached.
        private IModelBinder CreateBinderCoreCached(DefaultModelBinderProviderContext providerContext, object token)
        {
            if (TryGetCachedBinder(providerContext.Metadata, token, out var binder))
            {
                return(binder);
            }

            // We're definitely creating a binder for an non-root node here, so it's OK for binder creation
            // to fail.
            binder = CreateBinderCoreUncached(providerContext, token) ?? NoOpBinder.Instance;

            if (!(binder is PlaceholderBinder))
            {
                AddToCache(providerContext.Metadata, token, binder);
            }

            return(binder);
        }
Ejemplo n.º 11
0
        private IIndexModelBinder CreateBinderCoreCached(DefaultModelBinderProviderContext providerContext, object token)
        {
            IIndexModelBinder binder;

            if (TryGetCachedBinder(providerContext.Metadata, token, out binder))
            {
                return(binder);
            }

            binder = CreateBinderCoreUncached(providerContext, token) ?? IndexNoOpBinder.Instance;

            if (!(binder is PlaceholderBinder))
            {
                AddToCache(providerContext.Metadata, token, binder);
            }

            return(binder);
        }
Ejemplo n.º 12
0
        private IModelBinder CreateBinderCoreUncached(DefaultModelBinderProviderContext providerContext, object token)
        {
            if (!providerContext.Metadata.IsBindingAllowed)
            {
                return(NoOpBinder.Instance);
            }

            // A non-null token will usually be passed in at the the top level (ParameterDescriptor likely).
            // This prevents us from treating a parameter the same as a collection-element - which could
            // happen looking at just model metadata.
            var key = new Key(providerContext.Metadata, token);

            // The providerContext.Visited is used here to break cycles in recursion. We need a separate
            // per-operation cache for cycle breaking because the global cache (_cache) needs to always stay
            // in a valid state.
            //
            // We store null as a sentinel inside the providerContext.Visited to track the fact that we've visited
            // a given node but haven't yet created a binder for it. We don't want to eagerly create a
            // PlaceholderBinder because that would result in lots of unnecessary indirection and allocations.
            var visited = providerContext.Visited;

            IModelBinder binder;

            if (visited.TryGetValue(key, out binder))
            {
                if (binder != null)
                {
                    return(binder);
                }

                // If we're currently recursively building a binder for this type, just return
                // a PlaceholderBinder. We'll fix it up later to point to the 'real' binder
                // when the stack unwinds.
                binder       = new PlaceholderBinder();
                visited[key] = binder;
                return(binder);
            }

            // OK this isn't a recursive case (yet) so add an entry and then ask the providers
            // to create the binder.
            visited.Add(key, null);

            IModelBinder result = null;

            for (var i = 0; i < _providers.Length; i++)
            {
                var provider = _providers[i];
                result = provider.GetBinder(providerContext);
                if (result != null)
                {
                    break;
                }
            }

            // If the PlaceholderBinder was created, then it means we recursed. Hook it up to the 'real' binder.
            var placeholderBinder = visited[key] as PlaceholderBinder;

            if (placeholderBinder != null)
            {
                // It's also possible that user code called into `CreateBinder` but then returned null, we don't
                // want to create something that will null-ref later so just hook this up to the no-op binder.
                placeholderBinder.Inner = result ?? NoOpBinder.Instance;
            }

            if (result != null)
            {
                visited[key] = result;
            }

            return(result);
        }
Ejemplo n.º 13
0
        private IModelBinder CreateBinderCore(DefaultModelBinderProviderContext providerContext, object token)
        {
            if (!providerContext.Metadata.IsBindingAllowed)
            {
                return(NoOpBinder.Instance);
            }

            // A non-null token will usually be passed in at the the top level (ParameterDescriptor likely).
            // This prevents us from treating a parameter the same as a collection-element - which could
            // happen looking at just model metadata.
            var key = new Key(providerContext.Metadata, token);

            // If we're currently recursively building a binder for this type, just return
            // a PlaceholderBinder. We'll fix it up later to point to the 'real' binder
            // when the stack unwinds.
            var stack = providerContext.Stack;

            for (var i = 0; i < stack.Count; i++)
            {
                var entry = stack[i];
                if (key.Equals(entry.Key))
                {
                    if (entry.Value == null)
                    {
                        // Recursion detected, create a DelegatingBinder.
                        var binder = new PlaceholderBinder();
                        stack[i] = new KeyValuePair <Key, PlaceholderBinder>(entry.Key, binder);
                        return(binder);
                    }
                    else
                    {
                        return(entry.Value);
                    }
                }
            }

            // OK this isn't a recursive case (yet) so "push" an entry on the stack and then ask the providers
            // to create the binder.
            stack.Add(new KeyValuePair <Key, PlaceholderBinder>(key, null));

            IModelBinder result = null;

            for (var i = 0; i < _providers.Length; i++)
            {
                var provider = _providers[i];
                result = provider.GetBinder(providerContext);
                if (result != null)
                {
                    break;
                }
            }

            if (result == null && stack.Count > 1)
            {
                // Use a no-op binder if we're below the top level. At the top level, we throw.
                result = NoOpBinder.Instance;
            }

            // "pop"
            Debug.Assert(stack.Count > 0);
            var delegatingBinder = stack[stack.Count - 1].Value;

            stack.RemoveAt(stack.Count - 1);

            // If the DelegatingBinder was created, then it means we recursed. Hook it up to the 'real' binder.
            if (delegatingBinder != null)
            {
                delegatingBinder.Inner = result;
            }

            return(result);
        }
Ejemplo n.º 14
0
            public override IModelBinder CreateBinder(ModelMetadata metadata)
            {
                var nestedContext = new DefaultModelBinderProviderContext(this, metadata);

                return(_factory.CreateBinderCore(nestedContext, token: null));
            }