billbogaiv / hybrid-model-binding

Provides the ability to bind models using both ModelBinder and ValueProvider in ASP.NET Core.
MIT License
104 stars 20 forks source link

Combining with Custom Model Binder #43

Open Luke1979 opened 4 years ago

Luke1979 commented 4 years ago

Hi all,

Great nuget package; thanks.

Is it possible to combining HybridModelBinder with another Custom IModelBinder.

My Request class has a complex type (Company) which I'm attempting to resolve using CompanyModelBinder in the example below. If I use [FromQuery] it all works, but changing to [FromHybrid] prevents the model binder from being hit.

Any help/info would be great.

CompanyModelBinder.cs

    public class CompanyModelBinderProvider: IModelBinderProvider
    {
        public IModelBinder GetBinder(ModelBinderProviderContext context)
        {
            return context.Metadata.ModelType == typeof(Company) 
                ? new CompanyModelBinder() 
                : null;
        }
    }

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

            var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
            if (valueProviderResult == ValueProviderResult.None)
                return Task.CompletedTask;

            bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);

            var value = valueProviderResult.FirstValue;

            if (string.IsNullOrEmpty(value)) 
                return Task.CompletedTask;

            var model = new Company(value);

            bindingContext.Result = ModelBindingResult.Success(model);

            return Task.CompletedTask;
        }
    }

Startup.cs....

services
                .AddMvc(config =>
                {
                    // Custom Model Binders.
                    config.ModelBinderProviders.Insert(0, new CompanyModelBinderProvider());
                 })
                .AddHybridModelBinder();

Request.cs

public class Request : IRequest<Result<Response>>
{
    public Company Company { get; set; }
}

Controller.cs

Using [FromQuery] works (The Company model binder is invoked)

public IActionResult Index([FromQuery]Request request)
{
   ...
}

...but using [FromHybrid]` doesn't.... :(

public IActionResult Index([FromHybrid]Request request)
{
   ...
}
GET /api/v1/company?company=Foo HTTP/1.1
Host: localhost:44398

Any ideas what I might be doing wrong?

Thanks.

billbogaiv commented 4 years ago

I suspect it's a limitation of the framework to only execute the first successful instance of IModelBinder. HybridModelBinding registers itself as the first MvcOptions.ModelBinderProviders via AddHybridModelBinder.

However, I've had an idea before to create public-facing methods for adding model binders through HybridModelBinding to continue hydrating properties of the parent-instance (currently, this is all handled internally). Need to think about implementation a bit more since ordering of events becomes more important.

WarpSpideR commented 1 year ago

Hi Bill,

We've been using this package for a new project we're working on and it's been super helpful, sadly we've now come across this limitation as well.

Is this something that is being actively looked at? Are you open to some help implementing this? If so please let me know as I'd be keen to get this added to the package!

Cheers