Dzyann
Dzyann

Reputation: 5208

How to get instance of InstancePerRequest object inside a Singleton

I am new to Autofac, I really like its lifetime scope features, but I am having an issue.

I need to use an instance of an object created on a InstancePerRequest basis from a singleton in an assembly that doesn't have a refence to .Net MVC Web

I tried what this post suggests but it doesn't work. I get this exception:

No scope with a tag matching 'AutofacWebRequest' is visible from the scope in which the instance was requested.

What is the correct way to get/use an InstancePerRequest object in a Singleton?

Update I would like to clarify, considering Travis answer, we don't want to hold a captive dependency of the InstancePerRequest item in the singleton. We want to request the item each time we use it. We've analyzed and it would be more expensive to make the singleton an InstancePerRequest than request the InstancePerRequest item to the container each time we use it. Is this considered a bad practice? It is rather common to have certain things you need to access at the Request level and have a Singleton use them.

Upvotes: 1

Views: 1378

Answers (3)

Dzyann
Dzyann

Reputation: 5208

Thanks everyone for your answers and comments, It turns out the solution was rather simple. This is the structure of files I wind up having:

No-Web assembly

public IPerRequestDependencyResolver
{
    T Get<T>() where T : class;
}

public interface IPerRequestObject
{
    void DoSomething();
}

public class PerRequestObject: IPerRequestObject
{
    public void DoSomething()
    {}
}

public interface IMySingletonPerRequestWrapper 
{
    DoSomethingOnPerRequestBasis();
}

public class MySingletonPerRequestWrapper: IMySingletonPerRequestWrapper 
{
    private IPerRequestDependencyResolver perRequestDependencyResolver;
    public PerRequestWrapper(IPerRequestDependencyResolver perRequestDependencyResolver)
    {
        this.perRequestDependencyResolver = perRequestDependencyResolver;
    }

    public void DoSomethingOnPerRequestBasis()
    {
        this.perRequestDependencyResolver.Get<IPerRequestObject>().DoSomething();
    }
}

public interface IMySingletonBussinesObject
{
    void DoSomething();
}

public class MySingletonBussinessObject: IMySingletonBussinesObject
{
    private IMySingletonPerRequestWrapper mysingletonPerRequestWrapper;
    public MySingletonBussinessObject(IMySingletonPerRequestWrapper mysingletonPerRequestWrapper)
    {
        this.mysingletonPerRequestWrapper = mysingletonPerRequestWrapper;
    }

    public void DoSomething()
    {
        this.mysingletonPerRequestWrapper.DoSomethingOnPerRequestBasis();
    }
}

MVC Web assembly

public class PerRequestDependencyResolver : IPerRequestDependencyResolver
{
    public T Get<T>() where T : class
    {
        return DependencyResolver.Current.GetService<T>();
    }
}

MySingletonPerRequestWrapper, MySingletonBussinessObject, PerRequestDependencyResolver are configured as .SingleInstance() in Autofac while PerRequestObject is configured with `InstancePerRequest().

I create the MySingletonPerRequestWrapper instead of directly using the PerRequestDependencyResolver inside the MySingletonBussinessObject because I want to make sure that anyone using the PerRequestObject does so properly and no captive instances are kept by mistake. I rather use Autofac this way to keep all my instances than keeping my instance in the HttpRequest and using it from there (with a wrapper), I feel this is more flexible.

Update Web Api

This is the update to use this technique with WebApi. I didn't myself do the full implementation, we discussed it on my group and a friend implemented it. I will explain the approach he took.

I simplified the logic and classes for clarity, you can change whatever you need. In our system we configured Autofac so you can inject instances in static classes. You can change that to something that better suits you.

First some helper, Extension methods to simplify.

public static class Extensions
{
    private const string dependencyScopeKey = "Resolver.Unique.key";
    public static IExecutionContext ExecutionContext {  get; set; }
    public static IDependencyScope Current(this IDependencyResolver dependencyResolver)
    {
        return ExecutionContext.GetObject<IDependencyScope>(dependencyScopeKey);
    }

    public static void SetCurrentDependencyScope(this HttpRequestMessage request)
    {
        ExecutionContext.SetObject(dependencyScopeKey, request.GetDependencyScope());
    }
}


public class WebExecutionContext : IExecutionContext
{

    public T GetObject <T>(string key)
    {
        var result = HttpContext.Current.Items[key];
        return result != null ? (T) result : default(T);
    }

    public void SetObject(string key, object val)
    {
        if (HttpContext.Current.Items.Contains(key))
            HttpContext.Current.Items.Remove(key);
        HttpContext.Current.Items.Add(key, val);
    }
}

Then the WebApi implementation of the IPerRequestDependencyResolver, we make use of the GlobalConfiguration.

public class PerRequestWebAPIDependencyResolver : IPerRequestDependencyResolver
{
    public T Get <T>() where T : class
    {
        var config = GlobalConfiguration.Configuration;
        return (T)config.DependencyResolver.Current().GetService(typeof(T));
    }
}

Then an action filter to help us add the DependencyResolver we need:

public class IoCScopeAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext filterContext)
    {
        //ioc context set
        filterContext.Request.SetCurrentDependencyScope();
    }
}

Finally in your controller, or base controller [AuthorizationFilter] [IoCScope()] public class YourController : ApiController {

}

I hope this helps!

Upvotes: 0

Christian Johansen
Christian Johansen

Reputation: 377

Given the update from Dzyann

For me it sounds like you you need a Factory of the Instance per request instance. this way the Factory Func can be a singleton and the Captive Dependency is avoided. Code would look something like this.

public class MySingleton
{
    private readonly Func<IMyInstancePerRequestType> _instanceFactory;
    public MySingleton(Func<IMyInstancePerRequestType> instanceFactory)
    {
        _instanceFactory = instanceFactory;
    }

    public void DoStuff()
    {
       var myService = _instanceFactory();
       myService.DoStuff();
    }
} 

Autofac will implicitly create the factory method for you. So extra registration should not be needed.

Make sure you take a look at http://docs.autofac.org/en/latest/resolve/relationships.html

Regarding the 'AutofacWebRequest' error you need to use the AutofacMvc or AutoFacWebAPI nuget packages and use the Owin extension methods during startup (given your using owin) registering this will create the 'AutofacWebRequest' lifetimscope.

in startup.cs it would be something like

   app.UseAutofacMiddleware( iocContainer );
   app.UseAutofacWebApi( configuration );

you refer to a post. in there we have

builder.Register<Func<IDataStore>>(c => 
      {
         var context = c.Resolve<IComponentContext>();
         return context.Resolve<IDataStore>;
      });

.InstancePerHttpRequest() could be added there.

Upvotes: 1

Travis Illig
Travis Illig

Reputation: 23924

Don't get per request items in a singleton. That creates a captive dependency which is bad news. Switch the singleton to be per request as well.

Upvotes: 3

Related Questions