如何扩展 ComplexTypeModelBinder

len*_*est 5 c# model-binding asp.net-core-mvc asp.net-core

我需要为特定类自定义模型绑定,该类Foo涉及使用一些额外的后处理扩展正常绑定逻辑(例如,有条件地将空集合字段设置为空集合)。我想将此逻辑添加到模型绑定,以便结果可用于操作过滤器等​​。

最直接的方法是派生自ComplexTypeModelBinder和覆盖BindModelAsync。然而,不幸的是,该方法不是虚拟的。

组合是下一个选择。我正在尝试创建一个FooModelBinder具有或获取ComplexTypeModelBinder. 但是,我不知道如何注入或解析ComplexTypeModelBinder. 这可能吗?有没有更好的方法来扩展 的功能ComplexTypeModelBinder?

len*_*est 12

我终于意识到模型绑定器是从模型绑定器提供者那里获得的,而不是通过依赖注入获得的。要正确实例化 my FooModelBinder,我需要创建一个FooModelBinderProvider. 为了正确获取ComplexTypeModelBinderfor 组合的实例,我的提供者需要访问ComplexTypeModelBinderProvider. 换句话说,要组合模型绑定器,您还需要组合模型绑定器提供程序。

这里是提供者。请注意,我们不需要指定注入提供程序的确切类型,因为我们只是包装了另一个模型绑定器的现有功能。

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;
  }
}
Run Code Online (Sandbox Code Playgroud)

这是活页夹。请注意,我们做的第一件事BindModelAsync是将蹦床放入“工人”活页夹。

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
  }
}
Run Code Online (Sandbox Code Playgroud)

最后,这里是如何注册自定义绑定器:

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

  • 必须将其更改为 `insert(0, new... ` 否则 ComplexTypeModelBinder 将首先启动。 (2认同)