Quantcast
Channel: ASPNETWebStack Issue Tracker Rss Feed
Viewing all articles
Browse latest Browse all 7215

Created Unassigned: Using DependencyResolver with ITempDataProvider/IActionInvoker the resolved instances are only created once and then shared [1692]

$
0
0
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);
}
```

Viewing all articles
Browse latest Browse all 7215

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>