Custom Model binding with Collections

41 Views Asked by At

I have a List as a field in a Model class named "Person" enter image description here

I have enabled Custom Person Model Binder globally in Program.cs file enter image description here

Since I'm binding the values manually I was able to retrieve other fields like Emailenter image description here

But how do I retrieve the values from Tags List from the Request body and manually assign them to the person.Tags list?

So this is basically a combinations of Custom Model Class Provider and Collection Binding.

I tried this but it returned me nullenter image description here

1

There are 1 best solutions below

2
Fengzhi Zhou On

Your custom model-binding is almost correct, there may be some mistakes in your tags binding. Here is a sample.

PersonModelBinder

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

        var person = new Person();

        //Other properties
        //…

        // Bind the Tags property
        var values = bindingContext.ValueProvider.GetValue("Tags").Values;
        var tags = new List<string>();
        foreach (var value in values)
        {
            if (!string.IsNullOrEmpty(value))
            {
                tags.Add(value.Trim());
            }
        }
        person.Tags = tags;

        bindingContext.Result = ModelBindingResult.Success(person);

        return Task.CompletedTask;
    }
}

PersonModelBinderProvider

public class PersonModelBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        if (context.Metadata.ModelType == typeof(Person))
        {
            return new BinderTypeModelBinder(typeof(PersonModelBinder));
        }

        return null;
    }
}

Controller

[HttpPost]
public IActionResult CreatePerson([ModelBinder(BinderType = typeof(PersonModelBinder))] Person person)
{
    if (ModelState.IsValid)
    {
        return RedirectToAction("Success");
    }
    return View(person);
}

public IActionResult Create()
{
    return View();
}

Create view

@model Person

@{
    ViewData["Title"] = "Create Person";
}

<h2>@ViewData["Title"]</h2>

<form asp-action="CreatePerson" method="post">
    @*other properties*@
    <div>
        <label>Tags:</label>
        <input type="text" name="Tags" value="" placeholder="Enter tag1" />
        <input type="text" name="Tags" value="" placeholder="Enter tag2" />
        <input type="text" name="Tags" value="" placeholder="Enter tag3" />
        <span asp-validation-for="Tags"></span>
    </div>
    <button type="submit">Create</button>
</form>

@section Scripts {
    @{
        await Html.RenderPartialAsync("_ValidationScriptsPartial");
    }
}

enter image description here enter image description here