'ASP.NET Core - ModelBinder - Collection - stuck in endless / infinite loop
I am facing an issue with custom ModelBinder (IModelBinder implementation).
This is a model I am trying to bind from query string. The example parameter looks like this:
?Where.Name[eq]=Jakub
The class I am trying bind to is this:
[MetadataFilterBinder]
public class MetadataFilter
{
public string Term { get; set; }
public string Field { get; set; }
public FilterClause Clause { get; set; }
// Where.Name[eq]=Jakub
public static bool TryParse(string s, out MetadataFilter metadataFilter)
{
// commented out for brevity
}
}
My attribute annotating this model binder is:
public class MetadataFilterBinderAttribute : ModelBinderAttribute
{
public MetadataFilterBinderAttribute() : base(typeof(MetadataFilterBinder))
{
BindingSource = BindingSource.Query;
}
}
The ModelBinder implementation:
public class MetadataFilterBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
throw new ArgumentNullException(nameof(bindingContext));
var modelName = bindingContext.ModelName;
var queryStringKeyName = new string(modelName.Where(char.IsLetter).ToArray());
var whereKeyValue = bindingContext.HttpContext.Request.Query
.FirstOrDefault(q => q.Key.StartsWith($"{queryStringKeyName}.", StringComparison.InvariantCultureIgnoreCase));
if (whereKeyValue.Key == string.Empty)
return Task.CompletedTask;
var valueProviderResult = bindingContext.ValueProvider.GetValue(whereKeyValue.Key);
if (valueProviderResult == ValueProviderResult.None)
return Task.CompletedTask;
bindingContext.ModelState.SetModelValue(modelName, valueProviderResult);
if (string.IsNullOrEmpty(valueProviderResult.FirstValue))
return Task.CompletedTask;
var result = MetadataFilter.TryParse($"{whereKeyValue.Key}={whereKeyValue.Value}", out var metadataFilter);
if (result)
{
bindingContext.Result = ModelBindingResult.Success(metadataFilter);
}
else
{
bindingContext.ModelState.TryAddModelError(modelName, "Invalid metadata filter value.");
}
return Task.CompletedTask;
}
}
The MetadataFilter class resides in following model that should be bound from QueryString.
public class Metadata : IMetadataPaginable, IMetadataSortable, IMetadataFilterable
{
public MetadataPaging Paging { get; set; }
public MetadataSort Sort { get; set; }
public IReadOnlyCollection<MetadataFilter> Where { get; set; }
}
Now my code and ModelBinder get's executed correctly and all is getting set correctly. I set ModelBindingResult & returning Task.Completed. However it get stuck in an endless loop that I have no idea how to indicate that it should stop.
The BindModelAsync method gets called infinite amount of times until it hits the limit. The ModelName keeps incrementing as "Where[0]", "Where[1]", "Where[2]", "Where[n]".
How can I let the Binder know that I am good and that I've bound all the items I needed?
Note this is not a duplicate of Inifinite loop during model binding when using [ModelBinder] attribute
Solution 1:[1]
I solved this by putting the ModelBinder attribute on the model property. So my Binder instead of trying to bind each item individually which triggers the endless loop, it binds the whole collection at once and that's when this issue disappears.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Jakub Holovsky |

