/// <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); }
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); }
/// <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); }
/// <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); }
private DefaultModelBinderProviderContext( DefaultModelBinderProviderContext parent, ModelMetadata metadata, BindingInfo bindingInfo) { Metadata = metadata; _factory = parent._factory; MetadataProvider = parent.MetadataProvider; Visited = parent.Visited; BindingInfo = bindingInfo; }
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); }
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)); }
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)); }
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, }; }
// 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); }
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); }
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); }
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); }
public override IModelBinder CreateBinder(ModelMetadata metadata) { var nestedContext = new DefaultModelBinderProviderContext(this, metadata); return(_factory.CreateBinderCore(nestedContext, token: null)); }