How to wrap and replace one of the default compone

2019-06-28 02:38发布

问题:

ASP.NET 5 (RC1) gives us a completely new configuration model, where we add/replace configurations to a collection of ServiceDescriptor objects. Replacing a default implementation is straightforward, for instance:

services.Add(ServiceDescriptor.Instance<IHttpContextAccessor>(
    new MyHttpContextAccessor()));

However, I'm having trouble finding a way to extend an existing registration with extra behavior (through decoration). In other words, I want to replace the built-in version with a custom one that uses the built-in version internally. It is of course very common to extend framework behavior this way. For instance:

// How to get the instance here???
IHttpContextAccessor original = 
    services.Last(service => service.ServiceType == typeof(IHttpContextAccessor));

services.Add(ServiceDescriptor.Instance<IHttpContextAccessor>(
    new HttpContextAccessorDecorator(original)));

Do note that the use of the IHttpContextAccessor is just an example, but does effectively show the problem. The services collection contains ServiceDescriptor objects, and in the case of the IHttpContextAccessor, the ImplementationInstance and ImpementationFactory properties are empty, making it impossible to get that original instance. We can't inject an IServiceProvider into the decorator to delay the registration, because once we request an IHttpContextAccessor inside our decorator, we will get that same decorator back, which will cause a stackoverflow exception.

Funny thing is that with MVC 5 and Web API, this is actually really straightforward to do.

How can I achieve the same with ASP.NET 5?

回答1:

You could accomplish this, in a hacky way, by building an 'intermediate' service provider. You can then retrieve an instance of the original implementation and use it when creating your own instance:

var innerSp = services.BuildServiceProvider();
var original = innerSp.GetService<IHttpContextAccessor>();
services.AddInstance<IHttpContextAccessor>(new HttpContextAccessorDecorator(original));

This requires the Microsoft.Extensions.DependencyInjection package (not the Abstractions one) since it uses the internal ServiceProvider implementation.

Of course, this is a awful solution and I agree we need something better.