How to extend ComplexTypeModelBinder

﹥>﹥吖頭↗ 提交于 2020-01-03 20:59:28

问题


I need to customize model binding for a particular class Foo that involves extending the normal binding logic with some additional post-processing (e.g., conditionally set null collection fields to an empty collection). I want to add this logic to model binding so that the results are available to action filters, etc.

The most direct approach would be to derive from ComplexTypeModelBinder and override BindModelAsync. However, that method is unfortunately not virtual.

Composition is the next alternative. I'm trying to create a FooModelBinder class that has or obtains an instance of ComplexTypeModelBinder. However, I can't figure out how to inject or resolve a ComplexTypeModelBinder. Is this possible? Is there a better way to extend the functionality of ComplexTypeModelBinder?


回答1:


I finally realized that model binders are obtained from model binder providers, not via dependency injection. To properly instantiate my FooModelBinder, I need to create a FooModelBinderProvider. And to properly obtain an instance of ComplexTypeModelBinder for composition, my provider needs access to a ComplexTypeModelBinderProvider. In other words, to compose over a model binder, you also need to compose over a model binder provider.

Here's the provider. Note that we don't need to specify the exact type of the injected provider because we're simply wrapping the existing functionality of another model binder.

public class FooModelBinderProvider : IModelBinderProvider
{
  private readonly IModelBinderProvider workerProvider;

  public FooModelBinderProvider(IModelBinderProvider workerProvider)
  {
    this.workerProvider = workerProvider;
  }

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

    if (context.Metadata.ModelType == typeof(Foo))
    {
      return new FooModelBinder(this.workerProvider.GetBinder(context));
    }

    return null;
  }
}

And here is the binder. Note that the first thing we do in BindModelAsync is trampoline into the "worker" binder.

public class FooModelBinder : IModelBinder
{
  private readonly IModelBinder worker;

  public FooModelBinder(IModelBinder worker)
  {
    this.worker = worker;
  }

  public  async Task BindModelAsync(ModelBindingContext bindingContext)
  {
    await this.worker.BindModelAsync(bindingContext);
    if (!bindingContext.Result.IsModelSet)
    {
      return;
    }

    var foo = bindingContext.Result.Model as Foo;
    if (foo == null)
    {
      throw new InvalidOperationException($"Expected {bindingContext.ModelName} to have been bound by ComplexTypeModelBinder");
    }

    // NOW DO SOME INTERESTING POST-PROCESSING
  }
}

Finally, here's how to register the custom binder:

  services.AddMvc(options =>
  {
    var workerProvider = options.ModelBinderProviders.First(p => p.GetType() == typeof(ComplexTypeModelBinderProvider));
    options.ModelBinderProviders.Insert(options.ModelBinderProviders.IndexOf(workerProvider), new FooModelBinderProvider(workerProvider));
  })


来源:https://stackoverflow.com/questions/47643025/how-to-extend-complextypemodelbinder

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!