Autofac的Contextual绑定在ASP.Core 2.0中不起作用

时间:2018-05-14 12:57:34

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

最近我不得不利用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的默认实现。只有 PostalServiceSender CustomerNotifier 才会配置为接收不同的ISender实施。 不幸的是,DefaultSender实现在任何ISender被请求的地方传递。 有没有人在.NET Core 2.0中遇到过这样的行为?

1 个答案:

答案 0 :(得分:2)

Your configuration is great and everything should work fine but for ASP.net core controller.

Controllers in ASP.net core are not created by Autofac but byb ASP.net core :

By default, ASP.NET Core will resolve the controller parameters from the container but doesn’t actually resolve the controller from the container. This usually isn’t an issue but it does mean:

  • the lifecycle of the controller is handled by the framework, not the request lifetime.
  • The lifecycle of controller constructor parameters is handled by the request lifetime.
  • Special wiring that you may have done during registration of the controller (like setting up property injection) won’t work.

    You can change this by specifying AddControllersAsServices() when you register MVC with the service collection. Doing that will automatically register controller types into the IServiceCollection when you call builder.Populate(services).

    >> http://autofac.readthedocs.io/en/latest/integration/aspnetcore.html#controllers-as-services

  • All you have to do is to call AddControllersAsServices

    // Add controllers as services so they'll be resolved.
    services.AddMvc().AddControllersAsServices();
    

    BTW even if your code works, it is not the best practice to register the same service multiple time and rely on default order.

    builder.RegisterType<PostalServiceSender>()
           .Keyed<ISender>("postal");
    builder.RegisterType<EmailNotifier>()
           .Keyed<ISender>("email")
           .InstancePerLifetimeScope();
    builder.RegisterType<DefaultSender>()
           .As<ISender>()
           .InstancePerLifetimeScope();       
    

    and your controller like this :

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