Imported Upstream version 3.6.0

Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
This commit is contained in:
Jo Shields
2014-08-13 10:39:27 +01:00
commit a575963da9
50588 changed files with 8155799 additions and 0 deletions

View File

@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
namespace System.Web.Http.Dispatcher
{
/// <summary>
/// Provides an implementation of <see cref="IAssembliesResolver"/> with no external dependencies.
/// </summary>
internal class DefaultAssembliesResolver : IAssembliesResolver
{
/// <summary>
/// Returns a list of assemblies available for the application.
/// </summary>
/// <returns>A <see cref="Collection{Assembly}"/> of assemblies.</returns>
ICollection<Assembly> IAssembliesResolver.GetAssemblies()
{
return AppDomain.CurrentDomain.GetAssemblies().ToList();
}
}
}

View File

@ -0,0 +1,91 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
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
{
// First check in the local fast cache and if not a match then look in the broader
// HttpControllerDescriptor.Properties cache
if (_fastCache == null)
{
// 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;
}
// Otherwise create a delegate for creating a new instance of the type
Func<IHttpController> activator = TypeActivator.Create<IHttpController>(controllerType);
Tuple<HttpControllerDescriptor, Func<IHttpController>> cacheItem = Tuple.Create(controllerDescriptor, activator);
Interlocked.CompareExchange(ref _fastCache, cacheItem, null);
// Execute the delegate
return activator();
}
else if (_fastCache.Item1 == controllerDescriptor)
{
// If the key matches and we already have the delegate for creating an instance then just execute it
return _fastCache.Item2();
}
else
{
// If the key doesn't match then lookup/create delegate in the HttpControllerDescriptor.Properties for
// that HttpControllerDescriptor instance
Func<IHttpController> activator = (Func<IHttpController>)controllerDescriptor.Properties.GetOrAdd(
_cacheKey,
key => TypeActivator.Create<IHttpController>(controllerType));
return activator();
}
}
catch (Exception ex)
{
throw Error.InvalidOperation(ex, SRResources.DefaultControllerFactory_ErrorCreatingController, controllerType.Name);
}
}
}
}

View File

@ -0,0 +1,165 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Web.Http.Controllers;
using System.Web.Http.Properties;
using System.Web.Http.Routing;
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="HttpConfiguration.Services"/>.
/// </summary>
public class DefaultHttpControllerSelector : IHttpControllerSelector
{
public static readonly string ControllerSuffix = "Controller";
private const string ControllerKey = "controller";
private readonly HttpConfiguration _configuration;
private readonly HttpControllerTypeCache _controllerTypeCache;
private readonly Lazy<ConcurrentDictionary<string, HttpControllerDescriptor>> _controllerInfoCache;
/// <summary>
/// Initializes a new instance of the <see cref="DefaultHttpControllerSelector"/> class.
/// </summary>
/// <param name="configuration">The configuration.</param>
public DefaultHttpControllerSelector(HttpConfiguration configuration)
{
if (configuration == null)
{
throw Error.ArgumentNull("configuration");
}
_controllerInfoCache = new Lazy<ConcurrentDictionary<string, HttpControllerDescriptor>>(InitializeControllerInfoCache);
_configuration = configuration;
_controllerTypeCache = new HttpControllerTypeCache(_configuration);
}
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Caller is responsible for disposing of response instance.")]
public virtual HttpControllerDescriptor SelectController(HttpRequestMessage request)
{
if (request == null)
{
throw Error.ArgumentNull("request");
}
string controllerName = GetControllerName(request);
if (String.IsNullOrEmpty(controllerName))
{
throw new HttpResponseException(request.CreateResponse(HttpStatusCode.NotFound));
}
HttpControllerDescriptor controllerDescriptor;
if (_controllerInfoCache.Value.TryGetValue(controllerName, out controllerDescriptor))
{
return controllerDescriptor;
}
ICollection<Type> matchingTypes = _controllerTypeCache.GetControllerTypes(controllerName);
// ControllerInfoCache is already initialized.
Contract.Assert(matchingTypes.Count != 1);
if (matchingTypes.Count == 0)
{
// no matching types
throw new HttpResponseException(request.CreateResponse(
HttpStatusCode.NotFound,
Error.Format(SRResources.DefaultControllerFactory_ControllerNameNotFound, controllerName)));
}
else
{
// multiple matching types
throw new HttpResponseException(request.CreateResponse(
HttpStatusCode.InternalServerError,
CreateAmbiguousControllerExceptionMessage(request.GetRouteData().Route, controllerName, matchingTypes)));
}
}
public virtual IDictionary<string, HttpControllerDescriptor> GetControllerMapping()
{
return _controllerInfoCache.Value.ToDictionary(c => c.Key, c => c.Value, StringComparer.OrdinalIgnoreCase);
}
public virtual string GetControllerName(HttpRequestMessage request)
{
if (request == null)
{
throw Error.ArgumentNull("request");
}
IHttpRouteData routeData = request.GetRouteData();
if (routeData == null)
{
return null;
}
// Look up controller in route data
string controllerName = null;
routeData.Values.TryGetValue(ControllerKey, out controllerName);
return controllerName;
}
private static string CreateAmbiguousControllerExceptionMessage(IHttpRoute route, string controllerName, ICollection<Type> matchingTypes)
{
Contract.Assert(route != null);
Contract.Assert(controllerName != null);
Contract.Assert(matchingTypes != null);
// Generate an exception containing all the controller types
StringBuilder typeList = new StringBuilder();
foreach (Type matchedType in matchingTypes)
{
typeList.AppendLine();
typeList.Append(matchedType.FullName);
}
return Error.Format(SRResources.DefaultControllerFactory_ControllerNameAmbiguous_WithRouteTemplate, controllerName, route.RouteTemplate, typeList);
}
private ConcurrentDictionary<string, HttpControllerDescriptor> InitializeControllerInfoCache()
{
var result = new ConcurrentDictionary<string, HttpControllerDescriptor>(StringComparer.OrdinalIgnoreCase);
var duplicateControllers = new HashSet<string>();
Dictionary<string, ILookup<string, Type>> controllerTypeGroups = _controllerTypeCache.Cache;
foreach (KeyValuePair<string, ILookup<string, Type>> controllerTypeGroup in controllerTypeGroups)
{
string controllerName = controllerTypeGroup.Key;
foreach (IGrouping<string, Type> controllerTypesGroupedByNs in controllerTypeGroup.Value)
{
foreach (Type controllerType in controllerTypesGroupedByNs)
{
if (result.Keys.Contains(controllerName))
{
duplicateControllers.Add(controllerName);
break;
}
else
{
result.TryAdd(controllerName, new HttpControllerDescriptor(_configuration, controllerName, controllerType));
}
}
}
}
foreach (string duplicateController in duplicateControllers)
{
HttpControllerDescriptor descriptor;
result.TryRemove(duplicateController, out descriptor);
}
return result;
}
}
}

View File

@ -0,0 +1,73 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Reflection;
using System.Web.Http.Internal;
namespace System.Web.Http.Dispatcher
{
/// <summary>
/// Provides an implementation of <see cref="IHttpControllerTypeResolver"/> with no external dependencies.
/// </summary>
public class DefaultHttpControllerTypeResolver : IHttpControllerTypeResolver
{
private readonly Predicate<Type> _isControllerTypePredicate;
public DefaultHttpControllerTypeResolver() : this(IsControllerType)
{
}
/// <summary>
/// Creates a new <see cref="DefaultHttpControllerTypeResolver"/> instance using a predicate to filter controller types.
/// </summary>
/// <param name="predicate">The predicate.</param>
public DefaultHttpControllerTypeResolver(Predicate<Type> predicate)
{
Contract.Assert(predicate != null);
_isControllerTypePredicate = predicate;
}
protected Predicate<Type> IsControllerTypePredicate
{
get { return _isControllerTypePredicate; }
}
private static bool IsControllerType(Type t)
{
return
t != null &&
t.IsClass &&
t.IsPublic &&
t.Name.EndsWith(DefaultHttpControllerSelector.ControllerSuffix, StringComparison.OrdinalIgnoreCase) &&
!t.IsAbstract &&
TypeHelper.HttpControllerType.IsAssignableFrom(t);
}
/// <summary>
/// Returns a list of controllers available for the application.
/// </summary>
/// <returns>An <see cref="ICollection{Type}"/> of controllers.</returns>
public virtual ICollection<Type> GetControllerTypes(IAssembliesResolver assembliesResolver)
{
// Go through all assemblies referenced by the application and search for types matching a predicate
IEnumerable<Type> typesSoFar = Type.EmptyTypes;
ICollection<Assembly> assemblies = assembliesResolver.GetAssemblies();
foreach (Assembly assembly in assemblies)
{
if (assembly.IsDynamic)
{
// can't call GetExportedTypes on a dynamic assembly
continue;
}
typesSoFar = typesSoFar.Concat(assembly.GetExportedTypes());
}
return typesSoFar.Where(x => IsControllerTypePredicate(x)).ToList();
}
}
}

View File

@ -0,0 +1,44 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Diagnostics.Contracts;
using System.Runtime.Serialization;
using System.Xml.Serialization;
namespace System.Web.Http.Dispatcher
{
// Infrastructure class used to serialize unhandled exceptions at the dispatcher layer. Not meant to be used by end users.
// Only public to enable its serialization by XmlSerializer and in partial trust.
[DataContract(Name = "Exception")]
[XmlRoot("Exception")]
public class ExceptionSurrogate
{
private ExceptionSurrogate()
{
}
internal ExceptionSurrogate(Exception exception)
{
Contract.Assert(exception != null);
Message = exception.Message;
StackTrace = exception.StackTrace;
if (exception.InnerException != null)
{
InnerException = new ExceptionSurrogate(exception.InnerException);
}
ExceptionType = exception.GetType().FullName;
}
[DataMember]
public string ExceptionType { get; set; }
[DataMember(EmitDefaultValue = false, IsRequired = false)]
public string Message { get; set; }
[DataMember(EmitDefaultValue = false, IsRequired = false)]
public string StackTrace { get; set; }
[DataMember(EmitDefaultValue = false, IsRequired = false)]
public ExceptionSurrogate InnerException { get; set; }
}
}

View File

@ -0,0 +1,209 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http.Controllers;
using System.Web.Http.Hosting;
using System.Web.Http.Properties;
using System.Web.Http.Routing;
namespace System.Web.Http.Dispatcher
{
/// <summary>
/// Dispatches an incoming <see cref="HttpRequestMessage"/> to an <see cref="IHttpController"/> implementation for processing.
/// </summary>
public class HttpControllerDispatcher : HttpMessageHandler
{
private const string ControllerKey = "controller";
private IHttpControllerSelector _controllerSelector;
private readonly HttpConfiguration _configuration;
private bool _disposed;
/// <summary>
/// Initializes a new instance of the <see cref="HttpControllerDispatcher"/> class using default <see cref="HttpConfiguration"/>.
/// </summary>
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope",
Justification = "The configuration object is disposed as part of this class.")]
public HttpControllerDispatcher()
: this(new HttpConfiguration())
{
}
/// <summary>
/// Initializes a new instance of the <see cref="HttpControllerDispatcher"/> class.
/// </summary>
public HttpControllerDispatcher(HttpConfiguration configuration)
{
if (configuration == null)
{
throw Error.ArgumentNull("configuration");
}
_configuration = configuration;
}
/// <summary>
/// Gets the <see cref="HttpConfiguration"/>.
/// </summary>
public HttpConfiguration Configuration
{
get { return _configuration; }
}
private IHttpControllerSelector ControllerSelector
{
get
{
if (_controllerSelector == null)
{
_controllerSelector = _configuration.Services.GetHttpControllerSelector();
}
return _controllerSelector;
}
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged SRResources.</param>
protected override void Dispose(bool disposing)
{
if (!_disposed)
{
_disposed = true;
if (disposing)
{
_configuration.Dispose();
}
}
base.Dispose(disposing);
}
/// <summary>
/// Dispatches an incoming <see cref="HttpRequestMessage"/> to an <see cref="IHttpController"/>.
/// </summary>
/// <param name="request">The request to dispatch</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A <see cref="Task{HttpResponseMessage}"/> representing the ongoing operation.</returns>
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "We report the error in the HTTP response.")]
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// Runs Content Negotiation and Error Handling on the result of SendAsyncInternal
try
{
return SendAsyncInternal(request, cancellationToken)
.Catch(info => info.Handled(HandleException(request, info.Exception, _configuration)));
}
catch (Exception exception)
{
return TaskHelpers.FromResult(HandleException(request, exception, _configuration));
}
}
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Caller becomes owner.")]
private Task<HttpResponseMessage> SendAsyncInternal(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request == null)
{
throw Error.ArgumentNull("request");
}
if (_disposed)
{
throw Error.ObjectDisposed(SRResources.HttpMessageHandlerDisposed, typeof(HttpControllerDispatcher).Name);
}
// Lookup route data, or if not found as a request property then we look it up in the route table
IHttpRouteData routeData;
if (!request.Properties.TryGetValue(HttpPropertyKeys.HttpRouteDataKey, out routeData))
{
routeData = _configuration.Routes.GetRouteData(request);
if (routeData != null)
{
request.Properties.Add(HttpPropertyKeys.HttpRouteDataKey, routeData);
}
else
{
// TODO, 328927, add an error message in the response body
return TaskHelpers.FromResult(request.CreateResponse(HttpStatusCode.NotFound));
}
}
RemoveOptionalRoutingParameters(routeData.Values);
HttpControllerDescriptor httpControllerDescriptor = ControllerSelector.SelectController(request);
if (httpControllerDescriptor == null)
{
// TODO, 328927, add an error message in the response body
return TaskHelpers.FromResult(request.CreateResponse(HttpStatusCode.NotFound));
}
IHttpController httpController = httpControllerDescriptor.CreateController(request);
if (httpController == null)
{
// TODO, 328927, add an error message in the response body
return TaskHelpers.FromResult(request.CreateResponse(HttpStatusCode.NotFound));
}
// Create context
HttpControllerContext controllerContext = new HttpControllerContext(_configuration, routeData, request);
controllerContext.Controller = httpController;
controllerContext.ControllerDescriptor = httpControllerDescriptor;
return httpController.ExecuteAsync(controllerContext, cancellationToken);
}
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Caller owns HttpResponseMessage instance.")]
private static HttpResponseMessage HandleException(HttpRequestMessage request, Exception exception, HttpConfiguration configuration)
{
Exception unwrappedException = exception.GetBaseException();
HttpResponseException httpResponseException = unwrappedException as HttpResponseException;
if (httpResponseException != null)
{
return httpResponseException.Response;
}
if (configuration.ShouldIncludeErrorDetail(request))
{
return request.CreateResponse<ExceptionSurrogate>(HttpStatusCode.InternalServerError, new ExceptionSurrogate(unwrappedException));
}
return new HttpResponseMessage(HttpStatusCode.InternalServerError);
}
private static void RemoveOptionalRoutingParameters(IDictionary<string, object> routeValueDictionary)
{
Contract.Assert(routeValueDictionary != null);
// Get all keys for which the corresponding value is 'Optional'.
// Having a separate array is necessary so that we don't manipulate the dictionary while enumerating.
// This is on a hot-path and linq expressions are showing up on the profile, so do array manipulation.
int max = routeValueDictionary.Count;
int i = 0;
string[] matching = new string[max];
foreach (KeyValuePair<string, object> kv in routeValueDictionary)
{
if (kv.Value == RouteParameter.Optional)
{
matching[i] = kv.Key;
i++;
}
}
for (int j = 0; j < i; j++)
{
string key = matching[j];
routeValueDictionary.Remove(key);
}
}
}
}

View File

@ -0,0 +1,69 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Linq;
namespace System.Web.Http.Dispatcher
{
/// <summary>
/// Manages a cache of <see cref="System.Web.Http.Controllers.IHttpController"/> types detected in the system.
/// </summary>
internal sealed class HttpControllerTypeCache
{
private readonly HttpConfiguration _configuration;
private readonly Lazy<Dictionary<string, ILookup<string, Type>>> _cache;
public HttpControllerTypeCache(HttpConfiguration configuration)
{
if (configuration == null)
{
throw Error.ArgumentNull("configuration");
}
_configuration = configuration;
_cache = new Lazy<Dictionary<string, ILookup<string, Type>>>(InitializeCache);
}
internal Dictionary<string, ILookup<string, Type>> Cache
{
get { return _cache.Value; }
}
public ICollection<Type> GetControllerTypes(string controllerName)
{
if (String.IsNullOrEmpty(controllerName))
{
throw Error.ArgumentNullOrEmpty("controllerName");
}
HashSet<Type> matchingTypes = new HashSet<Type>();
ILookup<string, Type> namespaceLookup;
if (_cache.Value.TryGetValue(controllerName, out namespaceLookup))
{
foreach (var namespaceGroup in namespaceLookup)
{
matchingTypes.UnionWith(namespaceGroup);
}
}
return matchingTypes;
}
private Dictionary<string, ILookup<string, Type>> InitializeCache()
{
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);
return groupedByName.ToDictionary(
g => g.Key,
g => g.ToLookup(t => t.Namespace ?? String.Empty, StringComparer.OrdinalIgnoreCase),
StringComparer.OrdinalIgnoreCase);
}
}
}

View File

@ -0,0 +1,22 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
namespace System.Web.Http.Dispatcher
{
/// <summary>
/// Provides an abstraction for managing the assemblies of an application. A different
/// implementation can be registered via the <see cref="T:System.Web.Http.Services.DependencyResolver"/>.
/// </summary>
public interface IAssembliesResolver
{
/// <summary>
/// Returns a list of assemblies available for the application.
/// </summary>
/// <returns>An <see cref="ICollection{Assembly}"/> of assemblies.</returns>
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This is better handled as a method.")]
ICollection<Assembly> GetAssemblies();
}
}

View File

@ -0,0 +1,15 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Net.Http;
using System.Web.Http.Controllers;
namespace System.Web.Http.Dispatcher
{
/// <summary>
/// Defines the methods that are required for an <see cref="IHttpControllerActivator"/>.
/// </summary>
public interface IHttpControllerActivator
{
IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType);
}
}

View File

@ -0,0 +1,30 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Net.Http;
using System.Web.Http.Controllers;
namespace System.Web.Http.Dispatcher
{
/// <summary>
/// Defines the methods that are required for an <see cref="IHttpController"/> factory.
/// </summary>
public interface IHttpControllerSelector
{
/// <summary>
/// Selects a <see cref="HttpControllerDescriptor"/> for the given <see cref="HttpRequestMessage"/>.
/// </summary>
/// <param name="request">The request message.</param>
/// <returns>An <see cref="HttpControllerDescriptor"/> instance.</returns>
HttpControllerDescriptor SelectController(HttpRequestMessage request);
/// <summary>
/// Returns a map, keyed by controller string, of all <see cref="HttpControllerDescriptor"/> that the selector can select.
/// This is primarily called by <see cref="System.Web.Http.Description.IApiExplorer"/> to discover all the possible controllers in the system.
/// </summary>
/// <returns>A map of all <see cref="HttpControllerDescriptor"/> that the selector can select, or null if the selector does not have a well-defined mapping of <see cref="HttpControllerDescriptor"/>.</returns>
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This is better handled as a method.")]
IDictionary<string, HttpControllerDescriptor> GetControllerMapping();
}
}

View File

@ -0,0 +1,21 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
namespace System.Web.Http.Dispatcher
{
/// <summary>
/// Provides an abstraction for managing the controller types of an application. A different
/// implementation can be registered via the <see cref="T:System.Web.Http.Services.DependencyResolver"/>.
/// </summary>
public interface IHttpControllerTypeResolver
{
/// <summary>
/// Returns a list of controllers available for the application.
/// </summary>
/// <returns>An <see cref="ICollection{Type}"/> of controllers.</returns>
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This is better handled as a method.")]
ICollection<Type> GetControllerTypes(IAssembliesResolver assembliesResolver);
}
}