简体   繁体   English

如何检查是否在 ASP.NET Core 中注入了依赖项注入服务?

[英]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 :假设我在Program.cs中向我的 ASP.NET 6 Web 应用程序注册了一个范围服务:

services.AddScoped<MyRequestService>();

Now let's say I have some custom middleware that runs after MVC routing:现在假设我有一些在 MVC 路由之后运行的自定义中间件:

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.现在,在中间件中调用await Next()之后,我想检查 MVC 选择的控制器是否使用了MyRequestService 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.我不能使用Context.RequestServices.GetRequiredService<MyRequestService>()因为如果它还没有实例化它只会实例化服务。 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.我不会尝试通过反射扫描 DI 容器,而是尝试使用HttpContext类。 It has properties like Features and Items .它具有FeaturesItems等属性。 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.例如,您的服务可以将具有特定键(例如 const 字符串)的条目添加到Items字典中,其中包含值中所需的任何数据,并且在您的中间件中,您在返回Next()后检查字典是否包含指定的键。 If yes, your service was used.如果是,则使用了您的服务。

In that case the default mechanism of ASP is used and no reflection is needed.在这种情况下,使用 ASP 的默认机制,不需要反射。

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.我在评论中根据 Jonesopolis 的建议使用了反射,但我没有在控制器上使用反射,而是使用它来获取内部依赖注入对象缓存。 I'm caching the ResolvedServices PropertyInfo using a Lazy<PropertyInfo> object.我正在使用Lazy<PropertyInfo>对象缓存ResolvedServices PropertyInfo This works, but I don't like it.这行得通,但我不喜欢它。 Hopefully ASP.NET will have a public accessor for this soon:希望 ASP.NET 很快就会有一个公共访问器:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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