问题描述
我需要为特定类Foo
自定义模型绑定,这涉及通过一些附加的后处理来扩展常规绑定逻辑(例如,有条件地将null集合字段设置为空集合).我想将此逻辑添加到模型绑定中,以便结果可用于操作过滤器等.
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.
最直接的方法是从ComplexTypeModelBinder
派生并覆盖BindModelAsync
.但是,不幸的是,该方法不是虚拟的.
The most direct approach would be to derive from ComplexTypeModelBinder
and override BindModelAsync
. However, that method is unfortunately not virtual.
下一个替代方法是排版.我正在尝试创建一个具有或获取ComplexTypeModelBinder
实例的FooModelBinder
类.但是,我不知道如何注入或解析ComplexTypeModelBinder
.这可能吗?有没有更好的方法来扩展ComplexTypeModelBinder
的功能?
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
?
推荐答案
我最终意识到,模型联编程序是从模型联编程序提供程序获得的,而不是通过依赖项注入获得的.为了正确地实例化我的FooModelBinder
,我需要创建一个FooModelBinderProvider
.为了正确获取ComplexTypeModelBinder
的实例进行组合,我的提供程序需要访问ComplexTypeModelBinderProvider
.换句话说,要组成模型联编程序,还需要组成模型联编程序提供者.
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;
}
}
这是活页夹.请注意,我们在BindModelAsync
中要做的第一件事是将蹦床插入工人"活页夹中.
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));
})
这篇关于如何扩展ComplexTypeModelBinder的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!