c# - Autofac's Contextual binding not working in ASP.Core 2.0
问题描述
Recently I had to take advantage of the contextual binding that Autofac supports but somehow in ASP.NET Core 2.0 it's just not working. I followed the example (options 2 and 3) on the How do I pick a service implementation by context? FAQ page, however I managed to make it work only in ASP.NET MVC 5 and ASP.NET WEB API 2 projects. Example:
public interface ISender
{
void Send(Destination dest, Content content);
}
// We can implement the interface for different
// "sending strategies":
public class PostalServiceSender : ISender { ... }
public class EmailNotifier : ISender { ... }
public class ShippingProcessor
{
public ShippingProcessor(ISender shippingStrategy) { ... }
}
public class CustomerNotifier
{
public CustomerNotifier(ISender notificationStrategy) { ... }
}
The following binding configuration failed to work:
var builder = new ContainerBuilder();
builder.RegisterType<PostalServiceSender>()
.As<ISender>()
.AsSelf();
builder.RegisterType<EmailNotifier>()
.As<ISender>()
.AsSelf()
.InstancePerLifetimeScope();
builder.RegisterType<DefaultSender>()
.As<ISender>()
.InstancePerLifetimeScope();
builder.RegisterType<ShippingController>()
.WithParameter(
new ResolvedParameter(
(pi, ctx) => pi.ParameterType == typeof(ISender),
(pi, ctx) => ctx.Resolve<PostalServiceSender>()))
.InstancePerLifetimeScope();
builder.RegisterType<CustomersController>();
.WithParameter(
new ResolvedParameter(
(pi, ctx) => pi.ParameterType == typeof(ISender),
(pi, ctx) => ctx.Resolve<EmailNotifier>()))
.InstancePerLifetimeScope();
var container = builder.Build();
What is expected from the above binding configurations is the DefaultSender class to be the the default implementation of ISender. Only PostalServiceSender and CustomerNotifier are configured to receive a different implementation of ISender. Unfortunately the DefaultSender implementation is passed anywhere ISender is requested. Has anyone experienced such behavior in .NET Core 2.0?
解决方案
您的配置很好,一切都应该正常工作,但对于 ASP.net 核心控制器。
ASP.net core 中的控制器不是由 ASP.net core 创建的,Autofac
而是由 ASP.net core 创建的:
默认情况下,ASP.NET Core 将从容器中解析控制器参数,但实际上并不从容器中解析控制器。这通常不是问题,但它确实意味着:
控制器的生命周期由框架处理,而不是请求生命周期。 控制器构造函数参数的生命周期由请求生命周期处理。 您在控制器注册期间可能进行的特殊接线(例如设置属性注入)将不起作用。
您可以通过指定AddControllersAsServices()
何时向服务集合注册 MVC 来更改此设置。这样做会IServiceCollection
在您调用时自动将控制器类型注册到builder.Populate(services)
.
>> http://autofac.readthedocs.io/en/latest/integration/aspnetcore.html#controllers-as-services
你所要做的就是打电话AddControllersAsServices
// Add controllers as services so they'll be resolved.
services.AddMvc().AddControllersAsServices();
顺便说一句,即使您的代码有效,多次注册同一个服务并依赖默认顺序也不是最佳做法。
builder.RegisterType<PostalServiceSender>()
.Keyed<ISender>("postal");
builder.RegisterType<EmailNotifier>()
.Keyed<ISender>("email")
.InstancePerLifetimeScope();
builder.RegisterType<DefaultSender>()
.As<ISender>()
.InstancePerLifetimeScope();
和你的控制器是这样的:
builder.RegisterType<ShippingController>()
.WithParameter(
new ResolvedParameter(
(pi, ctx) => pi.ParameterType == typeof(ISender),
(pi, ctx) => ctx.ResolveNamed<ISender>("postal")))
.InstancePerLifetimeScope();
builder.RegisterType<CustomersController>()
.WithParameter(
new ResolvedParameter(
(pi, ctx) => pi.ParameterType == typeof(ISender),
(pi, ctx) => ctx.ResolveNamed<ISender>("email")))
.InstancePerLifetimeScope();
推荐阅读
- google-bigquery - BigQuery 关注更多领域
- https - kubelet和apiserver之间如何抓包
- bash - sh 等效于从命名管道读取的 bash 命令
- java - 显示 3D 地形 Mapbox v10 Android
- python-3.x - 如何在同一页面上将 django 404 错误作为错误消息抛出
- java - 正则表达式模式如何编写以检查 Java 中的至少一个字符串和数字?
- node.js - 使用 mongoose 更改“可用”数组的布尔值(例如,从用户输入获取的索引 4 处为 false)
- kotlin - 可点击切换连续重复导致搜索栏中的 UI 故障 - Jetpack Compose Android Studio Kotlin
- r - 用 NA 替换对称矩阵的元素对
- vue.js - 如何将 Vue2 代码转换为纯 Web 组件