asp.net-mvc – 表单帖子永远不会执行操作,并且在MVC 6 RC2中有
我在MVC 6 RC1到RC2迁移后遇到了一些奇怪的行为.
假设我们有一个表单的愚蠢版本,它会在提交时POST到一个动作: @model InstitutionViewModel <form asp-controller="Institution" asp-action="Create" method="post"> @Html.Hidden("companyId",ViewBag.CompanyId) @Html.DropDownListFor(Model => Model.LocationId,(List<SelectListItem>)ViewBag.Locations,new { Class = "form-control" }) @Html.TextAreaFor(model => model.Description,new { Class = "form-control" }) <input type="submit" value="Submit" class="btn btn-success" /> </form> 然后我们有这个InstitutionViewModel public class InstitutionViewModel { public int Id { get; set; } public string Description { get; set; } public int LocationId { get; set; } public LocationViewModel Location { get; set; } } 我们正在发布的动作,看起来像这样 [HttpPost] public IActionResult Create(int companyId,InstitutionViewModel institution) { ... } 我遇到的问题是提交永远不会触发操作.浏览器显示微调器,后台发生了一些事情,但程序从未到达该操作.更糟糕的是 – 当发生这种情况时,dotnet进程的RAM消耗开始逐渐上升,直到它用完为止.我最后一次让网站在这种状态下运行,dotnet进程使用的是7GB内存,只需要2到3分钟就可以达到这一点! 这曾经在RC1中没有任何问题.到目前为止,我找到的唯一解决方案是从InstitutionViewModel中删除LocationViewModel属性.如果我这样做,POST就会毫无问题地完成操作. LocationViewModel本身似乎也不是问题,因为如果类中有任何其他viewModel作为属性,则会发生同样的情况,无论viewModel包含什么. 现在我很困惑天气这是RC2中的一个错误,或者我正在做一些可怕的错误.也许我忘了包含一些东西,或者在升级到RC2时我在Startup.cs和project.json中破坏了一些东西.有人有什么想法吗? 解决方法
它是a known bug in ASP.NET Core MVC RC2,由默认模型绑定工厂中深度嵌套模型的错误处理引起. 建议的解决方法是to use a custom binder factory until it is fixed: public class MyModelBinderFactory : IModelBinderFactory { private readonly IModelMetadataProvider _metadataProvider; private readonly IModelBinderProvider[] _providers; private readonly ConcurrentDictionary<object,IModelBinder> _cache; /// <summary> /// Creates a new <see cref="ModelBinderFactory"/>. /// </summary> /// <param name="metadataProvider">The <see cref="IModelMetadataProvider"/>.</param> /// <param name="options">The <see cref="IOptions{TOptions}"/> for <see cref="MvcOptions"/>.</param> public MyModelBinderFactory(IModelMetadataProvider metadataProvider,IOptions<MvcOptions> options) { _metadataProvider = metadataProvider; _providers = options.Value.ModelBinderProviders.ToArray(); _cache = new ConcurrentDictionary<object,IModelBinder>(); } /// <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 = $"Could not create model binder for {providerContext.Metadata.ModelType}."; throw new InvalidOperationException(message); } if (context.CacheToken != null) { _cache.TryAdd(context.CacheToken,binder); } return binder; } 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 collection = providerContext.Collection; IModelBinder binder; if (collection.TryGetValue(key,out binder)) { if (binder != null) { return binder; } // Recursion detected,create a DelegatingBinder. binder = new PlaceholderBinder(); collection[key] = binder; return binder; } // OK this isn't a recursive case (yet) so "push" an entry on the stack and then ask the providers // to create the binder. collection.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 (result == null && token == null) { // Use a no-op binder if we're below the top level. At the top level,we throw. result = NoOpBinder.Instance; } // If the DelegatingBinder was created,then it means we recursed. Hook it up to the 'real' binder. var delegatingBinder = collection[key] as PlaceholderBinder; if (delegatingBinder != null) { delegatingBinder.Inner = result; } collection[key] = result; return result; } private class DefaultModelBinderProviderContext : ModelBinderProviderContext { private readonly MyModelBinderFactory _factory; public DefaultModelBinderProviderContext( MyModelBinderFactory factory,ModelBinderFactoryContext factoryContext) { _factory = factory; Metadata = factoryContext.Metadata; BindingInfo = factoryContext.BindingInfo; MetadataProvider = _factory._metadataProvider; Collection = new Dictionary<Key,IModelBinder>(); } private DefaultModelBinderProviderContext( DefaultModelBinderProviderContext parent,ModelMetadata metadata) { Metadata = metadata; _factory = parent._factory; MetadataProvider = parent.MetadataProvider; Collection = parent.Collection; BindingInfo = new BindingInfo() { BinderModelName = metadata.BinderModelName,BinderType = metadata.BinderType,BindingSource = metadata.BindingSource,PropertyFilterProvider = metadata.PropertyFilterProvider,}; } public override BindingInfo BindingInfo { get; } public override ModelMetadata Metadata { get; } public override IModelMetadataProvider MetadataProvider { get; } // Not using a 'real' Stack<> because we want random access to modify the entries. public Dictionary<Key,IModelBinder> Collection { get; } public override IModelBinder CreateBinder(ModelMetadata metadata) { var nestedContext = new DefaultModelBinderProviderContext(this,metadata); return _factory.CreateBinderCore(nestedContext,token: null); } } [DebuggerDisplay("{ToString(),nq}")] private struct Key : IEquatable<Key> { private readonly ModelMetadata _metadata; private readonly object _token; // Explicitly using ReferenceEquality for tokens. public Key(ModelMetadata metadata,object token) { _metadata = metadata; _token = token; } public bool Equals(Key other) { return _metadata.Equals(other._metadata) && object.ReferenceEquals(_token,other._token); } public override bool Equals(object obj) { var other = obj as Key?; return other.HasValue && Equals(other.Value); } public override int GetHashCode() { return _metadata.GetHashCode() ^ RuntimeHelpers.GetHashCode(_token); } public override string ToString() { if (_metadata.MetadataKind == ModelMetadataKind.Type) { return $"{_token} (Type: '{_metadata.ModelType.Name}')"; } else { return $"{_token} (Property: '{_metadata.ContainerType.Name}.{_metadata.PropertyName}' Type: '{_metadata.ModelType.Name}')"; } } } } 您可以从Startup.ConfigureServices在DI容器中注册它: services.AddSingleton<IModelBinderFactory,MyModelBinderFactory>(); (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- 我到底是什么时候需要在经典的asp中将对象设置为空?
- asp.net-mvc – 在Generic Repository中定义Default构造函数
- Crystal Reports 13和Asp.Net 3.5
- asp.net – 将RSS pubDate格式化为.NET DateTime
- asp.net-mvc-3 – 使用.SetValidator()时,FluentValidation
- asp.net-mvc – 在ASP.NET MVC 2中的RadioButtonFor
- asp.net-mvc – MVC框架是否配备了丰富的页面设计?
- asp.net – 如何将模型从一个局部视图传递到另一个局部视图
- asp.net-mvc – 使用Automapper映射嵌套类
- asp.net – App_Data / ASPNETDB.MDF到Sql Server 2005(或0