1

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.

Dzyann
  • 5,062
  • 11
  • 63
  • 95

3 Answers3

3

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.

Travis Illig
  • 23,195
  • 2
  • 62
  • 85
  • Hi Travis, I understand that concept, we are not going to keep the reference to the object that long, the idea is to request the item each time we use it and hold the reference just throughout a method, we don't want to hold the reference at the property/attribute level of the singleton. We analyze and it would be more expensive to have the Singleton be recreated each request rather than request the InstancePerRequest item each time we use it. – Dzyann Jun 08 '17 at 12:33
  • Best case scenario you'll have to use service location over a request lifetime scope. An example of this is [how you have to get per request lifetime scope items in Web API filters](http://docs.autofac.org/en/latest/integration/webapi.html#standard-web-api-filter-attributes-are-singletons). I still recommend strongly against this - that's bad Web API design, not a great optimization. – Travis Illig Jun 08 '17 at 13:43
1

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.

  • That function, when injected into a singleton, will try resolving from the root container, not the per request lifetime scope. – Travis Illig Jun 08 '17 at 13:39
  • @Travis Your right. Then An explicit Factory could be declared that takes ILifetimeScope as parameter in Create method and caller of factory should resolve currentLifeTimeScope. – Christian Johansen Jun 08 '17 at 13:47
  • Hi Christian the problem I am having is what @TravisIllig mentions, If everything is InstancePerRequest works fine, the issue is that when you are inside a singleton it goes to the root container for the instances. You suggest to create an explicit Factory that takes the ILifetimeScope as a parameter. But I don't know how to achieve that? Do you mean like during the resolve i would specify what scope and pass the "AutofacWebRequest" name? I am still learning these Autofac features so I am not sure i fullly understand your suggestion. – Dzyann Jun 08 '17 at 15:12
  • @Dzyann TravisIIIig mentions "Best case scenario you'll have to use service location over a request lifetime scope". Yes you would have to get a reference to the correct lifetimescope (ILifetimeScope) and then resolve your Per HttpRequest instance. The tricky thing is to get that reference. Maybe the caller of the Singleton can supply the correct lifetimeScope ? – Christian Johansen Jun 09 '17 at 07:30
0

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!

Dzyann
  • 5,062
  • 11
  • 63
  • 95
  • It looks good but how do you acheive the same thing with Web API (where you don't have access to a static dependency resolver ?) – mberube.Net Nov 28 '18 at 04:43
  • @mberube.Net I am sorry I couldn't answer before I have been rather busy with work, I wanted to post some code but I haven't had the time. I will do it tomorrow. In the meantime I wanted to explain you what we did. Basically Since you can do your own implementation of PerRequestDependencyResolver you can do whatever you need, like injecting a dependency Resolver in the HtttpRequest at the start of the request (with a filter) and then use it in the "PerRequestDependencyResolveR". Tomorrow I will post some code following this idea. – Dzyann Nov 29 '18 at 21:27
  • @mberube.Net I added the implementation for web.api. Sorry I took a while, I got caught up with work. – Dzyann Dec 02 '18 at 23:30