dzenesiz
dzenesiz

Reputation: 1542

Implement custom ModelBinder ASP.NET MVC

I am having the same problem many people already had - Model Binder doesn't accept localized decimal input. In all of the threads, here and other forums, the recommended solution is implementing a custom ModelBinder.

My problem is that those solutions somehow don't work for me. Let's use this solution for example: comma decimal seperator in asp.net mvc 5

When I reference all namespaces, two errors remain:

Error CS0115 'DecimalModelBinder.BindModel(ControllerContext, ModelBindingContext)': no suitable method found to override ...

and

Error CS0173 Type of conditional expression cannot be determined because there is no implicit conversion between 'bool' and 'decimal'

Where the second one references the entire return statement.

Did something change in the MVC Framework, so this code is outdated, or am I doing something wrong?

The code I ended up with is:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.ModelBinding;
using System.Web.Mvc;

namespace AetMuzickaOprema.App_Start
{
    public class DecimalModelBinder : System.Web.ModelBinding.DefaultModelBinder
    {
        public override object BindModel(ControllerContext controllerContext, System.Web.ModelBinding.ModelBindingContext bindingContext) //first error
        {
            var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);

            return valueProviderResult == null ? base.BindModel(controllerContext, bindingContext) : Convert.ToDecimal(valueProviderResult.AttemptedValue); //second error

        }
    }
}

Model property in question:

[Required]
[Range(typeof(decimal), "0", "999999999")]
public decimal Price { get; set; }

Upvotes: 1

Views: 2093

Answers (2)

Alexander Christov
Alexander Christov

Reputation: 10045

In ASP.NET Core 1.1 (Microsoft.AspNetCore.Mvc.Core.Abstraction, 1.0.1.0) you'll see the following interface

using System.Threading.Tasks;

namespace Microsoft.AspNetCore.Mvc.ModelBinding
{
    //
    // Summary:
    //     Defines an interface for model binders.
    public interface IModelBinder
    {
        //
        // Summary:
        //     Attempts to bind a model.
        //
        // Parameters:
        //   bindingContext:
        //     The Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingContext.
        //
        // Returns:
        //     A System.Threading.Tasks.Task which will complete when the model binding process
        //     completes.
        //     If model binding was successful, the Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingContext.Result
        //     should have Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingResult.IsModelSet
        //     set to true.
        //     A model binder that completes successfully should set Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingContext.Result
        //     to a value returned from Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingResult.Success(System.Object).
        Task BindModelAsync(ModelBindingContext bindingContext);
    }
}

Model binders are defined in Microsoft.AspNetCore.Mvc.ModelBinding.Binders namespace, assembly Microsoft.AspNetCore.Mvc.Core, Version=1.0.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60. None of them seem to expose BindModel() method, even less a virtual one. It looks like you are trying to override a non-existing method.

A better approach is to take an existing ModelBinder, one that best suits your needs, inherit from it, and override ModelBindAsync().

Upvotes: 1

Brian Mains
Brian Mains

Reputation: 50728

It seems ultimately what you are trying to achieve is property-level binding such as this?:

[PropertyBinder(typeof(PropertyBBinder))]
public IList<int> PropertyB {get; set;}

If that is correct, this post offers a solution: Custom model binder for a property

Thanks.

Upvotes: 1

Related Questions