On an ASP.NET Core project I have the following action:
public async Task<IActionResult> Get(ProductModel model) {
}
public class ProductModel {
public Filter<Double> Price { get; set; }
}
I have a Filter base class and a RangeFilter as follows:
public class Filter<T> { }
public class RangeFilter<T> : Filter<T> {
public abstract Boolean TryParse(String value, out Filter<T> filter);
}
I am passing a String ("[3.34;18.75]"
) to the Action as Price.
I need to create a ModelBinder
where I use the TryParse
method to try to convert that String
into a RangeFilter<Double>
to define the ProductModel.Price
property.
If TryParse
fails, e.g., returns false
then the ProductModel.Price
property becomes null.
How can this be done?
We can apply custom model binder using ModelBinder attribute by defining attributes on action method or model. If we are using this method (applying attribute on action method), we need to define this attribute on every action methods those want use this custom binding.
Custom Model Binder provides a mechanism using which we can map the data from the request to our ASP.NET MVC Model.
Model binding is a well-designed bridge between the HTTP request and the C# action methods. Data from HTTP requests are used by controllers and Razor pages. Route data, for example, may serve as a record key, while posted form fields may serve as values for model properties.
Model binding allows controller actions to work directly with model types (passed in as method arguments), rather than HTTP requests. Mapping between incoming request data and application models is handled by model binders.
If you're willing to move the parsing method into a static class, this would become a bit more feasible.
public static class RangeFilterParse
{
public static Filter<T> Parse<T>(string value)
{
// parse the stuff!
return new RangeFilter<T>();
}
}
public abstract class Filter<T> { }
public class RangeFilter<T> : Filter<T> { }
public class ProductModel
{
[ModelBinder(BinderType = typeof(RangeModelBinder))]
public Filter<double> Price { get; set; }
}
public class RangeModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
try
{
var input = bindingContext.ValueProvider.GetValue(bindingContext.ModelName).ToString();
var inputType = bindingContext.ModelType.GetTypeInfo().GenericTypeArguments[0];
// invoke generic method with proper type
var method = typeof(RangeFilterParse).GetMethod(nameof(RangeFilterParse.Parse), BindingFlags.Static);
var generic = method.MakeGenericMethod(inputType);
var result = generic.Invoke(this, new object[] { input });
bindingContext.Result = ModelBindingResult.Success(result);
return Task.CompletedTask;
}
catch(Exception) // or catch a more specific error related to parsing
{
bindingContext.Result = ModelBindingResult.Success(null);
return Task.CompletedTask;
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With