skip to Main Content

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.

3

Answers


  1. Chosen as BEST ANSWER

    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");
        }
    });
    

  2. 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");
    
    Login or Signup to reply.
  3. 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 (e.g. 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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search