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 ©?