Permalink
Browse files

26: Separate service location from dependency injection

  • Loading branch information...
1 parent 0ed4083 commit 1dcec5dfecea79433c7052dd86875e26256c15f8 @bradwilson bradwilson committed Apr 3, 2012
Showing with 1,908 additions and 930 deletions.
  1. +2 −2 src/System.Web.Http.WebHost/GlobalConfiguration.cs
  2. +1 −1 src/System.Web.Http/Controllers/HttpActionBinding.cs
  3. +3 −3 src/System.Web.Http/Controllers/HttpActionContextExtensions.cs
  4. +1 −1 src/System.Web.Http/Controllers/HttpActionDescriptor.cs
  5. +3 −2 src/System.Web.Http/Controllers/HttpControllerConfigurationAttribute.cs
  6. +7 −7 src/System.Web.Http/Controllers/HttpControllerDescriptor.cs
  7. +38 −0 src/System.Web.Http/Dependencies/EmptyResolver.cs
  8. +19 −0 src/System.Web.Http/Dependencies/IDependencyResolver.cs
  9. +27 −0 src/System.Web.Http/Dependencies/IDependencyScope.cs
  10. +3 −3 src/System.Web.Http/Description/ApiExplorer.cs
  11. +3 −14 src/System.Web.Http/Dispatcher/DefaultHttpControllerActivator.cs
  12. +1 −2 src/System.Web.Http/Dispatcher/DefaultHttpControllerSelector.cs
  13. +1 −1 src/System.Web.Http/Dispatcher/HttpControllerDispatcher.cs
  14. +8 −9 src/System.Web.Http/Dispatcher/HttpControllerTypeCache.cs
  15. +2 −0 src/System.Web.Http/GlobalSuppressions.cs
  16. +5 −0 src/System.Web.Http/Hosting/HttpPropertyKeys.cs
  17. +25 −12 src/System.Web.Http/HttpConfiguration.cs
  18. +31 −5 src/System.Web.Http/HttpRequestMessageExtensions.cs
  19. +3 −3 src/System.Web.Http/HttpServer.cs
  20. +1 −1 src/System.Web.Http/ModelBinding/Binders/CompositeModelBinderProvider.cs
  21. +1 −1 src/System.Web.Http/ModelBinding/DefaultActionValueBinder.cs
  22. +3 −3 src/System.Web.Http/ModelBinding/FormDataCollectionExtensions.cs
  23. +10 −12 src/System.Web.Http/ModelBinding/ModelBinderAttribute.cs
  24. +0 −2 src/System.Web.Http/Properties/AssemblyInfo.cs
  25. +9 −9 src/System.Web.Http/Properties/SRResources.Designer.cs
  26. +3 −3 src/System.Web.Http/Properties/SRResources.resx
  27. +0 −152 src/System.Web.Http/Services/DefaultServiceResolver.cs
  28. +597 −0 src/System.Web.Http/Services/DefaultServices.cs
  29. +0 −254 src/System.Web.Http/Services/DependencyResolver.cs
  30. +0 −22 src/System.Web.Http/Services/IDependencyResolver.cs
  31. +48 −50 src/System.Web.Http/{DependencyResolverExtensions.cs → ServicesExtensions.cs}
  32. +5 −4 src/System.Web.Http/System.Web.Http.csproj
  33. +3 −5 src/System.Web.Http/Tracing/ITraceManager.cs
  34. +13 −13 src/System.Web.Http/Tracing/TraceManager.cs
  35. +4 −4 test/System.Web.Http.Integration.Test/ApiExplorer/ApiExplorerSettingsTest.cs
  36. +5 −5 test/System.Web.Http.Integration.Test/ApiExplorer/DocumentationTest.cs
  37. +4 −4 test/System.Web.Http.Integration.Test/ApiExplorer/FormattersTest.cs
  38. +6 −6 test/System.Web.Http.Integration.Test/ApiExplorer/ParameterSourceTest.cs
  39. +6 −6 test/System.Web.Http.Integration.Test/ApiExplorer/RouteConstraintsTest.cs
  40. +14 −14 test/System.Web.Http.Integration.Test/ApiExplorer/RoutesTest.cs
  41. +1 −1 test/System.Web.Http.Integration.Test/ContentNegotiation/DefaultContentNegotiatorTests.cs
  42. +1 −1 test/System.Web.Http.Integration.Test/Controllers/CustomControllerDescriptorTest.cs
  43. +1 −1 test/System.Web.Http.Integration.Test/ExceptionHandling/HttpResponseExceptionTest.cs
  44. +20 −20 test/System.Web.Http.Test/Controllers/ApiControllerTest.cs
  45. +30 −2 test/System.Web.Http.Test/Controllers/HttpConfigurationTest.cs
  46. +1 −1 test/System.Web.Http.Test/Dispatcher/DefaultAssembliesResolverTest.cs
  47. +121 −0 test/System.Web.Http.Test/Dispatcher/DefaultHttpControllerActivatorTest.cs
  48. +7 −7 test/System.Web.Http.Test/Dispatcher/DefaultHttpControllerSelectorTest.cs
  49. +14 −9 test/System.Web.Http.Test/HttpRequestMessageExtensionsTest.cs
  50. +1 −1 test/System.Web.Http.Test/ModelBinding/Binders/ArrayModelBinderTest.cs
  51. +6 −6 test/System.Web.Http.Test/ModelBinding/Binders/CollectionModelBinderTest.cs
  52. +4 −2 test/System.Web.Http.Test/ModelBinding/Binders/ComplexModelDtoModelBinderTest.cs
  53. +1 −1 test/System.Web.Http.Test/ModelBinding/Binders/DictionaryModelBinderTest.cs
  54. +6 −4 test/System.Web.Http.Test/ModelBinding/Binders/KeyValuePairModelBinderTest.cs
  55. +2 −2 test/System.Web.Http.Test/ModelBinding/Binders/KeyValuePairModelBinderUtilTest.cs
  56. +1 −1 test/System.Web.Http.Test/ModelBinding/Binders/MutableObjectModelBinderTest.cs
  57. +1 −1 test/System.Web.Http.Test/ModelBinding/DefaultActionValueBinderTest.cs
  58. +29 −6 test/System.Web.Http.Test/ModelBinding/ModelBinderAttributeTest.cs
  59. +735 −0 test/System.Web.Http.Test/Services/DefaultServicesTests.cs
  60. +0 −219 test/System.Web.Http.Test/Services/DependencyResolverTests.cs
  61. +2 −1 test/System.Web.Http.Test/System.Web.Http.Test.csproj
  62. +8 −8 test/System.Web.Http.Test/Tracing/TraceManagerTest.cs
  63. +1 −1 test/System.Web.Http.Test/Validation/ModelValidationRequiredMemberSelectorTest.cs
View
4 src/System.Web.Http.WebHost/GlobalConfiguration.cs
@@ -14,8 +14,8 @@ public static class GlobalConfiguration
() =>
{
HttpConfiguration config = new HttpConfiguration(new HostedHttpRouteCollection(RouteTable.Routes));
- config.ServiceResolver.SetService(typeof(IAssembliesResolver), new WebHostAssembliesResolver());
- config.ServiceResolver.SetService(typeof(IHttpControllerTypeResolver), new WebHostHttpControllerTypeResolver());
+ config.Services.Replace(typeof(IAssembliesResolver), new WebHostAssembliesResolver());
+ config.Services.Replace(typeof(IHttpControllerTypeResolver), new WebHostHttpControllerTypeResolver());
return config;
});
View
2 src/System.Web.Http/Controllers/HttpActionBinding.cs
@@ -93,7 +93,7 @@ public virtual Task ExecuteBindingAsync(HttpActionContext actionContext, Cancell
if (_metadataProvider == null)
{
HttpConfiguration config = actionContext.ControllerContext.Configuration;
- _metadataProvider = config.ServiceResolver.GetModelMetadataProvider();
+ _metadataProvider = config.Services.GetModelMetadataProvider();
}
// Execute all the binders.
View
6 src/System.Web.Http/Controllers/HttpActionContextExtensions.cs
@@ -26,7 +26,7 @@ public static ModelMetadataProvider GetMetadataProvider(this HttpActionContext a
throw Error.ArgumentNull("actionContext");
}
- return actionContext.ControllerContext.Configuration.ServiceResolver.GetModelMetadataProvider();
+ return actionContext.ControllerContext.Configuration.Services.GetModelMetadataProvider();
}
/// <summary>
@@ -41,7 +41,7 @@ public static IEnumerable<ModelValidatorProvider> GetValidatorProviders(this Htt
throw Error.ArgumentNull("actionContext");
}
- return actionContext.ControllerContext.Configuration.ServiceResolver.GetModelValidatorProviders();
+ return actionContext.ControllerContext.Configuration.Services.GetModelValidatorProviders();
}
/// <summary>
@@ -88,7 +88,7 @@ public static bool TryGetBinder(this HttpActionContext actionContext, ModelBindi
}
else
{
- binder = actionContext.ControllerContext.Configuration.ServiceResolver.GetModelBinderProviders()
+ binder = actionContext.ControllerContext.Configuration.Services.GetModelBinderProviders()
.Select(p => p.GetBinder(actionContext, bindingContext))
.Where(b => b != null)
.FirstOrDefault();
View
2 src/System.Web.Http/Controllers/HttpActionDescriptor.cs
@@ -203,7 +203,7 @@ public virtual Collection<FilterInfo> GetFilterPipeline()
private Collection<FilterInfo> InitializeFilterPipeline()
{
- IEnumerable<IFilterProvider> filterProviders = _configuration.ServiceResolver.GetFilterProviders();
+ IEnumerable<IFilterProvider> filterProviders = _configuration.Services.GetFilterProviders();
IEnumerable<FilterInfo> filters = filterProviders.SelectMany(fp => fp.GetFilters(_configuration, this)).OrderBy(f => f, FilterInfoComparer.Instance);
View
5 src/System.Web.Http/Controllers/HttpControllerConfigurationAttribute.cs
@@ -1,12 +1,13 @@
-using System.Web.Http.Dispatcher;
+using System.Web.Http.Dependencies;
+using System.Web.Http.Dispatcher;
namespace System.Web.Http.Controllers
{
/// <summary>
/// Provides a mechanism for a <see cref="IHttpController"/> implementation to indicate
/// what kind of <see cref="IHttpControllerActivator"/>, <see cref="IHttpActionSelector"/>, <see cref="IActionValueBinder"/>
/// and <see cref="IHttpActionInvoker"/> to use for that controller. The types are
- /// first looked up in the <see cref="Services.DependencyResolver"/> and if not found there
+ /// first looked up in the <see cref="IDependencyResolver"/> and if not found there
/// then created directly.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
View
14 src/System.Web.Http/Controllers/HttpControllerDescriptor.cs
@@ -255,25 +255,25 @@ private void Initialize()
}
}
- // For everything still null we call the dependency resolver as normal.
+ // For everything still null we fall back to the default service list.
if (_controllerActivator == null)
{
- _controllerActivator = Configuration.ServiceResolver.GetHttpControllerActivator();
+ _controllerActivator = Configuration.Services.GetHttpControllerActivator();
}
if (_actionSelector == null)
{
- _actionSelector = Configuration.ServiceResolver.GetActionSelector();
+ _actionSelector = Configuration.Services.GetActionSelector();
}
if (_actionInvoker == null)
{
- _actionInvoker = Configuration.ServiceResolver.GetActionInvoker();
+ _actionInvoker = Configuration.Services.GetActionInvoker();
}
if (_actionValueBinder == null)
{
- _actionValueBinder = Configuration.ServiceResolver.GetActionValueBinder();
+ _actionValueBinder = Configuration.Services.GetActionValueBinder();
}
}
@@ -292,8 +292,8 @@ private void Initialize()
Contract.Assert(configuration != null);
if (serviceType != null)
{
- return (TBase)configuration.ServiceResolver.GetService(serviceType) ??
- (TBase)Activator.CreateInstance(serviceType);
+ return (TBase)configuration.DependencyResolver.GetService(serviceType)
+ ?? (TBase)Activator.CreateInstance(serviceType);
}
return null;
View
38 src/System.Web.Http/Dependencies/EmptyResolver.cs
@@ -0,0 +1,38 @@
+using System.Collections.Generic;
+using System.Linq;
+
+namespace System.Web.Http.Dependencies
+{
+ internal class EmptyResolver : IDependencyResolver
+ {
+ private static readonly IDependencyResolver _instance = new EmptyResolver();
+
+ private EmptyResolver()
+ {
+ }
+
+ public static IDependencyResolver Instance
+ {
+ get { return _instance; }
+ }
+
+ public IDependencyScope BeginScope()
+ {
+ return this;
+ }
+
+ public void Dispose()
+ {
+ }
+
+ public object GetService(Type serviceType)
+ {
+ return null;
+ }
+
+ public IEnumerable<object> GetServices(Type serviceType)
+ {
+ return Enumerable.Empty<object>();
+ }
+ }
+}
View
19 src/System.Web.Http/Dependencies/IDependencyResolver.cs
@@ -0,0 +1,19 @@
+namespace System.Web.Http.Dependencies
+{
+ /// <summary>
+ /// Represents a dependency injection container.
+ /// </summary>
+ public interface IDependencyResolver : IDependencyScope
+ {
+ /// <summary>
+ /// Starts a resolution scope. Objects which are resolved in the given scope will belong to
+ /// that scope, and when the scope is disposed, those objects are returned to the container.
+ /// Implementers should return a new instance of <see cref="IDependencyScope"/> every time this
+ /// method is called, unless the container does not have any concept of scope or resource
+ /// release (in which case, it would be okay to return 'this', so long as the calls to
+ /// <see cref="IDisposable.Dispose"/> are effectively NOOPs).
+ /// </summary>
+ /// <returns>The dependency scope.</returns>
+ IDependencyScope BeginScope();
+ }
+}
View
27 src/System.Web.Http/Dependencies/IDependencyScope.cs
@@ -0,0 +1,27 @@
+using System.Collections.Generic;
+
+namespace System.Web.Http.Dependencies
+{
+ /// <summary>
+ /// Represents a scope that is tracked by the dependency injection container. The scope is
+ /// used to keep track of resources that have been provided, so that they can then be
+ /// subsequently released when <see cref="IDisposable.Dispose"/> is called.
+ /// </summary>
+ public interface IDependencyScope : IDisposable
+ {
+ /// <summary>
+ /// Gets an instance of the given <paramref name="serviceType"/>. Must never throw.
+ /// </summary>
+ /// <param name="serviceType">The object type.</param>
+ /// <returns>The requested object, if found; <c>null</c> otherwise.</returns>
+ object GetService(Type serviceType);
+
+ /// <summary>
+ /// Gets all instances of the given <paramref name="serviceType"/>. Must never throw.
+ /// </summary>
+ /// <param name="serviceType">The object type.</param>
+ /// <returns>A sequence of isntances of the requested <paramref name="serviceType"/>. The sequence
+ /// should be empty (not null) if no objects of the given type are available.</returns>
+ IEnumerable<object> GetServices(Type serviceType);
+ }
+}
View
6 src/System.Web.Http/Description/ApiExplorer.cs
@@ -142,7 +142,7 @@ public virtual Collection<HttpMethod> GetHttpMethodsSupportedByAction(IHttpRoute
private Collection<ApiDescription> InitializeApiDescriptions()
{
Collection<ApiDescription> apiDescriptions = new Collection<ApiDescription>();
- IHttpControllerSelector controllerSelector = _config.ServiceResolver.GetHttpControllerSelector();
+ IHttpControllerSelector controllerSelector = _config.Services.GetHttpControllerSelector();
IDictionary<string, HttpControllerDescriptor> controllerMappings = controllerSelector.GetControllerMapping();
if (controllerMappings != null)
{
@@ -365,7 +365,7 @@ private ApiParameterDescription CreateParameterDescriptionFromBinding(HttpParame
private string GetApiDocumentation(HttpActionDescriptor actionDescriptor)
{
- IDocumentationProvider documentationProvider = DocumentationProvider ?? _config.ServiceResolver.GetDocumentationProvider();
+ IDocumentationProvider documentationProvider = DocumentationProvider ?? _config.Services.GetDocumentationProvider();
if (documentationProvider == null)
{
return string.Format(CultureInfo.CurrentCulture, SRResources.ApiExplorer_DefaultDocumentation, actionDescriptor.ActionName);
@@ -376,7 +376,7 @@ private string GetApiDocumentation(HttpActionDescriptor actionDescriptor)
private string GetApiParameterDocumentation(HttpParameterDescriptor parameterDescriptor)
{
- IDocumentationProvider documentationProvider = DocumentationProvider ?? _config.ServiceResolver.GetDocumentationProvider();
+ IDocumentationProvider documentationProvider = DocumentationProvider ?? _config.Services.GetDocumentationProvider();
if (documentationProvider == null)
{
return string.Format(CultureInfo.CurrentCulture, SRResources.ApiExplorer_DefaultDocumentation, parameterDescriptor.Prefix ?? parameterDescriptor.ParameterName);
View
17 src/System.Web.Http/Dispatcher/DefaultHttpControllerActivator.cs
@@ -17,20 +17,9 @@ namespace System.Web.Http.Dispatcher
/// </summary>
public class DefaultHttpControllerActivator : IHttpControllerActivator
{
- private readonly HttpConfiguration _configuration;
private Tuple<HttpControllerDescriptor, Func<IHttpController>> _fastCache;
private object _cacheKey = new object();
- public DefaultHttpControllerActivator(HttpConfiguration configuration)
- {
- if (configuration == null)
- {
- throw Error.ArgumentNull("configuration");
- }
-
- _configuration = configuration;
- }
-
/// <summary>
/// Creates the <see cref="IHttpController"/> specified by <paramref name="controllerType"/> using the given <paramref name="request"/>
/// </summary>
@@ -61,8 +50,8 @@ public IHttpController Create(HttpRequestMessage request, HttpControllerDescript
// HttpControllerDescriptor.Properties cache
if (_fastCache == null)
{
- // If service resolver returns controller object then keep asking it whenever we need a new instance
- IHttpController instance = (IHttpController)_configuration.ServiceResolver.GetService(controllerType);
+ // If dependency resolver returns controller object then keep asking it whenever we need a new instance
+ IHttpController instance = (IHttpController)request.GetDependencyScope().GetService(controllerType);
if (instance != null)
{
return instance;
@@ -93,7 +82,7 @@ public IHttpController Create(HttpRequestMessage request, HttpControllerDescript
}
catch (Exception ex)
{
- throw Error.InvalidOperation(ex, SRResources.DefaultControllerFactory_ErrorCreatingController, controllerType);
+ throw Error.InvalidOperation(ex, SRResources.DefaultControllerFactory_ErrorCreatingController, controllerType.Name);
}
}
View
3 src/System.Web.Http/Dispatcher/DefaultHttpControllerSelector.cs
@@ -9,13 +9,12 @@
using System.Web.Http.Controllers;
using System.Web.Http.Properties;
using System.Web.Http.Routing;
-using System.Web.Http.Services;
namespace System.Web.Http.Dispatcher
{
/// <summary>
/// Default <see cref="IHttpControllerSelector"/> instance for choosing a <see cref="HttpControllerDescriptor"/> given a <see cref="HttpRequestMessage"/>
- /// A different implementation can be registered via the <see cref="DependencyResolver"/>.
+ /// A different implementation can be registered via the <see cref="HttpConfiguration.Services"/>.
/// </summary>
public class DefaultHttpControllerSelector : IHttpControllerSelector
{
View
2 src/System.Web.Http/Dispatcher/HttpControllerDispatcher.cs
@@ -60,7 +60,7 @@ private IHttpControllerSelector ControllerSelector
{
if (_controllerSelector == null)
{
- _controllerSelector = _configuration.ServiceResolver.GetHttpControllerSelector();
+ _controllerSelector = _configuration.Services.GetHttpControllerSelector();
}
return _controllerSelector;
View
17 src/System.Web.Http/Dispatcher/HttpControllerTypeCache.cs
@@ -9,9 +9,7 @@ namespace System.Web.Http.Dispatcher
internal sealed class HttpControllerTypeCache
{
private readonly HttpConfiguration _configuration;
- private readonly IAssembliesResolver _assembliesResolver;
- private readonly IHttpControllerTypeResolver _controllersResolver;
- private readonly Dictionary<string, ILookup<string, Type>> _cache;
+ private readonly Lazy<Dictionary<string, ILookup<string, Type>>> _cache;
public HttpControllerTypeCache(HttpConfiguration configuration)
{
@@ -21,14 +19,12 @@ public HttpControllerTypeCache(HttpConfiguration configuration)
}
_configuration = configuration;
- _assembliesResolver = _configuration.ServiceResolver.GetAssembliesResolver();
- _controllersResolver = _configuration.ServiceResolver.GetHttpControllerTypeResolver();
- _cache = InitializeCache();
+ _cache = new Lazy<Dictionary<string, ILookup<string, Type>>>(InitializeCache);
}
internal Dictionary<string, ILookup<string, Type>> Cache
{
- get { return _cache; }
+ get { return _cache.Value; }
}
public ICollection<Type> GetControllerTypes(string controllerName)
@@ -41,7 +37,7 @@ public ICollection<Type> GetControllerTypes(string controllerName)
HashSet<Type> matchingTypes = new HashSet<Type>();
ILookup<string, Type> namespaceLookup;
- if (_cache.TryGetValue(controllerName, out namespaceLookup))
+ if (_cache.Value.TryGetValue(controllerName, out namespaceLookup))
{
foreach (var namespaceGroup in namespaceLookup)
{
@@ -54,7 +50,10 @@ public ICollection<Type> GetControllerTypes(string controllerName)
private Dictionary<string, ILookup<string, Type>> InitializeCache()
{
- List<Type> controllerTypes = _controllersResolver.GetControllerTypes(_assembliesResolver).ToList();
+ IAssembliesResolver assembliesResolver = _configuration.Services.GetAssembliesResolver();
+ IHttpControllerTypeResolver controllersResolver = _configuration.Services.GetHttpControllerTypeResolver();
+
+ ICollection<Type> controllerTypes = controllersResolver.GetControllerTypes(assembliesResolver);
var groupedByName = controllerTypes.GroupBy(
t => t.Name.Substring(0, t.Name.Length - DefaultHttpControllerSelector.ControllerSuffix.Length),
StringComparer.OrdinalIgnoreCase);
View
2 src/System.Web.Http/GlobalSuppressions.cs
@@ -14,5 +14,7 @@
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Web.Http.Filters.ActionFilterAttribute.#System.Web.Http.Filters.IActionFilter.ExecuteActionFilterAsync(System.Web.Http.Controllers.HttpActionContext,System.Threading.CancellationToken,System.Func`1<System.Threading.Tasks.Task`1<System.Net.Http.HttpResponseMessage>>)")]
[assembly: SuppressMessage("Microsoft.WebAPI", "CR4001:DoNotCallProblematicMethodsOnTask", Scope = "member", Target = "System.Web.Http.Tracing.Tracers.ActionInvokerTracer.#System.Web.Http.Controllers.IHttpActionInvoker.InvokeActionAsync(System.Web.Http.Controllers.HttpActionContext,System.Threading.CancellationToken)", Justification = "Tracing layer needs to observe all Task completion paths")]
[assembly: SuppressMessage("Microsoft.WebAPI", "CR4001:DoNotCallProblematicMethodsOnTask", Scope = "member", Target = "System.Web.Http.Tracing.Tracers.ApiControllerTracer.#System.Web.Http.Controllers.IHttpController.ExecuteAsync(System.Web.Http.Controllers.HttpControllerContext,System.Threading.CancellationToken)", Justification = "Tracing layer needs to observe all Task completion paths")]
+[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Net.Http.Formatting", Justification = "Namespace follows folder structure")]
+[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Web.Http.Dependencies", Justification = "Namespace follows folder structure")]
[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Web.Http.Tracing.Tracers", Justification = "Namespace follows folder structure")]
[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Web.Http.Validation.Validators", Justification = "Namespace follows folder structure")]
View
5 src/System.Web.Http/Hosting/HttpPropertyKeys.cs
@@ -31,6 +31,11 @@ public static class HttpPropertyKeys
public static readonly string DisposableRequestResourcesKey = "MS_DisposableRequestResources";
/// <summary>
+ /// Provides a key for the dependency scope for this request.
+ /// </summary>
+ public static readonly string DependencyScope = "MS_DependencyScope";
+
+ /// <summary>
/// Provides a key for the <see cref="Guid"/> stored in <see cref="HttpRequestMessage.Properties"/>.
/// This is the correlation id for that request.
/// </summary>
View
37 src/System.Web.Http/HttpConfiguration.cs
@@ -3,6 +3,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Net.Http;
using System.Net.Http.Formatting;
+using System.Web.Http.Dependencies;
using System.Web.Http.Filters;
using System.Web.Http.ModelBinding;
using System.Web.Http.Services;
@@ -15,11 +16,12 @@ namespace System.Web.Http
public class HttpConfiguration : IDisposable
{
private readonly HttpRouteCollection _routes;
- private readonly DependencyResolver _serviceResolver;
private readonly ConcurrentDictionary<object, object> _properties = new ConcurrentDictionary<object, object>();
private readonly MediaTypeFormatterCollection _formatters = DefaultFormatters();
private readonly Collection<DelegatingHandler> _messageHandlers = new Collection<DelegatingHandler>();
private readonly HttpFilterCollection _filters = new HttpFilterCollection();
+
+ private IDependencyResolver _dependencyResolver = EmptyResolver.Instance;
private bool _disposed;
/// <summary>
@@ -44,7 +46,7 @@ public HttpConfiguration(HttpRouteCollection routes)
}
_routes = routes;
- _serviceResolver = new DependencyResolver(this);
+ Services = new DefaultServices(this);
}
/// <summary>
@@ -99,17 +101,30 @@ public string VirtualPathRoot
}
/// <summary>
- /// Gets the <see cref="DependencyResolver"/> used to resolve services to use by this <see cref="HttpServer"/>.
+ /// Gets or sets the dependency resolver associated with this <see cref="HttpConfiguration"/>.
/// </summary>
- /// <value>
- /// The <see cref="DependencyResolver"/>.
- /// </value>
- public DependencyResolver ServiceResolver
+ public IDependencyResolver DependencyResolver
{
- get { return _serviceResolver; }
+ get { return _dependencyResolver; }
+ set
+ {
+ if (value == null)
+ {
+ throw Error.PropertyNull();
+ }
+
+ _dependencyResolver = value;
+ }
}
/// <summary>
+ /// Gets the container of default services associated with this <see cref="HttpConfiguration"/>.
+ /// Only supports the list of service types documented on <see cref="DefaultServices"/>. For general
+ /// purpose types, please use <see cref="DependencyResolver"/>.
+ /// </summary>
+ public DefaultServices Services { get; internal set; }
+
+ /// <summary>
/// Gets or sets a value indicating whether error details should be included in error messages.
/// </summary>
public IncludeErrorDetailPolicy IncludeErrorDetailPolicy { get; set; }
@@ -150,8 +165,6 @@ internal bool ShouldIncludeErrorDetail(HttpRequestMessage request)
}
}
- #region IDisposable
-
public void Dispose()
{
Dispose(true);
@@ -166,10 +179,10 @@ protected virtual void Dispose(bool disposing)
if (disposing)
{
_routes.Dispose();
+ Services.Dispose();
+ DependencyResolver.Dispose();
}
}
}
-
- #endregion
}
}
View
36 src/System.Web.Http/HttpRequestMessageExtensions.cs
@@ -6,6 +6,7 @@
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using System.Threading;
+using System.Web.Http.Dependencies;
using System.Web.Http.Hosting;
using System.Web.Http.Properties;
using System.Web.Http.Routing;
@@ -34,6 +35,31 @@ public static HttpConfiguration GetConfiguration(this HttpRequestMessage request
}
/// <summary>
+ /// Gets the dependency resolver scope associated with this <see cref="HttpRequestMessage"/>.
+ /// Services which are retrieved from this scope will be released when the request is
+ /// cleaned up by the framework.
+ /// </summary>
+ /// <param name="request">The HTTP request.</param>
+ /// <returns>The <see cref="IDependencyScope"/> for the given request.</returns>
+ public static IDependencyScope GetDependencyScope(this HttpRequestMessage request)
+ {
+ if (request == null)
+ {
+ throw Error.ArgumentNull("request");
+ }
+
+ IDependencyScope result;
+ if (!request.Properties.TryGetValue<IDependencyScope>(HttpPropertyKeys.DependencyScope, out result))
+ {
+ result = request.GetConfiguration().DependencyResolver.BeginScope();
+ request.Properties[HttpPropertyKeys.DependencyScope] = result;
+ request.RegisterForDispose(result);
+ }
+
+ return result;
+ }
+
+ /// <summary>
/// Gets the <see cref="System.Threading.SynchronizationContext"/> for the given request or null if not available.
/// </summary>
/// <param name="request">The HTTP request.</param>
@@ -120,7 +146,7 @@ public static HttpResponseMessage CreateResponse<T>(this HttpRequestMessage requ
throw Error.InvalidOperation(SRResources.HttpRequestMessageExtensions_NoConfiguration);
}
- IContentNegotiator contentNegotiator = configuration.ServiceResolver.GetContentNegotiator();
+ IContentNegotiator contentNegotiator = configuration.Services.GetContentNegotiator();
if (contentNegotiator == null)
{
throw Error.InvalidOperation(SRResources.HttpRequestMessageExtensions_NoContentNegotiator, typeof(IContentNegotiator).FullName);
@@ -310,10 +336,10 @@ public static void DisposeRequestResources(this HttpRequestMessage request)
throw Error.ArgumentNull("request");
}
- List<IDisposable> trackedResources;
- if (request.Properties.TryGetValue(HttpPropertyKeys.DisposableRequestResourcesKey, out trackedResources))
+ List<IDisposable> resourcesToDispose;
+ if (request.Properties.TryGetValue(HttpPropertyKeys.DisposableRequestResourcesKey, out resourcesToDispose))
{
- foreach (IDisposable resource in trackedResources)
+ foreach (IDisposable resource in resourcesToDispose)
{
try
{
@@ -324,7 +350,7 @@ public static void DisposeRequestResources(this HttpRequestMessage request)
// ignore exceptions
}
}
- trackedResources.Clear();
+ resourcesToDispose.Clear();
}
}
}
View
6 src/System.Web.Http/HttpServer.cs
@@ -166,7 +166,7 @@ private void EnsureInitialized()
Initialize();
// Attach tracing before creating pipeline to allow injection of message handlers
- ITraceManager traceManager = _configuration.ServiceResolver.GetTraceManager();
+ ITraceManager traceManager = _configuration.Services.GetTraceManager();
Contract.Assert(traceManager != null);
traceManager.Initialize(_configuration);
@@ -187,8 +187,8 @@ private void EnsureInitialized()
protected virtual void Initialize()
{
// Register the default IRequiredMemberSelector for formatters that haven't been assigned one
- ModelMetadataProvider metadataProvider = _configuration.ServiceResolver.GetModelMetadataProvider();
- IEnumerable<ModelValidatorProvider> validatorProviders = _configuration.ServiceResolver.GetModelValidatorProviders();
+ ModelMetadataProvider metadataProvider = _configuration.Services.GetModelMetadataProvider();
+ IEnumerable<ModelValidatorProvider> validatorProviders = _configuration.Services.GetModelValidatorProviders();
IRequiredMemberSelector defaultRequiredMemberSelector = new ModelValidationRequiredMemberSelector(metadataProvider, validatorProviders);
foreach (MediaTypeFormatter formatter in _configuration.Formatters)
View
2 src/System.Web.Http/ModelBinding/Binders/CompositeModelBinderProvider.cs
@@ -33,7 +33,7 @@ public override IModelBinder GetBinder(HttpActionContext actionContext, ModelBin
// or use the set of providers we were given.
IEnumerable<ModelBinderProvider> providers = _providers != null
? _providers
- : actionContext.ControllerContext.Configuration.ServiceResolver.GetModelBinderProviders().Where((p) => !typeof(CompositeModelBinderProvider).IsAssignableFrom(p.GetType()));
+ : actionContext.ControllerContext.Configuration.Services.GetModelBinderProviders().Where((p) => !typeof(CompositeModelBinderProvider).IsAssignableFrom(p.GetType()));
return new CompositeModelBinder(providers);
}
View
2 src/System.Web.Http/ModelBinding/DefaultActionValueBinder.cs
@@ -21,7 +21,7 @@ protected virtual IEnumerable<MediaTypeFormatter> GetFormatters(HttpActionDescri
protected virtual IBodyModelValidator GetBodyModelValidator(HttpActionDescriptor actionDescriptor)
{
- return actionDescriptor.Configuration.ServiceResolver.GetBodyModelValidator();
+ return actionDescriptor.Configuration.Services.GetBodyModelValidator();
}
/// <summary>
View
6 src/System.Web.Http/ModelBinding/FormDataCollectionExtensions.cs
@@ -153,18 +153,18 @@ public static object ReadAs(this FormDataCollection formData, Type type, string
if (validateRequiredMembers)
{
// Set a ModelValidatorProvider that understands the IRequiredMemberSelector
- config.ServiceResolver.SetService(typeof(ModelValidatorProvider), new RequiredMemberModelValidatorProvider(requiredMemberSelector));
+ config.Services.Replace(typeof(ModelValidatorProvider), new RequiredMemberModelValidatorProvider(requiredMemberSelector));
}
// Looks like HttpActionContext is just a way of getting to the config, which we really
// just need to get a list of modelbinderPRoviders for composition.
HttpControllerContext controllerContext = new HttpControllerContext() { Configuration = config };
HttpActionContext actionContext = new HttpActionContext { ControllerContext = controllerContext };
- ModelMetadataProvider metadataProvider = config.ServiceResolver.GetModelMetadataProvider();
+ ModelMetadataProvider metadataProvider = config.Services.GetModelMetadataProvider();
// Create default over config
- IEnumerable<ModelBinderProvider> providers = config.ServiceResolver.GetModelBinderProviders();
+ IEnumerable<ModelBinderProvider> providers = config.Services.GetModelBinderProviders();
ModelBinderProvider modelBinderProvider = new CompositeModelBinderProvider(providers);
IValueProvider vp = formData.GetJQueryValueProvider();
View
22 src/System.Web.Http/ModelBinding/ModelBinderAttribute.cs
@@ -37,30 +37,28 @@ public ModelBinderAttribute(Type binderType)
/// <summary>
/// Gets or sets the name to consider as the parameter name during model binding
/// </summary>
- public string Name { get; set; }
-
+ public string Name { get; set; }
+
public bool SuppressPrefixCheck { get; set; }
// This will get called by a parameter binding, which will cache the results.
public ModelBinderProvider GetModelBinderProvider(HttpConfiguration configuration)
{
if (BinderType != null)
{
- object value = configuration.ServiceResolver.GetService(BinderType);
- if (value == null)
- {
- value = Activator.CreateInstance(BinderType);
- }
+ object value = configuration.DependencyResolver.GetService(BinderType)
+ ?? Activator.CreateInstance(BinderType);
+
if (value != null)
{
VerifyBinderType(value.GetType());
ModelBinderProvider result = (ModelBinderProvider)value;
return result;
- }
+ }
}
// Create default over config
- IEnumerable<ModelBinderProvider> providers = configuration.ServiceResolver.GetModelBinderProviders();
+ IEnumerable<ModelBinderProvider> providers = configuration.Services.GetModelBinderProviders();
if (providers.Count() == 1)
{
@@ -69,16 +67,16 @@ public ModelBinderProvider GetModelBinderProvider(HttpConfiguration configuratio
return new CompositeModelBinderProvider(providers);
}
-
+
/// <summary>
/// Value providers that will be fed to the model binder.
/// </summary>
public virtual IEnumerable<ValueProviderFactory> GetValueProviderFactories(HttpConfiguration configuration)
{
// By default, just get all registered value provider factories
- return configuration.ServiceResolver.GetValueProviderFactories();
+ return configuration.Services.GetValueProviderFactories();
}
-
+
private static void VerifyBinderType(Type attemptedType)
{
Type required = typeof(ModelBinderProvider);
View
2 src/System.Web.Http/Properties/AssemblyInfo.cs
@@ -8,5 +8,3 @@
[assembly: Guid("70cecdcd-46f5-492b-9e1f-1d9a947f1fd1")]
[assembly: InternalsVisibleTo("System.Web.Http.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
[assembly: InternalsVisibleTo("System.Web.Http.Integration.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
-
-[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Net.Http.Formatting", Justification = "There are other types in this namespace in a different assembly")]
View
18 src/System.Web.Http/Properties/SRResources.Designer.cs
@@ -270,29 +270,29 @@ internal class SRResources {
}
/// <summary>
- /// Looks up a localized string similar to The &apos;{0}&apos; list is invalid because it contains one or more null items..
+ /// Looks up a localized string similar to The service type {0} is not supported..
/// </summary>
- internal static string DelegatingHandlerArrayContainsNullItem {
+ internal static string DefaultServices_InvalidServiceType {
get {
- return ResourceManager.GetString("DelegatingHandlerArrayContainsNullItem", resourceCulture);
+ return ResourceManager.GetString("DefaultServices_InvalidServiceType", resourceCulture);
}
}
/// <summary>
- /// Looks up a localized string similar to The &apos;{0}&apos; list is invalid because the property &apos;{1}&apos; of &apos;{2}&apos; is not null..
+ /// Looks up a localized string similar to The &apos;{0}&apos; list is invalid because it contains one or more null items..
/// </summary>
- internal static string DelegatingHandlerArrayHasNonNullInnerHandler {
+ internal static string DelegatingHandlerArrayContainsNullItem {
get {
- return ResourceManager.GetString("DelegatingHandlerArrayHasNonNullInnerHandler", resourceCulture);
+ return ResourceManager.GetString("DelegatingHandlerArrayContainsNullItem", resourceCulture);
}
}
/// <summary>
- /// Looks up a localized string similar to The type {0} does not appear to implement Microsoft.Practices.ServiceLocation.IServiceLocator..
+ /// Looks up a localized string similar to The &apos;{0}&apos; list is invalid because the property &apos;{1}&apos; of &apos;{2}&apos; is not null..
/// </summary>
- internal static string DependencyResolver_DoesNotImplementICommonServiceLocator {
+ internal static string DelegatingHandlerArrayHasNonNullInnerHandler {
get {
- return ResourceManager.GetString("DependencyResolver_DoesNotImplementICommonServiceLocator", resourceCulture);
+ return ResourceManager.GetString("DelegatingHandlerArrayHasNonNullInnerHandler", resourceCulture);
}
}
View
6 src/System.Web.Http/Properties/SRResources.resx
@@ -156,9 +156,6 @@
<data name="DataAnnotationsModelMetadataProvider_UnreadableProperty" xml:space="preserve">
<value>{0} has a DisplayColumn attribute for {1}, but property {1} does not have a public getter.</value>
</data>
- <data name="DependencyResolver_DoesNotImplementICommonServiceLocator" xml:space="preserve">
- <value>The type {0} does not appear to implement Microsoft.Practices.ServiceLocation.IServiceLocator.</value>
- </data>
<data name="ModelBinderProviderCollection_BinderForTypeNotFound" xml:space="preserve">
<value>A binder for type {0} could not be located.</value>
</data>
@@ -482,4 +479,7 @@ The request for '{0}' has found the following matching controllers:{2}</value>
<data name="HttpRequestMessageExtensions_NoMatchingFormatter" xml:space="preserve">
<value>Could not find a formatter matching the media type '{0}' that can write an instance of '{1}'.</value>
</data>
+ <data name="DefaultServices_InvalidServiceType" xml:space="preserve">
+ <value>The service type {0} is not supported.</value>
+ </data>
</root>
View
152 src/System.Web.Http/Services/DefaultServiceResolver.cs
@@ -1,152 +0,0 @@
-using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
-using System.Diagnostics.Contracts;
-using System.Linq;
-using System.Net.Http.Formatting;
-using System.Web.Http.Controllers;
-using System.Web.Http.Description;
-using System.Web.Http.Dispatcher;
-using System.Web.Http.Filters;
-using System.Web.Http.Metadata;
-using System.Web.Http.Metadata.Providers;
-using System.Web.Http.ModelBinding;
-using System.Web.Http.ModelBinding.Binders;
-using System.Web.Http.Tracing;
-using System.Web.Http.Validation;
-using System.Web.Http.Validation.Providers;
-using System.Web.Http.ValueProviders;
-using System.Web.Http.ValueProviders.Providers;
-
-namespace System.Web.Http.Services
-{
- /// <summary>
- /// This class is what <see cref="DependencyResolver"/> will ultimately fall back to.
- /// It handles built-in dependencies that the runtime expects to be present.
- /// </summary>
- internal class DefaultServiceResolver : IDependencyResolver
- {
- private readonly IDictionary<Type, object[]> _services = new Dictionary<Type, object[]>();
-
- // Use activator function instead of just object so that we can avoid eagerly calling the constructor.
- // This is especially important when the constructors will in turn ask for other objects. In that case, eagerly calling
- // ctors can result in infinite recursion.
- private readonly IDictionary<Type, Func<HttpConfiguration, object>> _deferredService = new Dictionary<Type, Func<HttpConfiguration, object>>();
-
- private readonly HttpConfiguration _configuration;
-
- [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "Class needs references to large number of types.")]
- public DefaultServiceResolver(HttpConfiguration configuration)
- {
- if (configuration == null)
- {
- throw Error.ArgumentNull("configuration");
- }
-
- _configuration = configuration;
-
- Add<IAssembliesResolver>(new DefaultAssembliesResolver());
-
- Add<IHttpControllerTypeResolver>(new DefaultHttpControllerTypeResolver());
-
- Add<IHttpControllerSelector>(config => new DefaultHttpControllerSelector(config));
-
- Add<IHttpControllerActivator>(config => new DefaultHttpControllerActivator(config));
-
- Add<IHttpActionSelector>(config => new ApiControllerActionSelector());
-
- Add<IHttpActionInvoker>(config => new ApiControllerActionInvoker());
-
- Add<ModelMetadataProvider>(new EmptyModelMetadataProvider());
-
- Add<IContentNegotiator>(new DefaultContentNegotiator());
-
- Add<IActionValueBinder>(new DefaultActionValueBinder());
-
- Add<IApiExplorer>(new ApiExplorer(configuration));
-
- Add<IBodyModelValidator>(new DefaultBodyModelValidator());
-
- AddRange<IFilterProvider>(
- new ConfigurationFilterProvider(),
- new ActionDescriptorFilterProvider());
-
- AddRange<ModelBinderProvider>(
- new TypeMatchModelBinderProvider(),
- new BinaryDataModelBinderProvider(),
- new KeyValuePairModelBinderProvider(),
- new ComplexModelDtoModelBinderProvider(),
- new ArrayModelBinderProvider(),
- new DictionaryModelBinderProvider(),
- new CollectionModelBinderProvider(),
- new TypeConverterModelBinderProvider(),
- new MutableObjectModelBinderProvider());
-
- AddRange<ModelValidatorProvider>(
- new DataAnnotationsModelValidatorProvider(),
- new DataMemberModelValidatorProvider());
-
- AddRange<ValueProviderFactory>(
- //new FormValueProviderFactory(),
- //new JsonValueProviderFactory(),
- new RouteDataValueProviderFactory(),
- new QueryStringValueProviderFactory());
-
- Add<ModelMetadataProvider>(new CachedDataAnnotationsModelMetadataProvider());
-
- Add<ITraceManager>(new TraceManager());
- }
-
- // activator creates the instance of TInterface
- private void Add<TInterface>(Func<HttpConfiguration, object> activator)
- {
- Type type = typeof(TInterface);
- Contract.Assert(type.IsInterface || type.IsAbstract);
-
- _deferredService[typeof(TInterface)] = activator;
- }
-
- // singleton for all requests for TInterface
- private void Add<TInterface>(TInterface singleton)
- {
- Add<TInterface>(_ => singleton); // just return existing instance
- }
-
- private void AddRange<T>(params object[] services)
- {
- // We'd like this to be T[] services, but you can't cast from T[] to object[].
- _services[typeof(T)] = services;
- }
-
- public object GetService(Type t)
- {
- Contract.Assert(t != null);
-
- Func<HttpConfiguration, object> activator;
- if (_deferredService.TryGetValue(t, out activator))
- {
- return activator(_configuration);
- }
-
- IEnumerable<object> results = GetServices(t);
- if (results == null)
- {
- return null;
- }
-
- return results.FirstOrDefault();
- }
-
- public IEnumerable<object> GetServices(Type t)
- {
- Contract.Assert(t != null);
-
- object[] services;
- if (!_services.TryGetValue(t, out services))
- {
- return Enumerable.Empty<object>();
- }
-
- return services;
- }
- }
-}
View
597 src/System.Web.Http/Services/DefaultServices.cs
@@ -0,0 +1,597 @@
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.Contracts;
+using System.Linq;
+using System.Net.Http.Formatting;
+using System.Threading;
+using System.Web.Http.Controllers;
+using System.Web.Http.Dependencies;
+using System.Web.Http.Description;
+using System.Web.Http.Dispatcher;
+using System.Web.Http.Filters;
+using System.Web.Http.Metadata;
+using System.Web.Http.Metadata.Providers;
+using System.Web.Http.ModelBinding;
+using System.Web.Http.ModelBinding.Binders;
+using System.Web.Http.Properties;
+using System.Web.Http.Tracing;
+using System.Web.Http.Validation;
+using System.Web.Http.Validation.Providers;
+using System.Web.Http.ValueProviders;
+using System.Web.Http.ValueProviders.Providers;
+
+namespace System.Web.Http.Services
+{
+ /// <summary>
+ /// <para>
+ /// Represents a container for service instances used by the <see cref="HttpConfiguration"/>. Note that
+ /// this container only supports known types, and methods to get or set arbitrary service types will
+ /// throw <see cref="ArgumentException"/> when called. For creation of arbitrary types, please use
+ /// <see cref="IDependencyResolver"/> instead. The supported types for this container are:
+ /// </para>
+ /// <list type="bullet">
+ /// <item><see cref="IActionValueBinder"/></item>
+ /// <item><see cref="IApiExplorer"/></item>
+ /// <item><see cref="IAssembliesResolver"/></item>
+ /// <item><see cref="IBodyModelValidator"/></item>
+ /// <item><see cref="IContentNegotiator"/></item>
+ /// <item><see cref="IDocumentationProvider"/></item>
+ /// <item><see cref="IFilterProvider"/></item>
+ /// <item><see cref="IHttpActionInvoker"/></item>
+ /// <item><see cref="IHttpActionSelector"/></item>
+ /// <item><see cref="IHttpControllerActivator"/></item>
+ /// <item><see cref="IHttpControllerSelector"/></item>
+ /// <item><see cref="IHttpControllerTypeResolver"/></item>
+ /// <item><see cref="ITraceManager"/></item>
+ /// <item><see cref="ITraceWriter"/></item>
+ /// <item><see cref="ModelBinderProvider"/></item>
+ /// <item><see cref="ModelMetadataProvider"/></item>
+ /// <item><see cref="ModelValidatorProvider"/></item>
+ /// <item><see cref="ValueProviderFactory"/></item>
+ /// </list>
+ /// <para>
+ /// Passing any type which is not on this to any method on this interface will cause
+ /// an <see cref="ArgumentException"/> to be thrown.
+ /// </para>
+ /// </summary>
+ [SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", Justification = "Although this class is not sealed, end users cannot set instances of it so in practice it is sealed.")]
+ public class DefaultServices : IDisposable
+ {
+ // This lock protects both caches (and _lastKnownDependencyResolver is updated under it as well)
+ private readonly ReaderWriterLockSlim _cacheLock = new ReaderWriterLockSlim();
+ private readonly Dictionary<Type, object[]> _cacheMulti = new Dictionary<Type, object[]>();
+ private readonly Dictionary<Type, object> _cacheSingle = new Dictionary<Type, object>();
+ private readonly HttpConfiguration _configuration;
+ private readonly Dictionary<Type, List<object>> _defaultServices = new Dictionary<Type, List<object>>();
+ private IDependencyResolver _lastKnownDependencyResolver;
+ private readonly HashSet<Type> _serviceTypes;
+
+ /// <summary>
+ /// This constructor is for unit testing purposes only.
+ /// </summary>
+ protected DefaultServices()
+ {
+ }
+
+ [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "Class needs references to large number of types.")]
+ public DefaultServices(HttpConfiguration configuration)
+ {
+ if (configuration == null)
+ {
+ throw Error.ArgumentNull("configuration");
+ }
+
+ _configuration = configuration;
+
+ // Initialize the dictionary with all known service types, even if the list for that service type is
+ // empty, because we will throw if the developer tries to read or write unsupported types.
+ _defaultServices.Add(typeof(IActionValueBinder), new List<object> { new DefaultActionValueBinder() });
+ _defaultServices.Add(typeof(IApiExplorer), new List<object> { new ApiExplorer(configuration) });
+ _defaultServices.Add(typeof(IAssembliesResolver), new List<object> { new DefaultAssembliesResolver() });
+ _defaultServices.Add(typeof(IBodyModelValidator), new List<object> { new DefaultBodyModelValidator() });
+ _defaultServices.Add(typeof(IContentNegotiator), new List<object> { new DefaultContentNegotiator() });
+ _defaultServices.Add(typeof(IDocumentationProvider), new List<object>());
+ _defaultServices.Add(typeof(IFilterProvider), new List<object>
+ {
+ new ConfigurationFilterProvider(),
+ new ActionDescriptorFilterProvider()
+ });
+ _defaultServices.Add(typeof(IHttpActionInvoker), new List<object> { new ApiControllerActionInvoker() });
+ _defaultServices.Add(typeof(IHttpActionSelector), new List<object> { new ApiControllerActionSelector() });
+ _defaultServices.Add(typeof(IHttpControllerActivator), new List<object> { new DefaultHttpControllerActivator() });
+ _defaultServices.Add(typeof(IHttpControllerSelector), new List<object> { new DefaultHttpControllerSelector(configuration) });
+ _defaultServices.Add(typeof(IHttpControllerTypeResolver), new List<object> { new DefaultHttpControllerTypeResolver() });
+ _defaultServices.Add(typeof(ITraceManager), new List<object> { new TraceManager() });
+ _defaultServices.Add(typeof(ITraceWriter), new List<object>());
+ _defaultServices.Add(typeof(ModelBinderProvider), new List<object>
+ {
+ new TypeMatchModelBinderProvider(),
+ new BinaryDataModelBinderProvider(),
+ new KeyValuePairModelBinderProvider(),
+ new ComplexModelDtoModelBinderProvider(),
+ new ArrayModelBinderProvider(),
+ new DictionaryModelBinderProvider(),
+ new CollectionModelBinderProvider(),
+ new TypeConverterModelBinderProvider(),
+ new MutableObjectModelBinderProvider()
+ });
+ _defaultServices.Add(typeof(ModelMetadataProvider), new List<object> { new CachedDataAnnotationsModelMetadataProvider() });
+ _defaultServices.Add(typeof(ModelValidatorProvider), new List<object>
+ {
+ new DataAnnotationsModelValidatorProvider(),
+ new DataMemberModelValidatorProvider()
+ });
+ _defaultServices.Add(typeof(ValueProviderFactory), new List<object>
+ {
+ new RouteDataValueProviderFactory(),
+ new QueryStringValueProviderFactory()
+ });
+
+ _serviceTypes = new HashSet<Type>(_defaultServices.Keys);
+ // Reset the caches and the known dependency scope
+ ResetCache();
+ }
+
+ /// <summary>
+ /// Returns a list of supported service types registered in the service list.
+ /// </summary>
+ public ICollection<Type> ServiceTypes
+ {
+ get { return _serviceTypes.ToArray(); }
+ }
+
+ /// <summary>
+ /// Adds a service to the end of services list for the given service type.
+ /// </summary>
+ /// <param name="serviceType">The service type.</param>
+ /// <param name="service">The service instance.</param>
+ public void Add(Type serviceType, object service)
+ {
+ if (serviceType == null)
+ {
+ throw Error.ArgumentNull("serviceType");
+ }
+ if (service == null)
+ {
+ throw Error.ArgumentNull("service");
+ }
+ if (!serviceType.IsAssignableFrom(service.GetType()))
+ {
+ throw Error.Argument("service", SRResources.Common_TypeMustDriveFromType, service.GetType().Name, serviceType.Name);
+ }
+
+ List<object> instances = GetServiceInstances(serviceType);
+ instances.Add(service);
+
+ ResetCache(serviceType);
+ }
+
+ /// <summary>
+ /// Adds the services of the specified collection to the end of the services list for
+ /// the given service type.
+ /// </summary>
+ /// <param name="serviceType">The service type.</param>
+ /// <param name="services">The services to add.</param>
+ public void AddRange(Type serviceType, IEnumerable<object> services)
+ {
+ if (serviceType == null)
+ {
+ throw Error.ArgumentNull("serviceType");
+ }
+ if (services == null)
+ {
+ throw Error.ArgumentNull("services");
+ }
+
+ object[] filteredServices = services.Where(svc => svc != null).ToArray();
+ object incorrectlyTypedService = filteredServices.FirstOrDefault(svc => !serviceType.IsAssignableFrom(svc.GetType()));
+ if (incorrectlyTypedService != null)
+ {
+ throw Error.Argument("services", SRResources.Common_TypeMustDriveFromType, incorrectlyTypedService.GetType().Name, serviceType.Name);
+ }
+
+ List<object> instances = GetServiceInstances(serviceType);
+ instances.AddRange(filteredServices);
+
+ ResetCache(serviceType);
+ }
+
+ /// <summary>
+ /// Removes all the service instances of the given service type.
+ /// </summary>
+ /// <param name="serviceType">The service type to clear from the services list.</param>
+ public void Clear(Type serviceType)
+ {
+ if (serviceType == null)
+ {
+ throw Error.ArgumentNull("serviceType");
+ }
+
+ List<object> instances = GetServiceInstances(serviceType);
+ instances.Clear();
+
+ ResetCache(serviceType);
+ }
+
+ /// <inheritdoc/>
+ [SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", Justification = "Although this class is not sealed, end users cannot set instances of it so in practice it is sealed.")]
+ [SuppressMessage("Microsoft.Usage", "CA1816:CallGCSuppressFinalizeCorrectly", Justification = "Although this class is not sealed, end users cannot set instances of it so in practice it is sealed.")]
+ public virtual void Dispose()
+ {
+ _cacheLock.Dispose();
+ }
+
+ /// <summary>
+ /// Searches for a service that matches the conditions defined by the specified
+ /// predicate, and returns the zero-based index of the first occurrence.
+ /// </summary>
+ /// <param name="serviceType">The service type.</param>
+ /// <param name="match">The delegate that defines the conditions of the element
+ /// to search for. </param>
+ /// <returns>The zero-based index of the first occurrence, if found; otherwise, -1.</returns>
+ public int FindIndex(Type serviceType, Predicate<object> match)
+ {
+ if (serviceType == null)
+ {
+ throw Error.ArgumentNull("serviceType");
+ }
+ if (match == null)
+ {
+ throw Error.ArgumentNull("match");
+ }
+
+ List<object> instances = GetServiceInstances(serviceType);
+ return instances.FindIndex(match);
+ }
+
+ /// <summary>
+ /// Try to get a service of the given type.
+ /// </summary>
+ /// <param name="serviceType">The service type.</param>
+ /// <returns>The first instance of the service, or null if the service is not found.</returns>
+ public virtual object GetService(Type serviceType)
+ {
+ if (serviceType == null)
+ {
+ throw Error.ArgumentNull("serviceType");
+ }
+ if (!_serviceTypes.Contains(serviceType))
+ {
+ throw Error.Argument("serviceType", SRResources.DefaultServices_InvalidServiceType, serviceType.Name);
+ }
+
+ // Invalidate the cache if the dependency scope has switched
+ if (_lastKnownDependencyResolver != _configuration.DependencyResolver)
+ {
+ ResetCache();
+ }
+
+ object result;
+
+ _cacheLock.EnterReadLock();
+ try
+ {
+ if (_cacheSingle.TryGetValue(serviceType, out result))
+ {
+ return result;
+ }
+ }
+ finally
+ {
+ _cacheLock.ExitReadLock();
+ }
+
+ // Get the service from DI, outside of the lock. If we're coming up hot, this might
+ // mean we end up creating the service more than once.
+ object dependencyService = _configuration.DependencyResolver.GetService(serviceType);
+
+ _cacheLock.EnterWriteLock();
+ try
+ {
+ if (!_cacheSingle.TryGetValue(serviceType, out result))
+ {
+ result = dependencyService ?? _defaultServices[serviceType].FirstOrDefault();
+ _cacheSingle[serviceType] = result;
+ }
+
+ return result;
+ }
+ finally
+ {
+ _cacheLock.ExitWriteLock();
+ }
+ }
+
+ /// <summary>
+ /// Try to get a list of services of the given type.
+ /// </summary>
+ /// <param name="serviceType">The service type.</param>
+ /// <returns>The list of service instances of the given type. Returns an empty enumeration if the
+ /// service is not found. </returns>
+ public virtual IEnumerable<object> GetServices(Type serviceType)
+ {
+ if (serviceType == null)
+ {
+ throw Error.ArgumentNull("serviceType");
+ }
+ if (!_serviceTypes.Contains(serviceType))
+ {
+ throw Error.Argument("serviceType", SRResources.DefaultServices_InvalidServiceType, serviceType.Name);
+ }
+
+ // Invalidate the cache if the dependency scope has switched
+ if (_lastKnownDependencyResolver != _configuration.DependencyResolver)
+ {
+ ResetCache();
+ }
+
+ object[] result;
+
+ _cacheLock.EnterReadLock();
+ try
+ {
+ if (_cacheMulti.TryGetValue(serviceType, out result))
+ {
+ return result;
+ }
+ }
+ finally
+ {
+ _cacheLock.ExitReadLock();
+ }
+
+ // Get the service from DI, outside of the lock. If we're coming up hot, this might
+ // mean we end up creating the service more than once.
+ IEnumerable<object> dependencyServices = _configuration.DependencyResolver.GetServices(serviceType);
+
+ _cacheLock.EnterWriteLock();
+ try
+ {
+ if (!_cacheMulti.TryGetValue(serviceType, out result))
+ {
+ result = dependencyServices.Where(s => s != null)
+ .Concat(_defaultServices[serviceType])
+ .ToArray();
+ _cacheMulti[serviceType] = result;
+ }
+
+ return result;
+ }
+ finally
+ {
+ _cacheLock.ExitWriteLock();
+ }
+ }
+
+ // Returns the List<object> for the given service type. Also validates serviceType is in the known service type list.
+ private List<object> GetServiceInstances(Type serviceType)
+ {
+ Contract.Assert(serviceType != null);
+
+ List<object> result;
+ if (!_defaultServices.TryGetValue(serviceType, out result))
+ {
+ throw Error.Argument("serviceType", SRResources.DefaultServices_InvalidServiceType, serviceType.Name);
+ }
+
+ return result;
+ }
+
+ /// <summary>
+ /// Inserts a service into the collection at the specified index.
+ /// </summary>
+ /// <param name="serviceType">The service type.</param>
+ /// <param name="index">The zero-based index at which the service should be inserted.</param>
+ /// <param name="service">The service to insert.</param>
+ public void Insert(Type serviceType, int index, object service)
+ {
+ if (serviceType == null)
+ {
+ throw Error.ArgumentNull("serviceType");
+ }
+ if (service == null)
+ {
+ throw Error.ArgumentNull("service");
+ }
+ if (!serviceType.IsAssignableFrom(service.GetType()))
+ {
+ throw Error.Argument("service", SRResources.Common_TypeMustDriveFromType, service.GetType().Name, serviceType.Name);
+ }
+
+ List<object> instances = GetServiceInstances(serviceType);
+ instances.Insert(index, service);
+
+ ResetCache(serviceType);
+ }
+
+ /// <summary>
+ /// Inserts the elements of the collection into the service list at the specified index.
+ /// </summary>
+ /// <param name="serviceType">The service type.</param>
+ /// <param name="index">The zero-based index at which the new elements should be inserted.</param>
+ /// <param name="services">The collection of services to insert.</param>
+ public void InsertRange(Type serviceType, int index, IEnumerable<object> services)
+ {
+ if (serviceType == null)
+ {
+ throw Error.ArgumentNull("serviceType");
+ }
+ if (services == null)
+ {
+ throw Error.ArgumentNull("services");
+ }
+
+ object[] filteredServices = services.Where(svc => svc != null).ToArray();
+ object incorrectlyTypedService = filteredServices.FirstOrDefault(svc => !serviceType.IsAssignableFrom(svc.GetType()));
+ if (incorrectlyTypedService != null)
+ {
+ throw Error.Argument("services", SRResources.Common_TypeMustDriveFromType, incorrectlyTypedService.GetType().Name, serviceType.Name);
+ }
+
+ List<object> instances = GetServiceInstances(serviceType);
+ instances.InsertRange(index, filteredServices);
+
+ ResetCache(serviceType);
+ }
+
+ /// <summary>
+ /// Removes the first occurrence of the given service from the service list for the given service type.
+ /// </summary>
+ /// <param name="serviceType">The service type.</param>
+ /// <param name="service">The service instance to remove.</param>
+ /// <returns> <c>true</c> if the item is successfull removed; otherwise, <c>false</c>.</returns>
+ public bool Remove(Type serviceType, object service)
+ {
+ if (serviceType == null)
+ {
+ throw Error.ArgumentNull("serviceType");
+ }
+ if (service == null)
+ {
+ throw Error.ArgumentNull("service");
+ }
+
+ List<object> instances = GetServiceInstances(serviceType);
+ bool result = instances.Remove(service);
+
+ ResetCache(serviceType);
+
+ return result;
+ }
+
+ /// <summary>
+ /// Removes all the elements that match the conditions defined by the specified predicate.
+ /// </summary>
+ /// <param name="serviceType">The service type.</param>
+ /// <param name="match">The delegate that defines the conditions of the elements to remove.</param>
+ /// <returns>The number of elements removed from the list.</returns>
+ public int RemoveAll(Type serviceType, Predicate<object> match)
+ {
+ if (serviceType == null)
+ {
+ throw Error.ArgumentNull("serviceType");
+ }
+ if (match == null)
+ {
+ throw Error.ArgumentNull("match");
+ }
+
+ List<object> instances = GetServiceInstances(serviceType);
+ int result = instances.RemoveAll(match);
+
+ ResetCache(serviceType);
+
+ return result;
+ }
+
+ /// <summary>
+ /// Removes the service at the specified index.
+ /// </summary>
+ /// <param name="serviceType">The service type.</param>
+ /// <param name="index">The zero-based index of the service to remove.</param>
+ public void RemoveAt(Type serviceType, int index)
+ {
+ if (serviceType == null)
+ {
+ throw Error.ArgumentNull("serviceType");
+ }
+
+ List<object> instances = GetServiceInstances(serviceType);
+ instances.RemoveAt(index);
+
+ ResetCache(serviceType);
+ }
+
+ /// <summary>
+ /// Replaces all existing services for the given service type with the given
+ /// service instance.
+ /// </summary>
+ /// <param name="serviceType">The service type.</param>
+ /// <param name="service">The service instance.</param>
+ public void Replace(Type serviceType, object service)
+ {
+ if (serviceType == null)
+ {
+ throw Error.ArgumentNull("serviceType");
+ }
+ if (service == null)
+ {
+ throw Error.ArgumentNull("service");
+ }
+ if (!serviceType.IsAssignableFrom(service.GetType()))
+ {
+ throw Error.Argument("service", SRResources.Common_TypeMustDriveFromType, service.GetType().Name, serviceType.Name);
+ }
+
+ List<object> instances = GetServiceInstances(serviceType);
+ instances.Clear();
+ instances.Add(service);
+
+ ResetCache(serviceType);
+ }
+
+ /// <summary>
+ /// Replaces all existing services for the given service type with the given
+ /// service instances.
+ /// </summary>
+ /// <param name="serviceType">The service type.</param>
+ /// <param name="services">The service instances.</param>
+ public void ReplaceRange(Type serviceType, IEnumerable<object> services)
+ {
+ if (serviceType == null)
+ {
+ throw Error.ArgumentNull("serviceType");
+ }
+ if (services == null)
+ {
+ throw Error.ArgumentNull("services");
+ }
+
+ object[] filteredServices = services.Where(svc => svc != null).ToArray();
+ object incorrectlyTypedService = filteredServices.FirstOrDefault(svc => !serviceType.IsAssignableFrom(svc.GetType()));
+ if (incorrectlyTypedService != null)
+ {
+ throw Error.Argument("services", SRResources.Common_TypeMustDriveFromType, incorrectlyTypedService.GetType().Name, serviceType.Name);
+ }
+
+ List<object> instances = GetServiceInstances(serviceType);
+ instances.Clear();
+ instances.AddRange(filteredServices);
+
+ ResetCache(serviceType);
+ }
+
+ // Removes the cached values for all service types. Called when the dependency scope
+ // has changed since the last time we made a request.
+ private void ResetCache()
+ {
+ _cacheLock.EnterWriteLock();
+ try
+ {
+ _cacheSingle.Clear();
+ _cacheMulti.Clear();
+ _lastKnownDependencyResolver = _configuration.DependencyResolver;
+ }
+ finally
+ {
+ _cacheLock.ExitWriteLock();
+ }
+ }
+
+ // Removes the cached values for a single service type. Called whenever the user manipulates
+ // the local service list for a given service type.
+ private void ResetCache(Type serviceType)
+ {
+ _cacheLock.EnterWriteLock();
+ try
+ {
+ _cacheSingle.Remove(serviceType);
+ _cacheMulti.Remove(serviceType);
+ }
+ finally
+ {
+ _cacheLock.ExitWriteLock();
+ }
+ }
+ }
+}
View
254 src/System.Web.Http/Services/DependencyResolver.cs
@@ -1,254 +0,0 @@
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
-using System.Linq;
-using System.Reflection;
-using System.Web.Http.Properties;
-
-namespace System.Web.Http.Services
-{
- public class DependencyResolver
- {
- // Allows user to specifically override certain resolution types, without needing to create a new DependencyResolver
- private ConcurrentDictionary<Type, object[]> _overrides = new ConcurrentDictionary<Type, object[]>();
-
- // Provides default objects for certain MVC types.
- private IDependencyResolver _defaultServiceBuiltInResolver;
-
- // The user supplied resolver. We provide a default implementation in case its null.
- private IDependencyResolver _userResolver;
-
- // Cache should always be a new CacheDependencyResolver(_current).
- private ConcurrentDictionary<Type, object> _cacheSingle = new ConcurrentDictionary<Type, object>();
- private ConcurrentDictionary<Type, IEnumerable<object>> _cacheMultiple = new ConcurrentDictionary<Type, IEnumerable<object>>();
-
- public DependencyResolver(HttpConfiguration configuration)
- : this(configuration, new DefaultServiceResolver(configuration))
- {
- }
-
- public DependencyResolver(HttpConfiguration configuration, IDependencyResolver defaultServiceResolver)
- {
- if (configuration == null)
- {
- throw Error.ArgumentNull("configuration");
- }
-
- SetResolver(new EmptyDependencyResolver());
-
- _defaultServiceBuiltInResolver = defaultServiceResolver ?? new EmptyDependencyResolver();
- }
-
- public void SetService(Type serviceType, object value)
- {
- if (serviceType == null)
- {
- throw Error.ArgumentNull("serviceType");
- }
-
- if (value == null)
- {
- object[] ignore;
- _overrides.TryRemove(serviceType, out ignore);
- return;
- }
-
- SetServices(serviceType, new object[] { value });
- }
-
- public void SetServices(Type serviceType, params object[] values)
- {
- if (serviceType == null)
- {
- throw Error.ArgumentNull("serviceType");
- }
-
- _overrides[serviceType] = values;
- }
-
- protected object GetCachedService(Type serviceType)
- {
- if (serviceType == null)
- {
- throw Error.ArgumentNull("serviceType");
- }
-
- return _cacheSingle.GetOrAdd(serviceType, (x) => GetService(x));
- }
-
- protected IEnumerable<object> GetCachedServices(Type serviceType)
- {
- if (serviceType == null)
- {
- throw Error.ArgumentNull("serviceType");
- }
-
- return _cacheMultiple.GetOrAdd(serviceType, (x) => GetServices(x));
- }
-
- public object GetService(Type serviceType)
- {
- if (serviceType == null)
- {
- throw Error.ArgumentNull("serviceType");
- }
-
- object result;
-
- // First lookup in overrides
- object[] results;
- if (_overrides.TryGetValue(serviceType, out results))
- {
- if ((results != null) && (results.Length > 0))
- {
- return results[0];
- }
- }
-
- // Then ask user
- result = _userResolver.GetService(serviceType);
- if (result != null)
- {
- return result;
- }
-
- // Then try defaults
- result = _defaultServiceBuiltInResolver.GetService(serviceType);
- return result;
- }
-
- public IEnumerable<object> GetServices(Type serviceType)
- {
- if (serviceType == null)
- {
- throw Error.ArgumentNull("serviceType");
- }
-
- IEnumerable<object> result;
-
- // First lookup override
- object[] results;
- if (_overrides.TryGetValue(serviceType, out results))
- {
- if (results != null)
- {
- return results;
- }
- }
-
- // Then ask user
- result = _userResolver.GetServices(serviceType);
- if (result.FirstOrDefault() != null)
- {
- return result;
- }
-
- // Then try defaults
- result = _defaultServiceBuiltInResolver.GetServices(serviceType);
- return result;
- }
-
- public void SetResolver(IDependencyResolver resolver)
- {
- if (resolver == null)
- {
- throw Error.ArgumentNull("resolver");
- }
-
- _userResolver = resolver;
- ResetCache();
- }
-
- public void SetResolver(object commonServiceLocator)
- {
- if (commonServiceLocator == null)
- {
- throw Error.ArgumentNull("commonServiceLocator");
- }
-
- Type locatorType = commonServiceLocator.GetType();
- MethodInfo getInstance = locatorType.GetMethod("GetInstance", new[] { typeof(Type) });
- MethodInfo getInstances = locatorType.GetMethod("GetAllInstances", new[] { typeof(Type) });
-
- if (getInstance == null ||
- getInstance.ReturnType != typeof(object) ||
- getInstances == null ||
- getInstances.ReturnType != typeof(IEnumerable<object>))
- {
- throw Error.Argument("commonServiceLocator", SRResources.DependencyResolver_DoesNotImplementICommonServiceLocator, locatorType.FullName);
- }
-
- var getService = (Func<Type, object>)Delegate.CreateDelegate(typeof(Func<Type, object>), commonServiceLocator, getInstance);
- var getServices = (Func<Type, IEnumerable<object>>)Delegate.CreateDelegate(typeof(Func<Type, IEnumerable<object>>), commonServiceLocator, getInstances);
-
- SetResolver(new DelegateBasedDependencyResolver(getService, getServices));
- }
-
- [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types.")]
- public void SetResolver(Func<Type, object> getService, Func<Type, IEnumerable<object>> getServices)
- {
- if (getService == null)
- {
- throw Error.ArgumentNull("getService");
- }
-
- if (getServices == null)
- {
- throw Error.ArgumentNull("getServices");
- }
-
- SetResolver(new DelegateBasedDependencyResolver(getService, getServices));
- }
-
- private void ResetCache()
- {
- _cacheSingle = new ConcurrentDictionary<Type, object>();
- _cacheMultiple = new ConcurrentDictionary<Type, IEnumerable<object>>();
- }
-
- // Helper classes
-
- private class DelegateBasedDependencyResolver : IDependencyResolver
- {
- private Func<Type, object> _getService;
- private Func<Type, IEnumerable<object>> _getServices;
-
- public DelegateBasedDependencyResolver(Func<Type, object> getService, Func<Type, IEnumerable<object>> getServices)
- {
- _getService = getService;
- _getServices = getServices;
- }
-
- [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "This method might throw exceptions whose type we cannot strongly link against; namely, ActivationException from common service locator")]
- public object GetService(Type type)
- {
- try
- {
- return _getService.Invoke(type);
- }
- catch
- {
- return null;
- }
- }
-
- public IEnumerable<object> GetServices(Type type)
- {
- return _getServices(type);
- }
- }
-
- private class EmptyDependencyResolver : IDependencyResolver
- {
- public object GetService(Type serviceType)
- {
- return null;
- }
-
- public IEnumerable<object> GetServices(Type serviceType)
- {
- return Enumerable.Empty<object>();
- }
- }
- }
-}
View
22 src/System.Web.Http/Services/IDependencyResolver.cs
@@ -1,22 +0,0 @@
-using System.Collections.Generic;
-
-namespace System.Web.Http.Services
-{
- public interface IDependencyResolver
- {
- /// <summary>
- /// Try to get a service of the given type.
- /// </summary>
- /// <param name="serviceType">Type of service to request.</param>
- /// <returns>an instance of the service, or null if the service is not found</returns>
- object GetService(Type serviceType);
-
- /// <summary>
- /// Try to get a list of services of the given type.
- /// </summary>
- /// <param name="serviceType">Type of services to request.</param>
- /// <returns>an enumeration (possibly empty) of the service.
- /// Return an empty enumeration is the service is not found (don't return null)</returns>
- IEnumerable<object> GetServices(Type serviceType);
- }
-}
View
98 ....Web.Http/DependencyResolverExtensions.cs → src/System.Web.Http/ServicesExtensions.cs
@@ -18,140 +18,138 @@
namespace System.Web.Http
{
/// <summary>
- /// This provides a centralized list of type-safe accessors describing where and how we use the dependency resolver.
- /// This also provides a single entry point for each service request. That makes it easy
+ /// This provides a centralized list of type-safe accessors describing where and how we get services.
+ /// This also provides a single entry point for each service request. That makes it easy
/// to see which parts of the code use it, and provides a single place to comment usage.
/// Accessors encapsulate usage like:
/// <list type="bullet">
/// <item>Type-safe using {T} instead of unsafe <see cref="System.Type"/>.</item>
- /// <item>which type do we key off? This is interesting with type hierarchies.</item>
+ /// <item>which type do we key off? This is interesting with type hierarchies.</item>
/// <item>do we ask for singular or plural?</item>
/// <item>is it optional or mandatory?</item>
/// <item>what are the ordering semantics</item>
- /// <item>Do we use a cached value or not?</item>
/// </list>
/// Expected that any <see cref="IEnumerable{T}"/> we return is non-null, although possibly empty.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
- public static class DependencyResolverExtensions
+ public static class ServicesExtensions
{
/// <summary>
- /// Get ValueProviderFactories. The order of returned providers is the priority order that we search the factories.
+ /// Get ValueProviderFactories. The order of returned providers is the priority order that we search the factories.
/// </summary>
- public static IEnumerable<ValueProviderFactory> GetValueProviderFactories(this DependencyResolver resolver)
+ public static IEnumerable<ValueProviderFactory> GetValueProviderFactories(this DefaultServices services)
{
- return resolver.GetServices<ValueProviderFactory>();
+ return services.GetServices<ValueProviderFactory>();
}
/// <summary>
/// Get a controller selector, which selects an <see cref="HttpControllerDescriptor"/> given an <see cref="HttpRequestMessage"/>.
/// </summary>
- public static IHttpControllerSelector GetHttpControllerSelector(this DependencyResolver resolver)
+ public static IHttpControllerSelector GetHttpControllerSelector(this DefaultServices services)
{
- return resolver.GetServiceOrThrow<IHttpControllerSelector>();
+ return services.GetServiceOrThrow<IHttpControllerSelector>();
}
/// <summary>
- /// Controller activator is used to instantiate an <see cref="IHttpController"/>.
+ /// Controller activator is used to instantiate an <see cref="IHttpController"/>.
/// </summary>
/// <returns>
/// An <see cref="IHttpControllerActivator"/> instance or null if none are registered.
/// </returns>
- public static IHttpControllerActivator GetHttpControllerActivator(this DependencyResolver resolver)
+ public static IHttpControllerActivator GetHttpControllerActivator(this DefaultServices services)
{
- return resolver.GetServiceOrThrow<IHttpControllerActivator>();
+ return services.GetServiceOrThrow<IHttpControllerActivator>();
}
- public static IAssembliesResolver GetAssembliesResolver(this DependencyResolver resolver)
+ public static IAssembliesResolver GetAssembliesResolver(this DefaultServices services)
{
- return resolver.GetServiceOrThrow<IAssembliesResolver>();
+ return services.GetServiceOrThrow<IAssembliesResolver>();
}
- public static IHttpControllerTypeResolver GetHttpControllerTypeResolver(this DependencyResolver resolver)
+ public static IHttpControllerTypeResolver GetHttpControllerTypeResolver(this DefaultServices services)
{
- return resolver.GetServiceOrThrow<IHttpControllerTypeResolver>();
+ return services.GetServiceOrThrow<IHttpControllerTypeResolver>();
}
- public static IHttpActionSelector GetActionSelector(this DependencyResolver resolver)
+ public static IHttpActionSelector GetActionSelector(this DefaultServices services)
{
- return resolver.GetServiceOrThrow<IHttpActionSelector>();
+ return services.GetServiceOrThrow<IHttpActionSelector>();
}
- public static IHttpActionInvoker GetActionInvoker(this DependencyResolver resolver)
+ public static IHttpActionInvoker GetActionInvoker(this DefaultServices services)
{
- return resolver.GetServiceOrThrow<IHttpActionInvoker>();
+ return services.GetServiceOrThrow<IHttpActionInvoker>();
}
- public static IApiExplorer GetApiExplorer(this DependencyResolver resolver)
+ public static IApiExplorer GetApiExplorer(this DefaultServices services)
{
- return resolver.GetServiceOrThrow<IApiExplorer>();
+ return services.GetServiceOrThrow<IApiExplorer>();
}
- public static IDocumentationProvider GetDocumentationProvider(this DependencyResolver resolver)
+ public static IDocumentationProvider GetDocumentationProvider(this DefaultServices services)
{
- return resolver.GetService<IDocumentationProvider>();
+ return services.GetService<IDocumentationProvider>();
}
- public static IEnumerable<IFilterProvider> GetFilterProviders(this DependencyResolver resolver)
+ public static IEnumerable<IFilterProvider> GetFilterProviders(this DefaultServices services)
{
- return resolver.GetServices<IFilterProvider>();
+ return services.GetServices<IFilterProvider>();
}
- public static ModelMetadataProvider GetModelMetadataProvider(this DependencyResolver resolver)
+ public static ModelMetadataProvider GetModelMetadataProvider(this DefaultServices services)
{
- // TODO: this is called a lot - should we use this.GetCachedService<T>? instead
- return resolver.GetServiceOrThrow<ModelMetadataProvider>();
+ return services.GetServiceOrThrow<ModelMetadataProvider>();
}
- public static IEnumerable<ModelBinderProvider> GetModelBinderProviders(this DependencyResolver resolver)
+ public static IEnumerable<ModelBinderProvider> GetModelBinderProviders(this DefaultServices services)
{
- return resolver.GetServices<ModelBinderProvider>();
+ return services.GetServices<ModelBinderProvider>();
}
- public static IEnumerable<ModelValidatorProvider> GetModelValidatorProviders(this DependencyResolver resolver)
+ public static IEnumerable<ModelValidatorProvider> GetModelValidatorProviders(this DefaultServices services)
{
- return resolver.GetServices<ModelValidatorProvider>();
+ return services.GetServices<ModelValidatorProvider>();
}
- public static IContentNegotiator GetContentNegotiator(this DependencyResolver resolver)
+ public static IContentNegotiator GetContentNegotiator(this DefaultServices services)
{
- return resolver.GetService<IContentNegotiator>();
+ return services.GetService<IContentNegotiator>();
}
- public static IActionValueBinder GetActionValueBinder(this DependencyResolver resolver)
+ public static IActionValueBinder GetActionValueBinder(this DefaultServices services)
{
- return resolver.GetService<IActionValueBinder>();
+ return services.GetService<IActionValueBinder>();
}
- public static ITraceManager GetTraceManager(this DependencyResolver resolver)
+ public static ITraceManager GetTraceManager(this DefaultServices services)
{
- return resolver.GetService<ITraceManager>();
+ return services.GetService<ITraceManager>();
}
- public static ITraceWriter GetTraceWriter(this DependencyResolver resolver)
+ public static ITraceWriter GetTraceWriter(this DefaultServices services)
{
- return resolver.GetService<ITraceWriter>();
+ return services.GetService<ITraceWriter>();
}
- public static IBodyModelValidator GetBodyModelValidator(this DependencyResolver resolver)
+ public static IBodyModelValidator GetBodyModelValidator(this DefaultServices services)
{
- return resolver.GetService<IBodyModelValidator>();
+ return services.GetService<IBodyModelValidator>();
}
// Runtime code shouldn't call GetService() directly. Instead, have a wrapper (like the ones above) and call through the wrapper.
- private static TService GetService<TService>(this DependencyResolver resolver)
+ private static TService GetService<TService>(this DefaultServices services)
{
- return (TService)resolver.GetService(typeof(TService));
+ return (TService)services.GetService(typeof(TService));
}
- private static IEnumerable<TService> GetServices<TService>(this DependencyResolver resolver)
+ private static IEnumerable<TService> GetServices<TService>(this DefaultServices services)
{
- return resolver.GetServices(typeof(TService)).Cast<TService>();
+ return services.GetServices(typeof(TService)).Cast<TService>();
}
- private static T GetServiceOrThrow<T>(this DependencyResolver resolver)
+ private static T GetServiceOrThrow<T>(this DefaultServices services)
{
- T result = resolver.GetService<T>();
+ T result = services.GetService<T>();
if (result == null)
{
throw Error.InvalidOperation(SRResources.DependencyResolverNoService, typeof(T).FullName);
View
9 src/System.Web.Http/System.Web.Http.csproj
@@ -91,6 +91,7 @@
<Compile Include="Controllers\ResponseMessageResultConverter.cs" />
<Compile Include="Controllers\ValueResultConverter.cs" />
<Compile Include="Controllers\VoidResultConverter.cs" />
+ <Compile Include="Dependencies\IDependencyScope.cs" />
<Compile Include="Dispatcher\DefaultAssembliesResolver.cs" />
<Compile Include="Dispatcher\IAssembliesResolver.cs" />
<Compile Include="HttpOptionsAttribute.cs" />
@@ -189,12 +190,12 @@
<Compile Include="NonActionAttribute.cs" />
<Compile Include="Routing\UriPathExtensionMapping.cs" />
<Compile Include="Routing\UrlHelper.cs" />
- <Compile Include="Services\DependencyResolver.cs" />
<Compile Include="Dispatcher\HttpControllerTypeCache.cs" />
<Compile Include="Dispatcher\DefaultHttpControllerSelector.cs" />
<Compile Include="Dispatcher\DefaultHttpControllerTypeResolver.cs" />
- <Compile Include="DependencyResolverExtensions.cs" />
- <Compile Include="Services\IDependencyResolver.cs" />
+ <Compile Include="ServicesExtensions.cs" />
+ <Compile Include="Dependencies\EmptyResolver.cs" />
+ <Compile Include="Dependencies\IDependencyResolver.cs" />
<Compile Include="Dispatcher\IHttpControllerSelector.cs" />
<Compile Include="Filters\FilterAttribute.cs" />