I am [writing](https://github.com/TheCloudlessSky/Harbour.RedisTempData) a custom `ITempDataProvider` for MVC >= 3. Normally I'd override `Controller.CreateTempDataProvider` in MVC <= 3:
```csharp
protected override ITempDataProvider CreateTempDataProvider()
{
// Or use the IoC container directly.
return DependencyResolver.Current.GetService<ITempDataProvider>();
}
```
With >= MVC4, we now don't have to override this. We can just wire up the `ITemDataProvder` with our IoC container + `DependencyResolver` adapater and it [should just work](https://github.com/ASP-NET-MVC/aspnetwebstack/blob/4db5fb00506b9102f099edccbed56dc154d8ef48/src/System.Web.Mvc/Controller.cs#L193).
When I went to wire up the `ITempDataProvider`, I noticed that it was **always using the same instance**. This was causing lots of problems especially since the provider took on per-request dependencies! After a day of debugging, I tracked it down...
The reason is because the internal `Resolver` property on the controller [uses the `DependencyResolver.CurrentCache`](https://github.com/ASP-NET-MVC/aspnetwebstack/blob/4db5fb00506b9102f099edccbed56dc154d8ef48/src/System.Web.Mvc/Controller.cs#L38). The `CachedDependencyResolver` only returns [**a single instance per type**](https://github.com/ASP-NET-MVC/aspnetwebstack/blob/4db5fb00506b9102f099edccbed56dc154d8ef48/src/System.Web.Mvc/DependencyResolver.cs#L131). Therefore, when an `ITempDataProvder` or `IActionInvoker` (same pattern) is resolved from the `DependencyResolver`, by default it will cache the instance.
At first I thought "maybe `ITempDataProvider` just needs to be thread-safe...?".
However:
1. The contract of the `CreateTempDataProvider` identifies that it is a factory method.
2. There is no indication that [`ITempDataProvider`](http://msdn.microsoft.com/en-us/library/system.web.mvc.itempdataprovider(v=vs.118).aspx) or [`IActionInvoker`](http://msdn.microsoft.com/en-us/library/system.web.mvc.iactioninvoker(v=vs.118).aspx) must be thread safe.
3. If `DependencyResolver` can't resolve it, it will always [instantiate a new instance](https://github.com/ASP-NET-MVC/aspnetwebstack/blob/4db5fb00506b9102f099edccbed56dc154d8ef48/src/System.Web.Mvc/Controller.cs#L195) of the `SessionStateTempDataProvider` **per-controller** by default.
Therefore, I consider this a **bug**.
Here are the workarounds:
1. Override both `CreateTempDataProvider`/`CreateActionInvoker` in a base controller class (e.g. `ApplicationController`) and use `DependencyResolver.Current.GetService<ITempDataProvider>()` so that caching is ignored.
2. When the controller is activated, use the IoC to set the `TempDataProvider` and `ActionInvoker` properties. However, the lazy-loading nature of each of these is then lost.
Here's what needs to change:
1. The internal `Resolver` on `Controller`:
```csharp
// By default, use the global resolver.
// Or we can override to supply this instance with its own cache.
internal IDependencyResolver Resolver
{
get { return _resolver ?? DependencyResolver.Current; }
set { _resolver = value; }
}
```
2. Change `ControllerTest.ValidateControllerUsesCachedResolver` to:
```csharp
[Fact]
public void ValidateControllerUsesResolver()
{
var controller = new EmptyController();
var resolver = controller.Resolver;
Assert.Same(DependencyResolver.Current, resolver);
}
```
```csharp
protected override ITempDataProvider CreateTempDataProvider()
{
// Or use the IoC container directly.
return DependencyResolver.Current.GetService<ITempDataProvider>();
}
```
With >= MVC4, we now don't have to override this. We can just wire up the `ITemDataProvder` with our IoC container + `DependencyResolver` adapater and it [should just work](https://github.com/ASP-NET-MVC/aspnetwebstack/blob/4db5fb00506b9102f099edccbed56dc154d8ef48/src/System.Web.Mvc/Controller.cs#L193).
When I went to wire up the `ITempDataProvider`, I noticed that it was **always using the same instance**. This was causing lots of problems especially since the provider took on per-request dependencies! After a day of debugging, I tracked it down...
The reason is because the internal `Resolver` property on the controller [uses the `DependencyResolver.CurrentCache`](https://github.com/ASP-NET-MVC/aspnetwebstack/blob/4db5fb00506b9102f099edccbed56dc154d8ef48/src/System.Web.Mvc/Controller.cs#L38). The `CachedDependencyResolver` only returns [**a single instance per type**](https://github.com/ASP-NET-MVC/aspnetwebstack/blob/4db5fb00506b9102f099edccbed56dc154d8ef48/src/System.Web.Mvc/DependencyResolver.cs#L131). Therefore, when an `ITempDataProvder` or `IActionInvoker` (same pattern) is resolved from the `DependencyResolver`, by default it will cache the instance.
At first I thought "maybe `ITempDataProvider` just needs to be thread-safe...?".
However:
1. The contract of the `CreateTempDataProvider` identifies that it is a factory method.
2. There is no indication that [`ITempDataProvider`](http://msdn.microsoft.com/en-us/library/system.web.mvc.itempdataprovider(v=vs.118).aspx) or [`IActionInvoker`](http://msdn.microsoft.com/en-us/library/system.web.mvc.iactioninvoker(v=vs.118).aspx) must be thread safe.
3. If `DependencyResolver` can't resolve it, it will always [instantiate a new instance](https://github.com/ASP-NET-MVC/aspnetwebstack/blob/4db5fb00506b9102f099edccbed56dc154d8ef48/src/System.Web.Mvc/Controller.cs#L195) of the `SessionStateTempDataProvider` **per-controller** by default.
Therefore, I consider this a **bug**.
Here are the workarounds:
1. Override both `CreateTempDataProvider`/`CreateActionInvoker` in a base controller class (e.g. `ApplicationController`) and use `DependencyResolver.Current.GetService<ITempDataProvider>()` so that caching is ignored.
2. When the controller is activated, use the IoC to set the `TempDataProvider` and `ActionInvoker` properties. However, the lazy-loading nature of each of these is then lost.
Here's what needs to change:
1. The internal `Resolver` on `Controller`:
```csharp
// By default, use the global resolver.
// Or we can override to supply this instance with its own cache.
internal IDependencyResolver Resolver
{
get { return _resolver ?? DependencyResolver.Current; }
set { _resolver = value; }
}
```
2. Change `ControllerTest.ValidateControllerUsesCachedResolver` to:
```csharp
[Fact]
public void ValidateControllerUsesResolver()
{
var controller = new EmptyController();
var resolver = controller.Resolver;
Assert.Same(DependencyResolver.Current, resolver);
}
```