Reputation: 1
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?
Upvotes: 0
Views: 181
Reputation: 16187
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 specifyingAddControllersAsServices()
when you register MVC with the service collection. Doing that will automatically register controller types into theIServiceCollection
when you callbuilder.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();
Upvotes: 2