.NET Core开发日志——Model Binding (3)

再到ControllerBinderDelegateProvider类的CreateBinderDelegate方法中,找到valueProvider创建的起始点。

async Task Bind(ControllerContext controllerContext, object controller, Dictionary<string, object> arguments) { var valueProvider = await CompositeValueProvider.CreateAsync(controllerContext); var parameters = actionDescriptor.Parameters; for (var i = 0; i < parameters.Count; i++) { var parameter = parameters[i]; var bindingInfo = parameterBindingInfo[i]; var modelMetadata = bindingInfo.ModelMetadata; if (!modelMetadata.IsBindingAllowed) { continue; } var result = await parameterBinder.BindModelAsync( controllerContext, bindingInfo.ModelBinder, valueProvider, parameter, modelMetadata, value: null); if (result.IsModelSet) { arguments[parameter.Name] = result.Model; } } ... }

所得到的valueProvider在ParameterBinder类的BindModelAsync方法里还要再作进一步的处理。先作为参数传入创建DefaultModelBindingContext的方法:

var modelBindingContext = DefaultModelBindingContext.CreateBindingContext( actionContext, valueProvider, metadata, parameter.BindingInfo, parameter.Name);

再对ValueProvider作过滤处理:

return new DefaultModelBindingContext() { ActionContext = actionContext, BinderModelName = binderModelName, BindingSource = bindingSource, PropertyFilter = propertyFilterProvider?.PropertyFilter, // Because this is the top-level context, FieldName and ModelName should be the same. FieldName = binderModelName ?? modelName, ModelName = binderModelName ?? modelName, IsTopLevelObject = true, ModelMetadata = metadata, ModelState = actionContext.ModelState, OriginalValueProvider = valueProvider, ValueProvider = FilterValueProvider(valueProvider, bindingSource), ValidationState = new ValidationStateDictionary(), };

FilterValueProvider方法最终会调用CompositeValueProvider类的Filter方法,以得到所有合适的valueProvider。

public IValueProvider Filter(BindingSource bindingSource) { ... var filteredValueProviders = new List<IValueProvider>(); foreach (var valueProvider in this.OfType<IBindingSourceValueProvider>()) { var result = valueProvider.Filter(bindingSource); if (result != null) { filteredValueProviders.Add(result); } } ... return new CompositeValueProvider(filteredValueProviders); }

那么当在ModelBinder的BindModelAsync方法里需要获取数据时,以FloatModelBinder为例:

public Task BindModelAsync(ModelBindingContext bindingContext) { ... var modelName = bindingContext.ModelName; var valueProviderResult = bindingContext.ValueProvider.GetValue(modelName); ... }

会试图从已过滤的ValueProvider中获取值。这时还是利用了CompositeValueProvider类中的方法。

public virtual ValueProviderResult GetValue(string key) { // Performance-sensitive // Caching the count is faster for IList<T> var itemCount = Items.Count; for (var i = 0; i < itemCount; i++) { var valueProvider = Items[i]; var result = valueProvider.GetValue(key); if (result != ValueProviderResult.None) { return result; } } return ValueProviderResult.None; }

这里的逻辑是从valueProvider集合中逐一尝试取值,有数据的则直接返回。

这也意味着数据绑定会以FormValueProvider到RouteValueProvider,再到QueryStringValueProvider,最后向JQueryFormValueProvider取值,这一流程执行,中间如果有任何一个能得到数据的话,则不再继续访问后面的ValueProvider。当然,前提是这些ValueProvider要不被先前的过滤处理排除在外。

若是还不明白这一顺序关系的话,可以回想下从ValueProviderFactories的添加顺序,再至ValueProvider集合生成时各个ValueProvider的顺序,就比较容易了解其中道理了。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyjjzf.html