Nuri Tasdemir
Nuri Tasdemir

Reputation: 9842

Autofac - InstancePerRequest created from DependencyResolver.Current.GetService<>() - When is it released?

In an API controller project in .NET, there is a service I am using, say SomeService, requires one time only initialisation (not per request or per SomeService instance) (Although I do not think it is relevant, here the explanation for this init part: It does some setup in Azure storage for once the api is created. Doing this for every instance of SomeService is unnecessarely costly. Therefore there was the following line in Global.asax

new SomeService().Init();

Now, I am using Autofac for dependency injection. I register SomeService as ISomeService and as InstancePerRequest (because SomeService is not thread-safe). Therefore now I want to initialise SomeService in Global.asax via an instance from container. However If I try to get an instance from container as in

container.Resolve<ISomeService>().Init();

it gives this error

An exception of type 'Autofac.Core.DependencyResolutionException' occurred in Autofac.dll but was not handled in user code

Additional information: No scope with a Tag matching 'AutofacWebRequest' is visible from the scope in which the instance was requested. This generally indicates that a component registered as per-HTTP request is being requested by a SingleInstance() component (or a similar scenario.) Under the web integration always request dependencies from the DependencyResolver.Current or ILifetimeScopeProvider.RequestLifetime, never from the container itself.

Therefore in Global.asax I get an instance as suggested in the error explanation.

DependencyResolver.Current.GetService<ISomeService>().Init();

What I want to know is that the SomeService instance I get from Current is released or not? Since there is no real request, I am not sure. At worst I can get the instance from concrete with new.

Upvotes: 1

Views: 1377

Answers (1)

Cyril Durand
Cyril Durand

Reputation: 16192

You are trying to merge 2 responsibilities into 1 components which breaks the Single Responsibility Principle.

In order to solve it, you can split the component with a component that will initialize the azure storage (IStorageProvider for example) and another component that will do the job. The IStorageProvider will be declared as SingleInstance (and implement IStartable if needed) and the other component will use this component.

public class AzureStorageProvider : IStorageProvider, IStartable
{
    public void Start()
    {
        // initialize storage
        this._storage = new ...
    }
} 


public class SomeService : ISomeService
{
    public SomeService(IStorageProvider storageProvider) 
    { 
        this._storageProvider = storageProvider;
    }

    private readonly IStorageProvider _storageProvider; 

    public void Do()
    {
        // do things with storage
        this._storageProvider.Storage.ExecuteX(); 
    }
}

and the registration :

builder.RegisterType<AzureStorageProvider>().As<IStorageProvider>().SingleInstance(); 
builder.RegisterType<SomeService>().As<ISomeService>().InstancePerRequest();

you can also register a IStorage and let SomeService depends directly on IStorage and use the IStorageProvider as a factory.

builder.Register(c => c.Resolve<IStorageProvider>().Storage).As<IStorage>(); 

Upvotes: 1

Related Questions