-
Notifications
You must be signed in to change notification settings - Fork 2.1k
HTML and tag helpers misname elements for expressions like m => ModelType
#5595
Comments
I have two custom binders and they continued working with 1.1. |
I was probably not specific enough : as soon as I update the packages to their 1.1 version, the model binder does not get called at all. Breakpoints do not trigger in the provider neither. |
My model binders are part of a library so I add them after
Try the same, maybe putting them in AddMvc is too early and the option object has not been already populated with the "standard providers". So when it is populated the collection is preliminarly cleared, and your provider is removed. This is the only explanation coming to my mind...since my software works properly also with 1.1 |
@pierre-weceipt can you share more of your app? Or perhaps upload it to GitHub so we can take a look? We're not aware of any changes in this area that could cause this. |
As migrating to 1.1 was not a priority for us I delayed dealing with the issue. After runing more tests the model binder is now correctly called (I don't know what changed since before holidays). What is surprising is that I detected an issue that should have prevented the binder to work before but was not.. One last thing is strange though, the project can run but VS and CLI is telling me packages can't be restored. Here is the message :
my global.json : {
"projects": [ "src", "test" ],
"sdk": {
"version": "1.0.0-preview2-1-003177"
}
}
and my project.json : {
"buildOptions": {
"emitEntryPoint": true,
"preserveCompilationContext": true
},
"dependencies": {
"Microsoft.AspNetCore.Authentication.Cookies": "1.1.0",
"Microsoft.AspNetCore.Diagnostics": "1.1.0",
"Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore": "1.1.0",
"Microsoft.AspNetCore.Identity.EntityFrameworkCore": "1.1.0",
"Microsoft.AspNetCore.Mvc": "1.1.0",
"Microsoft.AspNetCore.Server.IISIntegration": "1.1.0",
"Microsoft.AspNetCore.StaticFiles": "1.1.0",
"Microsoft.EntityFrameworkCore": "1.1.0",
"Microsoft.EntityFrameworkCore.Design": "1.1.0",
"Microsoft.EntityFrameworkCore.SqlServer": "1.1.0",
"Microsoft.EntityFrameworkCore.SqlServer.Design": "1.1.0",
"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.1.0",
"Microsoft.Extensions.Configuration.Json": "1.1.0",
"Microsoft.Extensions.Configuration.UserSecrets": "1.1.0",
"Microsoft.Extensions.Logging": "1.1.0",
"Microsoft.Extensions.Logging.Console": "1.1.0",
"Microsoft.Extensions.Logging.Debug": "1.1.0",
"Microsoft.Extensions.Options.ConfigurationExtensions": "1.1.0",
"Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.1.0",
"Microsoft.VisualStudio.Web.CodeGenerators.Mvc": "1.1.0-preview4-final",
"Microsoft.WindowsAzure.ConfigurationManager": "3.2.3",
"ncalc": "1.3.8",
"Newtonsoft.Json": "9.0.1",
"Sendgrid": "8.0.5",
"WindowsAzure.Storage": "8.0.0"
},
"tools": {
"Microsoft.AspNetCore.Razor.Tools": "1.1.0-preview4-final",
"Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.1.0-preview4-final",
"Microsoft.EntityFrameworkCore.Tools": "1.1.0-preview4-final",
"Microsoft.Extensions.SecretManager.Tools": "1.1.0-preview4-final",
"Microsoft.VisualStudio.Web.CodeGeneration.Tools": "1.1.0-preview4-final"
},
"frameworks": {
"net461": { }
},
"publishOptions": {
"include": [
"wwwroot",
"Views",
"appsettings.json",
"web.config"
]
},
"scripts": {
"prepublish": [ "npm install", "bower install", "gulp clean", "gulp min" ],
"postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
}
}
|
As a follow up, in my view @{
var ModelType = @Model.GetType().FullName;
}
<input asp-for="@ModelType" type="hidden" /> is generating with 1.0.1 <input name="Type" id="Type" type="hidden" value="RatioContentBlock"> and for 1.1 <input name="ModelType" id="ModelType" type="hidden" value="RatioContentBlock"> So I had to change a line in the binder to catch |
@pierre-weceipt generating the name / id "Type" with 1.0.1 looks like a straight-up bug. Alternatively, something else changed in your app when updating to the 1.1.0 packages. (It's not clear where MVC could get that name.) Are you suggesting there's a problem with the new HTML? |
@dougbu no, the only change is that now the name is correctly ModelType instead of previously Type. I think it was a bug in 1.0.1, as I understood razor should be using the var name for input name but was changing ModelType in Type (the previous code was all in a razor view). But this part is fine for me now, I just had to change a string in my model binder to match the new var name. I'm more interested in understanding why the cli is launching the error message but project is still managing to launch. |
Since expression like |
@pierre-weceipt we would like more info to try and reproduce the problem. Can you show the implementation of your model binder and provider? @dougbu can you start taking a look at this? |
@Eilon here is my code : using Microsoft.AspNetCore.Mvc.ModelBinding;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Thelos.NAO.Models.Templates.AccountingTables;
using Thelos.NAO.Models.Templates.Sections.Conditions;
using Thelos.NAO.Models.Templates.Sections.ContentBlocks;
namespace Thelos.NAO.Binders
{
public class TemplateSectionModelBinder : IModelBinder
{
private readonly IModelMetadataProvider _metadataProvider;
private readonly Dictionary<string, IModelBinder> _binders;
public TemplateSectionModelBinder(
IModelMetadataProvider metadataProvider,
Dictionary<string, IModelBinder> binders
)
{
_metadataProvider = metadataProvider;
_binders = binders;
}
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
var typeResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName + ".Type");
if (typeResult == ValueProviderResult.None)
{
bindingContext.Result = ModelBindingResult.Failed();
return;
}
IModelBinder binder;
if (!_binders.TryGetValue(typeResult.FirstValue, out binder))
{
bindingContext.Result = ModelBindingResult.Failed();
return;
}
// Now know the type exists in the assembly.
var type = Type.GetType(typeResult.FirstValue);
if (!typeof(ContentBlock).IsAssignableFrom(type) && !typeof(Condition).IsAssignableFrom(type) && !typeof(Row).IsAssignableFrom(type)
|| (!typeResult.FirstValue.StartsWith("Thelos.NAO.Models.Templates") && !typeResult.FirstValue.StartsWith("Thelos.NAO.Models.AccountingTables")))
{
throw new InvalidOperationException("Bad Type");
}
var metadata = _metadataProvider.GetMetadataForType(type);
var model = Activator.CreateInstance(type);
var result = bindingContext.Result;
bindingContext.Model = model;
using (bindingContext.EnterNestedScope(
metadata,
bindingContext.FieldName,
bindingContext.ModelName,
model: model))
{
await binder.BindModelAsync(bindingContext);
}
bindingContext.Result = ModelBindingResult.Success(model);
return;
}
}
} and the provider using Microsoft.AspNetCore.Mvc.ModelBinding;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Thelos.NAO.Models.Templates.AccountingTables;
using Thelos.NAO.Models.Templates.Sections.Conditions;
using Thelos.NAO.Models.Templates.Sections.ContentBlocks;
namespace Thelos.NAO.Binders
{
public class TemplateSectionModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.Metadata.ModelType == typeof(ContentBlock) || context.Metadata.ModelType == typeof(Condition) || context.Metadata.ModelType == typeof(Row))
{
var binders = new Dictionary<string, IModelBinder>();
foreach (var type in typeof(TemplateSectionModelBinderProvider).GetTypeInfo().Assembly.GetTypes())
{
var typeInfo = type.GetTypeInfo();
if (typeInfo.IsAbstract || typeInfo.IsNested)
{
continue;
}
if (!(typeInfo.IsClass || typeInfo.IsPublic))
{
continue;
}
var metadata = context.MetadataProvider.GetMetadataForType(type);
var binder = context.CreateBinder(metadata);
binders.Add(type.FullName, binder);
}
return new TemplateSectionModelBinder(context.MetadataProvider, binders);
}
return null;
}
}
} declared in the startup file services.AddMvc(options =>
{
options.ModelBinderProviders.Insert(0, new TemplateSectionModelBinderProvider());
}); the views contain @{
var ModelType = @Model.GetType().FullName;
}
<input asp-for="@ModelType" type="hidden" /> |
@pierre-weceipt it's still not clear how your model binder could work. The view should submit the type in a field named If the field name is not the problem, have you debugged and found the provider or binder is not invoked at all? |
@dougbu I agree with you, this shouldn't work, yet it does. Razor is generating the field as 'Type' instead of 'ModelType' when in 1.0.x (this code is for 1.0.x). |
@pierre-weceipt I'd appreciate a small repro for the problem in 1.0.x. Do you still have a sample showing the wrong name generation? Ideal is a small project in a public GitHub repo. |
I've confirmed this issue and found it repros when using HTML helpers, including HTML helpers in MVC 5.2.3. Surprised nobody found the issue before; problem has been in the code forever. Bug is an overly-accepting Problem does not repro in MVC 1.1.0-preview1 and later because we rewrote if (text.StartsWith(".model", StringComparison.OrdinalIgnoreCase))
{
// 6 is the length of the string ".model".
builder.Remove(0, 6);
} |
m => ModelType
Updated issue title and cleared labels other than bug. @Eilon I'm torn on this one. It can be worked around with new variable names and it appears our customers haven't been impacted over the years. On the other hand, the generated names break model binding, there's no indication where the problem lies, and the fix is simple. Either way, let's do this all-or-nothing: If we patch Core 1.0.x, I suggest we also patch 5.2.4. |
Proposing this for next 1.0.x patch. Bug is not an issue in 1.1.x. |
This patch bug is approved. Please use the normal code review process w/ a PR and make sure the fix is in the correct branch, then close the bug and mark it as done. |
- #5595 - enhance the `StartsWith()` check to look at what comes after
- relates to #5595 which was a 1.0.x-only problem
Hi, as stated in the title, my custom model binder stopped working after updating to core 1.1. I couldn't find any anouncement for changes on model binding, am I missing a new package or something?
Here is my startup file :
Thanks!
The text was updated successfully, but these errors were encountered: