Hey @mythz,
You gave me some good guidance in a previous post: Moving Services to Plugins
Which I took seriously, since you’ve been saying this a few times over the years. So I enacted that guidance and normalised all dependency registrations to either AddSingleton() or AddTransient() which turned out to be a big job across all our services and libraries.
However, its not quite the complete guidance, as I discovered the hard way.
You were right that in general we should be using AddSingleton() and AddTransient() based on whether the dependency (and its sub-dependencies) are thread-safe or not.
However, there are still those dependencies which are dependent on the current IRequest, and making those AddTransient() is where things have gone dramatically wrong at runtime.
For example, we have some dependencies that provide information based on the current IRequest. Our services depend on these dependencies having different values for each request, so they cannot be singleton obviously. However, they cannot be transient either, since they need the IRequest and something has to inject the IRequest into them as a dependency, in order for them to report on the data that is dependent on the IRequest.
What we currently do is have a GlobalRequestFilter that runs and look for dependencies that implement IRequiresRequest and for those dependencies we inject the IRequest into that dependency so it can work properly.
In these dependencies if they are transient, and they are used more than once within the same IRequest then they lose their IRequest after the first use, and subsequent uses have no IRequest anymore.
We either need to make these ones (a) AddScoped() or (b) find another way to get them access to the current IRequest and make them AddTransient() or AddSingleton() even.
Option (a) is possible with the current mechanism,
Option (b) requires some other way tot get the IRequest when the dependency needs it.
We discovered some time back that we could have class like this:
public class HttpRequestFactory : IHttpRequestFactory
{
public IRequest Get()
{
var httpRequest = HostContext.GetCurrentRequest();
return httpRequest ?? new BasicRequest();
}
}
we can now add this to the container as AddSingleton() since its a factory.
and have IHttpRequestFactory as a property on other dependencies that need the IRequest, and those dependencies can even be singletons.
public class SubDomainDetective
{
public IHttpRequestFactory HttpRequestFactory { get; set; }
public string GetCurrentDomain()
{
var request = HttpRequestFactory.Get();
return request.Host;
}
}
Which option above would you recommend? is there an option ©?