简体   繁体   中英

How do I check if a dependency injection service was injected in ASP.NET Core?

Let's say I register a scoped service to my ASP.NET 6 web application in Program.cs :

services.AddScoped<MyRequestService>();

Now let's say I have some custom middleware that runs after MVC routing:

app.UseRouting();

app.Use(async delegate (HttpContext Context, Func<Task> Next)
{
    await Next(); // <-- here a controller gets instantiated based on MVC routing
                  //     and *MIGHT* have 'MyRequestService' injected, but 
                  //     it's *NOT GUARANTEED*

    // what do I put here to check if 'MyRequestService' was injected into the
    // controller that was chosen by MVC?
});

Now, after the call to await Next() in the middleware, I would like to check if MyRequestService was used by the controller that was chosen by MVC. How can I do this?

I cannot use Context.RequestServices.GetRequiredService<MyRequestService>() since that will just instantiate the service if it isn't already instantiated. I need to check if the controller injected it, without inadvertently instantiating it.

Instead of trying to scan the DI container by reflection, I would try to use the HttpContext class. It has properties like Features and Items . Both are able to hold arbitrary data within the scope of the current context. Your service could for example add an entry with a specific key (eg a const string) into the Items dictionary with any data needed within the value and in your middleware you check after the return of Next() if the dictionary contains the specified key. If yes, your service was used.

In that case the default mechanism of ASP is used and no reflection is needed.

This is what I would do, it is not tested but I think this concept should work and it is easier to read than reflexion in my opinion:

// Scoped
MyRequestService {
    constructor(MyServiceMonitor monitor) {
        monitor.AddResolved(this.GetType().Name);
    }

}

// Scoped
MyServiceMonitor {
    List<string> types; 
    
    AddResolved(string type) {
        types.Add(type);    
    }
    
    
    IsResolved(string name) {
        return types.Contains(name);    
    }
}

// Check in the delegate

context.RequestServices.GetRequiredService<MyServiceMonitor>().IsResolved("MyRequestService");

I used reflection per Jonesopolis's suggestion in the comments, but instead of using reflection on controllers, I'm using it to get at the internal dependency injection object cache. I'm caching the ResolvedServices PropertyInfo using a Lazy<PropertyInfo> object. This works, but I don't like it. Hopefully ASP.NET will have a public accessor for this soon:

app.UseRouting();

var ResolvedServicesPropLoader = new Lazy<PropertyInfo>(delegate ()
{
    var ProviderType = Type.GetType("Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope, Microsoft.Extensions.DependencyInjection");
    if (ProviderType == null) { throw new Exception("service caching class type could not be found"); }
    var TheProperty = ProviderType.GetProperty("ResolvedServices", BindingFlags.NonPublic | BindingFlags.Instance);
    return TheProperty ?? throw new Exception("could not find the cached services property using reflection");
});

app.Use(async delegate (HttpContext Context, Func<Task> Next)
{
    await Next();        
    var InjectedServices = (ICollection<object>?)((IDictionary?)ResolvedServicesPropLoader.Value.GetValue(Context.RequestServices))?.Values;
    if (InjectedServices == null) { throw new Exception("cached services collection is null"); }
    if (InjectedServices.Where(x => x is MyRequestService).Any())
    {
        Console.WriteLine("service was injected");
    }
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM