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!