PEtter
PEtter

Reputation: 836

Howto use the IServiceScopeFactory.CreateScope

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

Answers (2)

PEtter
PEtter

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

iskcal
iskcal

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.

  • Transient: Each time DI would create a new service object.
  • Singleton: In the DI object, there is only one service object corresponding to a specific type.
  • Scoped: If a service type is registered by the scoped strategy, in the same scope, the creation behavior is similar to singleton, but in the different scope, it is similar to transient. That is, if you obtain the service objects of the same types from a scope object, they are the same, if you obtain these from different scopes, the services are different.

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

Related Questions