davewilliams459
davewilliams459

Reputation: 1739

Does RequestServices.GetService() have a significant impact on performance

Is RequestServices.GetService() an expensive operation? Is it a bad practice to use GetService() in a method if you cannot get services in the constructor?

Note: I am not asking if Dependency Injection is performant. I am asking if using RequestServices.GetService() inside a method (where you can's use DI such as in a custom attribute) is a bad idea, or is it essentially equivalent in performance to 'normal' constructor based DI.

I am using a custom attribute in an Asp.Net MVC Core application. In the OnAuthoizationAsync() method, I need to access a configuration setting. As my class inherits from 'Attribute', I cannot use DI to pass an object into the constructor. I am using the HttpContext.RequestServices.GetService() method to get the instance of configuration. The custom attribute could run on most requests, so it cannot have a big performance impact.

public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
   var config = (IConfiguration)context.HttpContext.RequestServices.GetService(typeof(IConfiguration));
    ...
}

Upvotes: 1

Views: 2242

Answers (2)

Alex from Jitbit
Alex from Jitbit

Reputation: 60626

I was interested in this as well. And since .NET is now opensource you can just... look!

Turns out internally, service provider uses a ConcurrentDictionary of accessors. So the overhead added by GetService - is just a look-up for the locator delegate in the dictionary.

Dictionary lookups are O(1) (as in "constant, no matter dictionary size).

I still wrote a benchmark and on my machine it takes 9.448ns (nine nanoseconds).

Method Mean Error StdDev Ratio RatioSD Allocated Alloc Ratio
Direct_Var_Ascess 4.399 ns 0.1896 ns 0.0104 ns 1.00 0.00 - NA
AsyncLocal_Cache 7.374 ns 1.6657 ns 0.0913 ns 1.68 0.02 - NA
ServiceLookup 9.448 ns 0.4034 ns 0.0221 ns 2.15 0.01 - NA

Here I'm accessing an object directly, then access it in an AsyncLocal variable (I thought caching a service could make it faster maybe?), and then finally use the GetService

The verdict is - FORGET IT.

P.S. Also see the other answer about dependency lifetime.

Upvotes: 2

abdusco
abdusco

Reputation: 11081

Since the method is run for (almost) every request, the dependency is also resolved at every request.

The cost of resolution is usually a non-issue, unless you're using a custom DI provider (Autofac) and have complex rules for type resolution. But Microsoft's DI purposefully keeps things simple, which speeds up type resolution.

Additionally, you might need to consider the dependency lifetime. If the type is added to DI as transient, it's allocated & created every time you resolve it (i.e. every time the method is executed)

If it's scoped, it's allocated once per request. So if the attribute is used multiple times, only a single object is created and returned when you resolve a type. So you pay the price once per request.

If it's singleton, it's allocated once per (app/DI container) lifetime, so you pay the price only once.

So if the type is large and slow to create, it might create some overhead, but you can avoid it by conditionally resolving it under some ifs, for example. All things considered, it's almost certainly a fraction of the time spent when doing IO, so I'd not worry about it.

Upvotes: 1

Related Questions