This repository has been archived by the owner on Nov 25, 2019. It is now read-only.
/
DefaultHttpControllerActivator.cs
127 lines (116 loc) · 5.76 KB
/
DefaultHttpControllerActivator.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Diagnostics.Contracts;
using System.Net.Http;
using System.Threading;
using System.Web.Http.Controllers;
using System.Web.Http.Internal;
using System.Web.Http.Properties;
namespace System.Web.Http.Dispatcher
{
/// <summary>
/// Default implementation of an <see cref="IHttpControllerActivator"/>.
/// A different implementation can be registered via the <see cref="T:System.Web.Http.Services.DependencyResolver"/>.
/// We optimize for the case where we have an <see cref="Controllers.ApiControllerActionInvoker"/>
/// instance per <see cref="HttpControllerDescriptor"/> instance but can support cases where there are
/// many <see cref="HttpControllerDescriptor"/> instances for one <see cref="System.Web.Http.Controllers.ApiControllerActionInvoker"/>
/// as well. In the latter case the lookup is slightly slower because it goes through the
/// <see cref="P:HttpControllerDescriptor.Properties"/> dictionary.
/// </summary>
public class DefaultHttpControllerActivator : IHttpControllerActivator
{
private Tuple<HttpControllerDescriptor, Func<IHttpController>> _fastCache;
private object _cacheKey = new object();
/// <summary>
/// Creates the <see cref="IHttpController"/> specified by <paramref name="controllerType"/> using the given <paramref name="request"/>
/// </summary>
/// <param name="request">The request message.</param>
/// <param name="controllerType">Type of the controller.</param>
/// <param name="controllerDescriptor">The controller descriptor</param>
/// <returns>An instance of type <paramref name="controllerType"/>.</returns>
public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
{
if (request == null)
{
throw Error.ArgumentNull("request");
}
if (controllerDescriptor == null)
{
throw Error.ArgumentNull("controllerDescriptor");
}
if (controllerType == null)
{
throw Error.ArgumentNull("controllerType");
}
try
{
Func<IHttpController> activator;
// First check in the local fast cache and if not a match then look in the broader
// HttpControllerDescriptor.Properties cache
if (_fastCache == null)
{
IHttpController controller = GetInstanceOrActivator(request, controllerType, out activator);
if (controller != null)
{
// we have a controller registered with the dependency resolver for this controller type
return controller;
}
else
{
Tuple<HttpControllerDescriptor, Func<IHttpController>> cacheItem = Tuple.Create(controllerDescriptor, activator);
Interlocked.CompareExchange(ref _fastCache, cacheItem, null);
}
}
else if (_fastCache.Item1 == controllerDescriptor)
{
// If the key matches and we already have the delegate for creating an instance.
activator = _fastCache.Item2;
}
else
{
// If the key doesn't match then lookup/create delegate in the HttpControllerDescriptor.Properties for
// that HttpControllerDescriptor instance
object value;
if (controllerDescriptor.Properties.TryGetValue(_cacheKey, out value))
{
activator = (Func<IHttpController>)value;
}
else
{
IHttpController controller = GetInstanceOrActivator(request, controllerType, out activator);
if (controller != null)
{
// we have a controller registered with the dependency resolver for this controller type
return controller;
}
else
{
controllerDescriptor.Properties.TryAdd(_cacheKey, activator);
}
}
}
return activator();
}
catch (Exception ex)
{
throw Error.InvalidOperation(ex, SRResources.DefaultControllerFactory_ErrorCreatingController, controllerType.Name);
}
}
// Returns the controller instance from the dependency resolver if there is one registered
// else returns the activator that calls the default ctor for the give controllerType.
private static IHttpController GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, out Func<IHttpController> activator)
{
Contract.Assert(request != null);
Contract.Assert(controllerType != null);
// If dependency resolver returns controller object then use it.
IHttpController instance = (IHttpController)request.GetDependencyScope().GetService(controllerType);
if (instance != null)
{
activator = null;
return instance;
}
// Otherwise create a delegate for creating a new instance of the type
activator = TypeActivator.Create<IHttpController>(controllerType);
return null;
}
}
}