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);
}
```
Comments: The other thing that I wanted to mention is that having this bug leads to more friction. I spent 2 days debugging this issue because it was really hard to nail down (I thought my IoC container was the problem!). As an extension point author, these kinds of issues raise the barrier to entry for people using our extension. For example, I also maintain a Redis based [SessionStateStoreProvider](https://github.com/TheCloudlessSky/Harbour.RedisSessionStateStore). There is [clear documentation](http://msdn.microsoft.com/en-us/library/system.web.sessionstate.sessionstatestoreproviderbase.aspx) that implementors need to guarantee thread safety.
```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);
}
```
Comments: The other thing that I wanted to mention is that having this bug leads to more friction. I spent 2 days debugging this issue because it was really hard to nail down (I thought my IoC container was the problem!). As an extension point author, these kinds of issues raise the barrier to entry for people using our extension. For example, I also maintain a Redis based [SessionStateStoreProvider](https://github.com/TheCloudlessSky/Harbour.RedisSessionStateStore). There is [clear documentation](http://msdn.microsoft.com/en-us/library/system.web.sessionstate.sessionstatestoreproviderbase.aspx) that implementors need to guarantee thread safety.