Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.NET core custom and default binding combined

I'm creating a custom model binder for a view model, implementing IModelBinder

I have a lot of properties in my view model, the majority of which do not need any custom binding. Rather than explicitly set all of the property values on my model individually from the ModelBindingContext, I would to be able to get the framework to bind the model for me, then I would carry out any custom binding:

public class ApplicationViewModelBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext == null)
        {
            throw new ArgumentNullException(nameof(bindingContext));
        }

        // get .net core to bind values on model

        // Cary out any customization of the models properties

        bindingContext.Result = ModelBindingResult.Success(bindingContext.Model);
        return Task.CompletedTask; 
    }
}

Basically I want to carry out the default model binding, then apply custom binding, similar to the approach taken in this SO post but for .NET Core, not framework.

I assumed applying the default binding would be straight forward, but haven't been able to find out how to do so. I believe the solution would involve ComplexTypeModelBinder and ComplexTypeModelBinderProvider classes, but can't seem to find out how to go about it.

I know I could just make any changes when the POST request hits my controller method, but this seem the wrong place and wrong time to do so.

like image 555
Dave0504 Avatar asked Aug 19 '18 19:08

Dave0504


People also ask

What is two way binding MVC?

So the two-way data binding means we can perform both read and write operations. In our previous article Data Binding in Spring MVC with Example, we have discussed how to write-to-variable task and in this article, we mainly focus on the read-from-a-variable task.

What is Bindproperty attribute?

Model binding allows you map request parameters to actions. This means action methods will have one or more parameters and those parameters will receive their values from the model binding framework.

What is FromQuery?

[FromQuery] - Gets values from the query string. [FromRoute] - Gets values from route data. [FromForm] - Gets values from posted form fields. [FromBody] - Gets values from the request body.

What is FromBody ASP NET core?

The [FromBody] attribute which inherits ParameterBindingAttribute class is used to populate a parameter and its properties from the body of an HTTP request. The ASP.NET runtime delegates the responsibility of reading the body to an input formatter.


1 Answers

For custom ComplexTypeModelBinder, you could inherit from ComplexTypeModelBinder.

  1. Model
    public class BinderModel
    {
       public int Id { get; set; }
       public string Name { get; set; }
       public string BinderValue { get; set; }
    }
  1. Controller Action
    [HttpPost]
    public void Post([FromForm]BinderModel value)
    {

    }
  1. CustomBinder
    public class CustomBinder : ComplexTypeModelBinder
    {
        private readonly IDictionary<ModelMetadata, IModelBinder> _propertyBinders;
        public CustomBinder(IDictionary<ModelMetadata, IModelBinder> propertyBinders)
        : base(propertyBinders)
        {
            _propertyBinders = propertyBinders;
        }
        protected override Task BindProperty(ModelBindingContext bindingContext)
        {
            if (bindingContext.FieldName == "BinderValue")
            {
                bindingContext.Result = ModelBindingResult.Success("BinderValueTest");
                return Task.CompletedTask;
            }
            else
            {
                return base.BindProperty(bindingContext);
            }
        }
        protected override void SetProperty(ModelBindingContext bindingContext, string modelName, ModelMetadata propertyMetadata, ModelBindingResult result)
        {
            base.SetProperty(bindingContext, modelName, propertyMetadata, result);
        }
    }
  1. CustomBinderProvider
    public class CustomBinderProvider : IModelBinderProvider
    {
        public IModelBinder GetBinder(ModelBinderProviderContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (context.Metadata.IsComplexType && !context.Metadata.IsCollectionType)
            {
                var propertyBinders = new Dictionary<ModelMetadata, IModelBinder>();
                for (var i = 0; i < context.Metadata.Properties.Count; i++)
                {
                    var property = context.Metadata.Properties[i];
                    propertyBinders.Add(property, context.CreateBinder(property));
                }

                //var loggerFactory = context.Services.GetRequiredService<ILoggerFactory>();
                //return new ComplexTypeModelBinder(propertyBinders, loggerFactory);
                return new CustomBinder(propertyBinders);
            }

            return null;
        }

    }
  1. Inject provider
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc(options => {
            options.ModelBinderProviders.Insert(0, new CustomBinderProvider());
        });
    }
like image 70
Edward Avatar answered Sep 19 '22 03:09

Edward