【问题标题】:Autofac's Contextual binding not working in ASP.Core 2.0Autofac 的上下文绑定在 ASP.Core 2.0 中不起作用
【发布时间】:2018-10-24 03:35:25
【问题描述】:

最近我不得不利用 Autofac 支持的上下文绑定,但不知何故在 ASP.NET Core 2.0 中它不起作用。我按照How do I pick a service implementation by context? FAQ 页面上的示例(选项 2 和 3)进行操作,但是我设法使其仅在 ASP.NET MVC 5 和 ASP.NET WEB API 2 项目中工作。 示例:

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) { ... }
}

以下绑定配置失败工作:

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();

从上述绑定配置中可以看出 DefaultSender 类是 ISender 的默认实现。只有 PostalServiceSenderCustomerNotifier 配置为接收不同的 ISender 实现。 不幸的是,在任何请求 ISender 的地方都会传递 DefaultSender 实现。 有没有人在 .NET Core 2.0 中遇到过这种行为?

【问题讨论】:

  • 您的意思是 ShippingController 正在接收 DefaultSender 而不是预期的 PostalServiceSender 吗?你能分享这个控制器的构造函数吗?
  • 没关系。它也可能与此有关:autofac.readthedocs.io/en/latest/integration/…

标签: c# asp.net-core-mvc autofac asp.net-core-2.0 asp.net-core-webapi


【解决方案1】:

您的配置很好,除了 ASP.net 核心控制器外,一切都应该正常工作。

ASP.net core 中的控制器不是由Autofac 创建的,而是由 ASP.net core 创建的:

默认情况下,ASP.NET Core 将从容器中解析控制器参数,但实际上并不从容器中解析控制器。这通常不是问题,但它确实意味着:

  • 控制器的生命周期由框架处理,而不是请求生命周期。
  • 控制器构造函数参数的生命周期由请求生命周期处理。
  • 您在控制器注册期间可能进行的特殊接线(如设置属性注入)将不起作用。

    您可以通过在向服务集合注册 MVC 时指定 AddControllersAsServices() 来更改此设置。当您调用builder.Populate(services) 时,这样做会自动将控制器类型注册到IServiceCollection

    >>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();
    

    【讨论】:

    • 谢谢伙计,确实 AddControllersAsServices() 解决了我的问题。我们正在从 ASP.NET MVC5 和 Ninject 迁移到 ASP.NET Core 2.0 和 Autofac,我想我们还在学习。我同意你的看法,依赖配置顺序不是一个好主意,而是习惯了 Ninject 的功能,在这种情况下,绑定注册的实际顺序并不重要。附言我为你的答案投了票,但我仍然没有达到要求的分数。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-20
    相关资源
    最近更新 更多