ASP.NET Core中如何脱离默认流程手动实现模型绑定
ASP.NET Core 手动模型绑定实现方案
ASP.NET Core 完全支持脱离默认MVC管道手动执行模型绑定,内置组件可直接实现你贴出的Orchard CMS在ASP.NET MVC 4下的同等能力,不需要引入第三方依赖。
新旧版本API对应关系
你贴的旧版代码用到的核心组件,在ASP.NET Core里都有直接对应实现:
- 旧版
DefaultModelBinder:Core中通过IModelBinderFactory创建对应类型的绑定器,复杂类型默认使用ComplexObjectModelBinder,支持所有默认的绑定规则、数据注解校验 - 旧版
ModelMetadataProviders.Current:Core中对应IModelMetadataProvider,负责提供模型类型的元数据 - 旧版
DictionaryValueProvider<string>:Core中同名类位于Microsoft.AspNetCore.Mvc.ModelBinding命名空间,逻辑完全兼容,可直接传入字典作为数据源 - 旧版
ModelBindingContext:Core中对应DefaultModelBindingContext,提供了工厂方法快速初始化绑定上下文
两种场景的实现代码
1. 完全脱离HTTP请求管道的纯手动绑定
这种场景不需要启动ASP.NET Core宿主,可直接在任意.NET应用中使用,和你贴的Orchard代码使用方式完全一致:
using Microsoft.AspNetCore.Mvc.ModelBinding; using System.Globalization; public static class ManualModelBinder { private static readonly IModelMetadataProvider _metadataProvider = new DefaultModelMetadataProvider( new EmptyCompositeMetadataDetailsProvider()); private static readonly IReadOnlyList<IModelBinderProvider> _binderProviders = new List<IModelBinderProvider> { new SimpleTypeModelBinderProvider(), new ComplexTypeModelBinderProvider(), new CollectionModelBinderProvider(), new DictionaryModelBinderProvider() }; public static T? TryGetModel<T>(string key, IDictionary<string, string> source) where T : class { var valueProvider = new DictionaryValueProvider<string>(source, CultureInfo.InvariantCulture); var modelMetadata = _metadataProvider.GetMetadataForType(typeof(T)); var binderFactoryContext = new ModelBinderProviderContext(modelMetadata, _metadataProvider, _binderProviders); var binder = new ComplexObjectModelBinderProvider().CreateBinder(binderFactoryContext); var bindingContext = new DefaultModelBindingContext { ModelMetadata = modelMetadata, ModelName = key, ValueProvider = valueProvider, ModelState = new ModelStateDictionary() }; binder.BindModelAsync(bindingContext).Wait(); return bindingContext.Result.IsModelSet ? bindingContext.Result.Model as T : null; } }
调用示例:
var source = new Dictionary<string, string> { ["Id"] = "1001", ["Name"] = "张三", ["Age"] = "25" }; var user = ManualModelBinder.TryGetModel<User>("", source);
2. 在ASP.NET Core请求管道内手动绑定
如果是在Controller、Filter、中间件等管道场景下使用,不需要手动初始化元数据、绑定器提供器,直接注入管道内已经配置好的实例即可,会自动复用你全局配置的绑定规则、本地化、自定义绑定器等设置:
// 在Controller中使用示例 public async Task<IActionResult> BindFromDict([FromServices] IModelBinderFactory binderFactory, [FromServices] IModelMetadataProvider metadataProvider) { var source = new Dictionary<string, string> { ["User:Id"] = "1001", ["User:Name"] = "张三" }; var valueProvider = new DictionaryValueProvider<string>(source, CultureInfo.InvariantCulture); var modelMetadata = metadataProvider.GetMetadataForType(typeof(User)); var binder = binderFactory.CreateBinder(new ModelBinderFactoryContext { Metadata = modelMetadata }); var bindingContext = DefaultModelBindingContext.CreateBindingContext( ControllerContext, valueProvider, modelMetadata, bindingInfo: null, modelName: "User"); await binder.BindModelAsync(bindingContext); // 绑定结果 var user = bindingContext.Result.Model as User; // 校验结果和默认管道一致,从ModelState中获取 var isValid = bindingContext.ModelState.IsValid; return Ok(user); }
注意事项
- 纯手动场景如果需要支持集合、字典类型的属性绑定,在
_binderProviders列表中加入对应提供器即可,和上面代码中已经加入的CollectionModelBinderProvider、DictionaryModelBinderProvider用法一致 - 绑定过程中产生的所有数据校验错误都会写入
ModelStateDictionary,和默认模型绑定的校验逻辑完全一致 - 除了字典数据源,你可以替换成任意实现了
IValueProvider接口的数据源,比如从JSON、XML、自定义配置结构读取数据做绑定。
内容的提问来源于stack exchange,提问作者Bochen Lin
相关产品推荐
相关产品推荐

