C#ASP.NET Core ModelBinder不更新模型

时间:2017-09-05 13:28:47

标签: c# asp.net-core-mvc model-validation custom-model-binder

我创建了一个ModelBinder,只有在一个对象分配了[Decimal]属性时才会被触发,但由于某种原因,尽管它实际上正在清理数据,但似乎并没有更新已发布的模型。

我想知道是否有人可以从下面的代码中看到我可能出错的地方。

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);
}

为了测试,我将在金额字段中输入值£252.83并提交表格。

如果我在行var value = valueProviderResult.FirstValue;上放置制动点,我可以看到该值为252.83英镑,如果我在行modelBindingContext.Result = ModelBindingResult.Success(result);上放置一个断点,我可以看到结果是{{1} }。

但是,如果我进一步单步执行代码并在行252.83M上放置断点,则有效状态为false,如果我检查模型if (ModelState.IsValid),则对象test为0。 / p>

如果有人能提供帮助,我将不胜感激: - )

1 个答案:

答案 0 :(得分:0)

尝试进一步检查ModelState错误,Amount属性应该无效并且必须有异常。

我猜它应该是InvalidCastException。我注意到,在DecimalAttribute中生成Decimal时,Test类中的Amount属性为Double。

因此,处理Test类(应该是ComplexTypeModelBinder)的内置Model Binder无法设置Amount属性,因为它是不同的类型。