简体   繁体   中英

C# ASP.NET Core ModelBinder not Updating Model

I have created a ModelBinder, which only gets triggered if an object has a [Decimal] attribute assigned, yet for some reason, despite it actually sanitising the data it does not seem to update the posted model.

I wonder if someone could see from my code below, where I maybe going wrong.

Startup.cs

public void ConfigureServices(IServiceCollection serviceCollection)
{
    serviceCollection.AddMvc(config => config.ModelBinderProviders.Insert(0, new DecimalModelBinderProvider()));        
}

DecimalModelBinderProvider.cs

public class DecimalModelBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(ModelBinderProviderContext modelBinderProviderContext)
    {
        if (modelBinderProviderContext == null)
        {
            throw new ArgumentNullException(nameof(modelBinderProviderContext));
        }

        if (!modelBinderProviderContext.Metadata.IsComplexType)
        {
            try
            {
                var propertyName = modelBinderProviderContext.Metadata.PropertyName;

                var property = modelBinderProviderContext.Metadata.ContainerType.GetProperty(propertyName);

                if (property != null)
                {
                    var attribute = property.GetCustomAttributes(typeof(DecimalAttribute), false).FirstOrDefault();

                    if (attribute != null)
                    {
                        return new DecimalModelBinder(modelBinderProviderContext.Metadata.ModelType, attribute as IDecimalAttribute);
                    }
                }
            }
            catch (Exception exception)
            {
                var message = exception.Message;

                return null;
            }
        }

        return null;
    }
}

DecimalModelBinder.cs

public class DecimalModelBinder : IModelBinder
{
    private readonly IDecimalAttribute _decimalAttribute;

    private readonly SimpleTypeModelBinder _simpleTypeModelBinder;

    public DecimalModelBinder(Type type, IDecimalAttribute decimalAttribute)
    {
        if (type == null)
        {
            throw new ArgumentNullException(nameof(type));
        }

        _decimalAttribute = decimalAttribute;

        _simpleTypeModelBinder = new SimpleTypeModelBinder(type);
    }

    public Task BindModelAsync(ModelBindingContext modelBindingContext)
    {
        if (modelBindingContext == null)
        {
            throw new ArgumentNullException(nameof(modelBindingContext));
        }

        var valueProviderResult = modelBindingContext.ValueProvider.GetValue(modelBindingContext.ModelName);

        if (valueProviderResult != ValueProviderResult.None)
        {
            modelBindingContext.ModelState.SetModelValue(modelBindingContext.ModelName, valueProviderResult);

            var value = valueProviderResult.FirstValue;

            bool success;

            var result = _decimalAttribute.Decimal(value, out success);

            if (success)
            {
                modelBindingContext.Result = ModelBindingResult.Success(result);

                return Task.CompletedTask;
            }
        }

        return _simpleTypeModelBinder.BindModelAsync(modelBindingContext);
    }
}

IDecimalAttribute.cs

public interface IDecimalAttribute
{
    object Decimal(string value, out bool success);
}

DecimalAttribute.cs

[AttributeUsage(AttributeTargets.Property)]
public class DecimalAttribute : Attribute, IDecimalAttribute
{
    public object Decimal(string value, out bool success)
    {
        var tryModelValue = string.IsNullOrEmpty(value) ? "0.00" : value.Replace("£", "").Replace("%", "");

        decimal @decimal;

        success = decimal.TryParse(tryModelValue, out @decimal);

        return @decimal;
    }
}

Test.cs

public class Test
{
    [Display(Name = "Name", Description = "Name")]
    public string Name { get; set; }

    [Decimal]
    [Display(Name = "Amount", Description = "Amount")]
    public double Amount { get; set; }
}

HomeController

[ValidateAntiForgeryToken]
[HttpPost]
public IActionResult Index(Test test)
{
    if (ModelState.IsValid)
    {

    }

    return View(test);
}

For the purpose of testing I will enter the value £252.83 into the Amount field and submit the form.

If I then place a brakepoint on the line var value = valueProviderResult.FirstValue; I can see that value is £252.83 and if I place a breakpoint on the line modelBindingContext.Result = ModelBindingResult.Success(result); I can see that the result is 252.83M .

However if I step through the code further and place a breakpoint on the line if (ModelState.IsValid) the valid state is false and if I inspect the model test the object Amount is 0.

If anyone can help it would be much appreciated :-)

Try inspect further on the ModelState Error, the Amount property should be invalid and there must be an exception.

I am guessing it should be InvalidCastException. I notice that the Amount property in Test class is Double while you are producing Decimal in your DecimalAttribute.

So the build-in Model Binder that processing the Test class (should be ComplexTypeModelBinder) unable to set the Amount property as it is different type.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM