Reputation: 836
I have a loop handling "messages" from a queue kind of thing. The ting is that every queue item needs to have it's own 'scope'
My impression was that this is what the scope factory should solve for me..
using (var scope = scopeFactory.CreateScope())
{
//Capture the scope..
var sp = scope.ServiceProvider;
//Set up the customer context here
//CurrentContext is registerd as scoped
//This will create a new instance of the object.
var cc = sp.GetService<CurrentContext>();
cc.AssetName = info.SourceName;
cc.Customer = info.CustomerGuid;
cc.Env = info.Env;
//and I'm setting the need values.
//Using the service provider generated from the scope factory
//to get my 'worker'
var worker = sp.GetService<TaskGeneratorWorker>();
await worker.CreateTasks(info);
}
In my DI registration I have something similar to this.
services.AddHttpClient<Visavi_SF_REST_Endpoint_Helper>((sp, client) =>
{
//CurrentScope.ServiceProvider
var service = sp.GetService<ICustomerDataService>();
//Trying to get the "CurrentContex" set in the scope in the previouse snippet.
//This is now a 'new' instance of the CurrentContext and my values is 'lost'
var customer = sp.GetService<CurrentContext>();
});
If i put the service provider i created in step 1 in a global param, and reference that in both it works as expected.
STEP 1:
globalSp = scope;
var cc = globalSp.ServiceProvider.GetService<CurrentContext>()
STEP 2:
var customer = globalSp.ServiceProvider.GetService<CurrentContext>();
Clearly there must be something that I have misunderstood about how it is suppose to work.. I thought that the service provider sent in, in the services.AddHttpClient would have the same 'scope' as the parent that is trying to create it.
Upvotes: 0
Views: 8800
Reputation: 836
AS far as I'm able to understand there is something "wrong" with the AddHttpClient method.
I created my own extension and factory and it worked as expected.
public static IServiceCollection AddScopedMultitenantHttpClient<TOwner>(this IServiceCollection services, Action<ClientConfig<TOwner>> config, Action<IServiceProvider, HttpClient> preExec) where TOwner : class
{
var conf = new ClientConfig<TOwner>();
config(conf);
conf.HttpHandlers.ForEach(e => services.AddScoped(e.type));
services.AddSingleton(conf);
services.AddScoped<TOwner>(sp =>
{
var conf = sp.GetService<ClientConfig<TOwner>>();
//Create the HttpHandlers for the client.. Just have to validate that it works first.
var httpClient = new HttpClient(new AvoidDisposeMessageHandler(conf.LastHandler));
preExec(sp,httpClient);
return ActivatorUtilities.CreateInstance<TOwner>(sp, new object[] { httpClient });
});
return services;
}
I have visited this problem before, but was not aware that this was the same issue..
******* DO NOT USE ******
the above code in production..
Upvotes: 2
Reputation: 52
The service has its own lifetime. There are three different lifetimes to apply different strategies to create a service, shown in as follows. Microsoft gives a more detail information about service lifetimes.
I don't know which lifetime does CurrentContext
have. If it is registered by the transient or scoped lifetime, it is hard to obtain the same service unless you keep the reference to the scope object just like STEP 1.
If you want to obtain the same object in different scope, it is better to register it as a singleton service.
services.AddSingleton<CurrentContext>();
Upvotes: -1