Remove IScopedInstance - use AsyncLocal for ActionContext and

ActionBindingContext

This change replaces IScopedInstance<T> in favor or IActionContextAccessor
and IActionBindingContextAccessor. In the spirit of IHttpContextAccessor,
these are both singletons which use AsyncLocal for storage.

This change allows the invoker factory to be cached which results in some
significant perf gains.
This commit is contained in:
Ryan Nowak 2015-07-23 17:30:46 -07:00
parent ecfbdf2ae1
commit 89a8d0e36c
40 changed files with 311 additions and 371 deletions

View File

@ -0,0 +1,40 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#if DNX451
using System.Runtime.Remoting;
using System.Runtime.Remoting.Messaging;
#else
using System.Threading;
#endif
namespace Microsoft.AspNet.Mvc
{
public class ActionBindingContextAccessor : IActionBindingContextAccessor
{
#if DNX451
private static string Key = typeof(ActionBindingContext).FullName;
public ActionBindingContext ActionBindingContext
{
get
{
var handle = CallContext.LogicalGetData(Key) as ObjectHandle;
return handle != null ? (ActionBindingContext)handle.Unwrap() : null;
}
set
{
CallContext.LogicalSetData(Key, new ObjectHandle(value));
}
}
#else
private readonly AsyncLocal<ActionBindingContext> _storage = new AsyncLocal<ActionBindingContext>();
public ActionBindingContext ActionBindingContext
{
get { return _storage.Value; }
set { _storage.Value = value; }
}
#endif
}
}

View File

@ -0,0 +1,40 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#if DNX451
using System.Runtime.Remoting;
using System.Runtime.Remoting.Messaging;
#else
using System.Threading;
#endif
namespace Microsoft.AspNet.Mvc
{
public class ActionContextAccessor : IActionContextAccessor
{
#if DNX451
private static string Key = typeof(ActionContext).FullName;
public ActionContext ActionContext
{
get
{
var handle = CallContext.LogicalGetData(Key) as ObjectHandle;
return handle != null ? (ActionContext)handle.Unwrap() : null;
}
set
{
CallContext.LogicalSetData(Key, new ObjectHandle(value));
}
}
#else
private readonly AsyncLocal<ActionContext> _storage = new AsyncLocal<ActionContext>();
public ActionContext ActionContext
{
get { return _storage.Value; }
set { _storage.Value = value; }
}
#endif
}
}

View File

@ -30,8 +30,8 @@ namespace Microsoft.AspNet.Mvc.Core
[NotNull] IReadOnlyList<IModelBinder> modelBinders, [NotNull] IReadOnlyList<IModelBinder> modelBinders,
[NotNull] IReadOnlyList<IModelValidatorProvider> modelValidatorProviders, [NotNull] IReadOnlyList<IModelValidatorProvider> modelValidatorProviders,
[NotNull] IReadOnlyList<IValueProviderFactory> valueProviderFactories, [NotNull] IReadOnlyList<IValueProviderFactory> valueProviderFactories,
[NotNull] IScopedInstance<ActionBindingContext> actionBindingContextAccessor, [NotNull] IActionBindingContextAccessor actionBindingContextAccessor,
[NotNull] ILoggerFactory loggerFactory, [NotNull] ILogger logger,
int maxModelValidationErrors) int maxModelValidationErrors)
: base( : base(
actionContext, actionContext,
@ -42,7 +42,7 @@ namespace Microsoft.AspNet.Mvc.Core
modelValidatorProviders, modelValidatorProviders,
valueProviderFactories, valueProviderFactories,
actionBindingContextAccessor, actionBindingContextAccessor,
loggerFactory, logger,
maxModelValidationErrors) maxModelValidationErrors)
{ {
_descriptor = descriptor; _descriptor = descriptor;

View File

@ -21,16 +21,16 @@ namespace Microsoft.AspNet.Mvc.Core
private readonly IReadOnlyList<IOutputFormatter> _outputFormatters; private readonly IReadOnlyList<IOutputFormatter> _outputFormatters;
private readonly IReadOnlyList<IModelValidatorProvider> _modelValidatorProviders; private readonly IReadOnlyList<IModelValidatorProvider> _modelValidatorProviders;
private readonly IReadOnlyList<IValueProviderFactory> _valueProviderFactories; private readonly IReadOnlyList<IValueProviderFactory> _valueProviderFactories;
private readonly IScopedInstance<ActionBindingContext> _actionBindingContextAccessor; private readonly IActionBindingContextAccessor _actionBindingContextAccessor;
private readonly int _maxModelValidationErrors; private readonly int _maxModelValidationErrors;
private readonly ILoggerFactory _loggerFactory; private readonly ILogger _logger;
public ControllerActionInvokerProvider( public ControllerActionInvokerProvider(
IControllerFactory controllerFactory, IControllerFactory controllerFactory,
IEnumerable<IFilterProvider> filterProviders, IEnumerable<IFilterProvider> filterProviders,
IControllerActionArgumentBinder argumentBinder, IControllerActionArgumentBinder argumentBinder,
IOptions<MvcOptions> optionsAccessor, IOptions<MvcOptions> optionsAccessor,
IScopedInstance<ActionBindingContext> actionBindingContextAccessor, IActionBindingContextAccessor actionBindingContextAccessor,
ILoggerFactory loggerFactory) ILoggerFactory loggerFactory)
{ {
_controllerFactory = controllerFactory; _controllerFactory = controllerFactory;
@ -43,7 +43,8 @@ namespace Microsoft.AspNet.Mvc.Core
_valueProviderFactories = optionsAccessor.Options.ValueProviderFactories.ToArray(); _valueProviderFactories = optionsAccessor.Options.ValueProviderFactories.ToArray();
_actionBindingContextAccessor = actionBindingContextAccessor; _actionBindingContextAccessor = actionBindingContextAccessor;
_maxModelValidationErrors = optionsAccessor.Options.MaxModelValidationErrors; _maxModelValidationErrors = optionsAccessor.Options.MaxModelValidationErrors;
_loggerFactory = loggerFactory;
_logger = loggerFactory.CreateLogger<ControllerActionInvoker>();
} }
public int Order public int Order
@ -70,7 +71,7 @@ namespace Microsoft.AspNet.Mvc.Core
_modelValidatorProviders, _modelValidatorProviders,
_valueProviderFactories, _valueProviderFactories,
_actionBindingContextAccessor, _actionBindingContextAccessor,
_loggerFactory, _logger,
_maxModelValidationErrors); _maxModelValidationErrors);
} }
} }

View File

@ -12,12 +12,15 @@ namespace Microsoft.AspNet.Mvc
{ {
public class DefaultControllerPropertyActivator : IControllerPropertyActivator public class DefaultControllerPropertyActivator : IControllerPropertyActivator
{ {
private readonly ConcurrentDictionary<Type, PropertyActivator<ActionContext>[]> _activateActions; private readonly IActionBindingContextAccessor _actionBindingContextAccessor;
private readonly Func<Type, PropertyActivator<ActionContext>[]> _getPropertiesToActivate; private readonly ConcurrentDictionary<Type, PropertyActivator<Contexts>[]> _activateActions;
private readonly Func<Type, PropertyActivator<Contexts>[]> _getPropertiesToActivate;
public DefaultControllerPropertyActivator() public DefaultControllerPropertyActivator(IActionBindingContextAccessor actionBindingContextAccessor)
{ {
_activateActions = new ConcurrentDictionary<Type, PropertyActivator<ActionContext>[]>(); _actionBindingContextAccessor = actionBindingContextAccessor;
_activateActions = new ConcurrentDictionary<Type, PropertyActivator<Contexts>[]>();
_getPropertiesToActivate = GetPropertiesToActivate; _getPropertiesToActivate = GetPropertiesToActivate;
} }
@ -28,34 +31,39 @@ namespace Microsoft.AspNet.Mvc
controllerType, controllerType,
_getPropertiesToActivate); _getPropertiesToActivate);
var contexts = new Contexts()
{
ActionBindingContext = _actionBindingContextAccessor.ActionBindingContext,
ActionContext = actionContext,
};
for (var i = 0; i < propertiesToActivate.Length; i++) for (var i = 0; i < propertiesToActivate.Length; i++)
{ {
var activateInfo = propertiesToActivate[i]; var activateInfo = propertiesToActivate[i];
activateInfo.Activate(controller, actionContext); activateInfo.Activate(controller, contexts);
} }
} }
private PropertyActivator<ActionContext>[] GetPropertiesToActivate(Type type) private PropertyActivator<Contexts>[] GetPropertiesToActivate(Type type)
{ {
IEnumerable<PropertyActivator<ActionContext>> activators; IEnumerable<PropertyActivator<Contexts>> activators;
activators = PropertyActivator<ActionContext>.GetPropertiesToActivate( activators = PropertyActivator<Contexts>.GetPropertiesToActivate(
type, type,
typeof(ActionContextAttribute), typeof(ActionContextAttribute),
p => new PropertyActivator<ActionContext>(p, c => c)); p => new PropertyActivator<Contexts>(p, c => c.ActionContext));
activators = activators.Concat(PropertyActivator<ActionContext>.GetPropertiesToActivate( activators = activators.Concat(PropertyActivator<Contexts>.GetPropertiesToActivate(
type, type,
typeof(ActionBindingContextAttribute), typeof(ActionBindingContextAttribute),
p => new PropertyActivator<ActionContext>(p, GetActionBindingContext))); p => new PropertyActivator<Contexts>(p, c => c.ActionBindingContext)));
return activators.ToArray(); return activators.ToArray();
} }
private static ActionBindingContext GetActionBindingContext(ActionContext context) private struct Contexts
{ {
var serviceProvider = context.HttpContext.RequestServices; public ActionBindingContext ActionBindingContext;
var accessor = serviceProvider.GetRequiredService<IScopedInstance<ActionBindingContext>>(); public ActionContext ActionContext;
return accessor.Value;
} }
} }
} }

View File

@ -104,8 +104,8 @@ namespace Microsoft.Framework.DependencyInjection
// //
// Action Invoker // Action Invoker
// //
// These two access per-request services // The IActionInvokerFactory is cachable
services.TryAddTransient<IActionInvokerFactory, ActionInvokerFactory>(); services.TryAddSingleton<IActionInvokerFactory, ActionInvokerFactory>();
services.TryAddEnumerable( services.TryAddEnumerable(
ServiceDescriptor.Transient<IActionInvokerProvider, ControllerActionInvokerProvider>()); ServiceDescriptor.Transient<IActionInvokerProvider, ControllerActionInvokerProvider>());
@ -136,8 +136,9 @@ namespace Microsoft.Framework.DependencyInjection
// //
services.TryAddSingleton<MvcMarkerService, MvcMarkerService>(); services.TryAddSingleton<MvcMarkerService, MvcMarkerService>();
services.TryAddSingleton<ITypeActivatorCache, DefaultTypeActivatorCache>(); services.TryAddSingleton<ITypeActivatorCache, DefaultTypeActivatorCache>();
services.TryAddScoped(typeof(IScopedInstance<>), typeof(ScopedInstance<>)); services.TryAddSingleton<IActionContextAccessor, ActionContextAccessor>();
services.TryAddScoped<IUrlHelper, UrlHelper>(); services.TryAddSingleton<IActionBindingContextAccessor, ActionBindingContextAccessor>();
services.TryAddSingleton<IUrlHelper, UrlHelper>();
} }
private static void ConfigureDefaultServices(IServiceCollection services) private static void ConfigureDefaultServices(IServiceCollection services)

View File

@ -22,7 +22,7 @@ namespace Microsoft.AspNet.Mvc.Core
private readonly IReadOnlyList<IOutputFormatter> _outputFormatters; private readonly IReadOnlyList<IOutputFormatter> _outputFormatters;
private readonly IReadOnlyList<IModelValidatorProvider> _modelValidatorProviders; private readonly IReadOnlyList<IModelValidatorProvider> _modelValidatorProviders;
private readonly IReadOnlyList<IValueProviderFactory> _valueProviderFactories; private readonly IReadOnlyList<IValueProviderFactory> _valueProviderFactories;
private readonly IScopedInstance<ActionBindingContext> _actionBindingContextAccessor; private readonly IActionBindingContextAccessor _actionBindingContextAccessor;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly int _maxModelValidationErrors; private readonly int _maxModelValidationErrors;
@ -61,8 +61,8 @@ namespace Microsoft.AspNet.Mvc.Core
[NotNull] IReadOnlyList<IModelBinder> modelBinders, [NotNull] IReadOnlyList<IModelBinder> modelBinders,
[NotNull] IReadOnlyList<IModelValidatorProvider> modelValidatorProviders, [NotNull] IReadOnlyList<IModelValidatorProvider> modelValidatorProviders,
[NotNull] IReadOnlyList<IValueProviderFactory> valueProviderFactories, [NotNull] IReadOnlyList<IValueProviderFactory> valueProviderFactories,
[NotNull] IScopedInstance<ActionBindingContext> actionBindingContextAccessor, [NotNull] IActionBindingContextAccessor actionBindingContextAccessor,
[NotNull] ILoggerFactory loggerFactory, [NotNull] ILogger logger,
int maxModelValidationErrors) int maxModelValidationErrors)
{ {
ActionContext = actionContext; ActionContext = actionContext;
@ -74,7 +74,7 @@ namespace Microsoft.AspNet.Mvc.Core
_modelValidatorProviders = modelValidatorProviders; _modelValidatorProviders = modelValidatorProviders;
_valueProviderFactories = valueProviderFactories; _valueProviderFactories = valueProviderFactories;
_actionBindingContextAccessor = actionBindingContextAccessor; _actionBindingContextAccessor = actionBindingContextAccessor;
_logger = loggerFactory.CreateLogger<FilterActionInvoker>(); _logger = logger;
_maxModelValidationErrors = maxModelValidationErrors; _maxModelValidationErrors = maxModelValidationErrors;
} }
@ -84,11 +84,11 @@ namespace Microsoft.AspNet.Mvc.Core
{ {
get get
{ {
return _actionBindingContextAccessor.Value; return _actionBindingContextAccessor.ActionBindingContext;
} }
private set private set
{ {
_actionBindingContextAccessor.Value = value; _actionBindingContextAccessor.ActionBindingContext = value;
} }
} }
@ -316,7 +316,22 @@ namespace Microsoft.AspNet.Mvc.Core
} }
else else
{ {
// We've reached the end of resource filters, so move on to exception filters. // We've reached the end of resource filters, so move to setting up state to invoke model
// binding.
ActionBindingContext = new ActionBindingContext();
ActionBindingContext.InputFormatters = _resourceExecutingContext.InputFormatters;
ActionBindingContext.OutputFormatters = _resourceExecutingContext.OutputFormatters;
ActionBindingContext.ModelBinder = new CompositeModelBinder(_resourceExecutingContext.ModelBinders);
ActionBindingContext.ValidatorProvider = new CompositeModelValidatorProvider(
_resourceExecutingContext.ValidatorProviders);
var valueProviderFactoryContext = new ValueProviderFactoryContext(
ActionContext.HttpContext,
ActionContext.RouteData.Values);
ActionBindingContext.ValueProvider = CompositeValueProvider.Create(
_resourceExecutingContext.ValueProviderFactories,
valueProviderFactoryContext);
// >> ExceptionFilters >> Model Binding >> ActionFilters >> Action // >> ExceptionFilters >> Model Binding >> ActionFilters >> Action
await InvokeAllExceptionFiltersAsync(); await InvokeAllExceptionFiltersAsync();
@ -465,23 +480,6 @@ namespace Microsoft.AspNet.Mvc.Core
{ {
_cursor.SetStage(FilterStage.ActionFilters); _cursor.SetStage(FilterStage.ActionFilters);
Debug.Assert(_resourceExecutingContext != null);
ActionBindingContext = new ActionBindingContext();
ActionBindingContext.InputFormatters = _resourceExecutingContext.InputFormatters;
ActionBindingContext.OutputFormatters = _resourceExecutingContext.OutputFormatters;
ActionBindingContext.ModelBinder = new CompositeModelBinder(_resourceExecutingContext.ModelBinders);
ActionBindingContext.ValidatorProvider = new CompositeModelValidatorProvider(
_resourceExecutingContext.ValidatorProviders);
var valueProviderFactoryContext = new ValueProviderFactoryContext(
ActionContext.HttpContext,
ActionContext.RouteData.Values);
ActionBindingContext.ValueProvider = CompositeValueProvider.Create(
_resourceExecutingContext.ValueProviderFactories,
valueProviderFactoryContext);
Instance = CreateInstance(); Instance = CreateInstance();
var arguments = await BindActionArgumentsAsync(ActionContext, ActionBindingContext); var arguments = await BindActionArgumentsAsync(ActionContext, ActionBindingContext);

View File

@ -21,11 +21,11 @@ namespace Microsoft.AspNet.Mvc
/// Initializes an instance of <see cref="FormatFilter"/>. /// Initializes an instance of <see cref="FormatFilter"/>.
/// </summary> /// </summary>
/// <param name="options">The <see cref="IOptions{MvcOptions}"/></param> /// <param name="options">The <see cref="IOptions{MvcOptions}"/></param>
/// <param name="actionContext">The <see cref="IScopedInstance{ActionContext}"/></param> /// <param name="actionContextAccessor">The <see cref="IActionContextAccessor"/></param>
public FormatFilter(IOptions<MvcOptions> options, IScopedInstance<ActionContext> actionContext) public FormatFilter(IOptions<MvcOptions> options, IActionContextAccessor actionContextAccessor)
{ {
IsActive = true; IsActive = true;
Format = GetFormat(actionContext.Value); Format = GetFormat(actionContextAccessor.ActionContext);
if (string.IsNullOrEmpty(Format)) if (string.IsNullOrEmpty(Format))
{ {

View File

@ -0,0 +1,10 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNet.Mvc
{
public interface IActionBindingContextAccessor
{
ActionBindingContext ActionBindingContext { get; set; }
}
}

View File

@ -0,0 +1,10 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNet.Mvc
{
public interface IActionContextAccessor
{
ActionContext ActionContext { get; set; }
}
}

View File

@ -1,12 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace Microsoft.AspNet.Mvc
{
public interface IScopedInstance<TValue> : IDisposable
{
TValue Value { get; set; }
}
}

View File

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Diagnostics;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Http; using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.Core; using Microsoft.AspNet.Mvc.Core;
@ -17,15 +16,19 @@ namespace Microsoft.AspNet.Mvc
{ {
public class MvcRouteHandler : IRouter public class MvcRouteHandler : IRouter
{ {
private INotifier _notifier; private IActionContextAccessor _actionContextAccessor;
private IActionInvokerFactory _actionInvokerFactory;
private IActionSelector _actionSelector;
private ILogger _logger; private ILogger _logger;
private INotifier _notifier;
public VirtualPathData GetVirtualPath([NotNull] VirtualPathContext context) public VirtualPathData GetVirtualPath([NotNull] VirtualPathContext context)
{ {
EnsureServices(context.Context);
// The contract of this method is to check that the values coming in from the route are valid; // The contract of this method is to check that the values coming in from the route are valid;
// that they match an existing action, setting IsBound = true if the values are OK. // that they match an existing action, setting IsBound = true if the values are OK.
var actionSelector = context.Context.RequestServices.GetRequiredService<IActionSelector>(); context.IsBound = _actionSelector.HasValidAction(context);
context.IsBound = actionSelector.HasValidAction(context);
// We return null here because we're not responsible for generating the url, the route is. // We return null here because we're not responsible for generating the url, the route is.
return null; return null;
@ -38,13 +41,9 @@ namespace Microsoft.AspNet.Mvc
// Verify if AddMvc was done before calling UseMvc // Verify if AddMvc was done before calling UseMvc
// We use the MvcMarkerService to make sure if all the services were added. // We use the MvcMarkerService to make sure if all the services were added.
MvcServicesHelper.ThrowIfMvcNotRegistered(services); MvcServicesHelper.ThrowIfMvcNotRegistered(services);
EnsureServices(context.HttpContext);
EnsureLogger(context.HttpContext); var actionDescriptor = await _actionSelector.SelectAsync(context);
EnsureNotifier(context.HttpContext);
var actionSelector = services.GetRequiredService<IActionSelector>();
var actionDescriptor = await actionSelector.SelectAsync(context);
if (actionDescriptor == null) if (actionDescriptor == null)
{ {
_logger.LogVerbose("No actions matched the current request."); _logger.LogVerbose("No actions matched the current request.");
@ -104,15 +103,10 @@ namespace Microsoft.AspNet.Mvc
private async Task InvokeActionAsync(RouteContext context, ActionDescriptor actionDescriptor) private async Task InvokeActionAsync(RouteContext context, ActionDescriptor actionDescriptor)
{ {
var services = context.HttpContext.RequestServices;
Debug.Assert(services != null);
var actionContext = new ActionContext(context.HttpContext, context.RouteData, actionDescriptor); var actionContext = new ActionContext(context.HttpContext, context.RouteData, actionDescriptor);
_actionContextAccessor.ActionContext = actionContext;
var contextAccessor = services.GetRequiredService<IScopedInstance<ActionContext>>();
contextAccessor.Value = actionContext; var invoker = _actionInvokerFactory.CreateInvoker(actionContext);
var invokerFactory = services.GetRequiredService<IActionInvokerFactory>();
var invoker = invokerFactory.CreateInvoker(actionContext);
if (invoker == null) if (invoker == null)
{ {
throw new InvalidOperationException( throw new InvalidOperationException(
@ -123,17 +117,29 @@ namespace Microsoft.AspNet.Mvc
await invoker.InvokeAsync(); await invoker.InvokeAsync();
} }
private void EnsureLogger(HttpContext context) private void EnsureServices(HttpContext context)
{ {
if (_actionContextAccessor == null)
{
_actionContextAccessor = context.RequestServices.GetRequiredService<IActionContextAccessor>();
}
if (_actionInvokerFactory == null)
{
_actionInvokerFactory = context.RequestServices.GetRequiredService<IActionInvokerFactory>();
}
if (_actionSelector == null)
{
_actionSelector = context.RequestServices.GetRequiredService<IActionSelector>();
}
if (_logger == null) if (_logger == null)
{ {
var factory = context.RequestServices.GetRequiredService<ILoggerFactory>(); var factory = context.RequestServices.GetRequiredService<ILoggerFactory>();
_logger = factory.CreateLogger<MvcRouteHandler>(); _logger = factory.CreateLogger<MvcRouteHandler>();
} }
}
private void EnsureNotifier(HttpContext context)
{
if (_notifier == null) if (_notifier == null)
{ {
_notifier = context.RequestServices.GetRequiredService<INotifier>(); _notifier = context.RequestServices.GetRequiredService<INotifier>();

View File

@ -297,8 +297,8 @@ namespace Microsoft.AspNet.Mvc
var actionBindingContext = context var actionBindingContext = context
.HttpContext .HttpContext
.RequestServices .RequestServices
.GetRequiredService<IScopedInstance<ActionBindingContext>>() .GetRequiredService<IActionBindingContextAccessor>()
.Value; .ActionBindingContext;
// In scenarios where there is a resource filter which directly shortcircuits using an ObjectResult. // In scenarios where there is a resource filter which directly shortcircuits using an ObjectResult.
// actionBindingContext is not setup yet and is null. // actionBindingContext is not setup yet and is null.

View File

@ -1,21 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace Microsoft.AspNet.Mvc
{
public class ScopedInstance<T> : IScopedInstance<T>
{
public T Value { get; set; }
public void Dispose()
{
var disposable = Value as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
}
}
}

View File

@ -7,7 +7,6 @@ using System.Diagnostics;
using Microsoft.AspNet.Http; using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.Internal; using Microsoft.AspNet.Mvc.Internal;
using Microsoft.AspNet.Routing; using Microsoft.AspNet.Routing;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Internal; using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc namespace Microsoft.AspNet.Mvc
@ -18,28 +17,32 @@ namespace Microsoft.AspNet.Mvc
/// </summary> /// </summary>
public class UrlHelper : IUrlHelper public class UrlHelper : IUrlHelper
{ {
private readonly HttpContext _httpContext; private readonly IActionContextAccessor _actionContextAccessor;
private readonly IRouter _router;
private readonly IDictionary<string, object> _ambientValues;
private readonly IActionSelector _actionSelector; private readonly IActionSelector _actionSelector;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="UrlHelper"/> class using the specified action context and /// Initializes a new instance of the <see cref="UrlHelper"/> class using the specified action context and
/// action selector. /// action selector.
/// </summary> /// </summary>
/// <param name="contextAccessor">The <see cref="IScopedInstance{TContext}"/> to access the action context /// <param name="actionContextAccessor">The <see cref="IActionContextAccessor"/> to access the action context
/// of the current request.</param> /// of the current request.</param>
/// <param name="actionSelector">The <see cref="IActionSelector"/> to be used for verifying the correctness of /// <param name="actionSelector">The <see cref="IActionSelector"/> to be used for verifying the correctness of
/// supplied parameters for a route. /// supplied parameters for a route.
/// </param> /// </param>
public UrlHelper(IScopedInstance<ActionContext> contextAccessor, IActionSelector actionSelector) public UrlHelper(IActionContextAccessor actionContextAccessor, IActionSelector actionSelector)
{ {
_httpContext = contextAccessor.Value.HttpContext; _actionContextAccessor = actionContextAccessor;
_router = contextAccessor.Value.RouteData.Routers[0];
_ambientValues = contextAccessor.Value.RouteData.Values;
_actionSelector = actionSelector; _actionSelector = actionSelector;
} }
protected IDictionary<string, object> AmbientValues => ActionContext.RouteData.Values;
protected ActionContext ActionContext => _actionContextAccessor.ActionContext;
protected HttpContext HttpContext => ActionContext.HttpContext;
protected IRouter Router => ActionContext.RouteData.Routers[0];
/// <inheritdoc /> /// <inheritdoc />
public virtual string Action(UrlActionContext actionContext) public virtual string Action(UrlActionContext actionContext)
{ {
@ -98,8 +101,8 @@ namespace Microsoft.AspNet.Mvc
/// <returns>The absolute path of the URL.</returns> /// <returns>The absolute path of the URL.</returns>
protected virtual string GeneratePathFromRoute(string routeName, IDictionary<string, object> values) protected virtual string GeneratePathFromRoute(string routeName, IDictionary<string, object> values)
{ {
var context = new VirtualPathContext(_httpContext, _ambientValues, values, routeName); var context = new VirtualPathContext(HttpContext, AmbientValues, values, routeName);
var pathData = _router.GetVirtualPath(context); var pathData = Router.GetVirtualPath(context);
if (pathData == null) if (pathData == null)
{ {
return null; return null;
@ -108,7 +111,7 @@ namespace Microsoft.AspNet.Mvc
// VirtualPathData.VirtualPath returns string.Empty for null. // VirtualPathData.VirtualPath returns string.Empty for null.
Debug.Assert(pathData.VirtualPath != null); Debug.Assert(pathData.VirtualPath != null);
var fullPath = _httpContext.Request.PathBase.Add(pathData.VirtualPath).Value; var fullPath = HttpContext.Request.PathBase.Add(pathData.VirtualPath).Value;
if (fullPath.Length == 0) if (fullPath.Length == 0)
{ {
return "/"; return "/";
@ -129,7 +132,7 @@ namespace Microsoft.AspNet.Mvc
else if (contentPath[0] == '~') else if (contentPath[0] == '~')
{ {
var segment = new PathString(contentPath.Substring(1)); var segment = new PathString(contentPath.Substring(1));
var applicationPath = _httpContext.Request.PathBase; var applicationPath = HttpContext.Request.PathBase;
return applicationPath.Add(segment).Value; return applicationPath.Add(segment).Value;
} }
@ -144,8 +147,8 @@ namespace Microsoft.AspNet.Mvc
{ {
RouteName = routeName, RouteName = routeName,
Values = values, Values = values,
Protocol = _httpContext.Request.Scheme, Protocol = HttpContext.Request.Scheme,
Host = _httpContext.Request.Host.ToUriComponent() Host = HttpContext.Request.Host.ToUriComponent()
}); });
} }
@ -174,7 +177,7 @@ namespace Microsoft.AspNet.Mvc
else else
{ {
protocol = string.IsNullOrEmpty(protocol) ? "http" : protocol; protocol = string.IsNullOrEmpty(protocol) ? "http" : protocol;
host = string.IsNullOrEmpty(host) ? _httpContext.Request.Host.Value : host; host = string.IsNullOrEmpty(host) ? HttpContext.Request.Host.Value : host;
url = protocol + "://" + host + url; url = protocol + "://" + host + url;
return url; return url;

View File

@ -2040,8 +2040,8 @@ namespace Microsoft.AspNet.Mvc
new IModelBinder[0], new IModelBinder[0],
new IModelValidatorProvider[0], new IModelValidatorProvider[0],
new IValueProviderFactory[0], new IValueProviderFactory[0],
new MockScopedInstance<ActionBindingContext>(), new ActionBindingContextAccessor(),
new NullLoggerFactory(), new NullLoggerFactory().CreateLogger<ControllerActionInvoker>(),
maxAllowedErrorsInModelState); maxAllowedErrorsInModelState);
return invoker; return invoker;
@ -2102,8 +2102,8 @@ namespace Microsoft.AspNet.Mvc
new IModelBinder[] { binder.Object }, new IModelBinder[] { binder.Object },
new IModelValidatorProvider[0], new IModelValidatorProvider[0],
new IValueProviderFactory[0], new IValueProviderFactory[0],
new MockScopedInstance<ActionBindingContext>(), new ActionBindingContextAccessor(),
new NullLoggerFactory(), new NullLoggerFactory().CreateLogger<ControllerActionInvoker>(),
200); 200);
// Act // Act
@ -2202,8 +2202,8 @@ namespace Microsoft.AspNet.Mvc
IReadOnlyList<IModelBinder> modelBinders, IReadOnlyList<IModelBinder> modelBinders,
IReadOnlyList<IModelValidatorProvider> modelValidatorProviders, IReadOnlyList<IModelValidatorProvider> modelValidatorProviders,
IReadOnlyList<IValueProviderFactory> valueProviderFactories, IReadOnlyList<IValueProviderFactory> valueProviderFactories,
IScopedInstance<ActionBindingContext> actionBindingContext, IActionBindingContextAccessor actionBindingContext,
ILoggerFactory loggerFactory, ILogger logger,
int maxAllowedErrorsInModelState) int maxAllowedErrorsInModelState)
: base( : base(
actionContext, actionContext,
@ -2217,7 +2217,7 @@ namespace Microsoft.AspNet.Mvc
modelValidatorProviders, modelValidatorProviders,
valueProviderFactories, valueProviderFactories,
actionBindingContext, actionBindingContext,
loggerFactory, logger,
maxAllowedErrorsInModelState) maxAllowedErrorsInModelState)
{ {
ControllerFactory = controllerFactory; ControllerFactory = controllerFactory;

View File

@ -102,13 +102,12 @@ namespace Microsoft.AspNet.Mvc
services.Setup(s => s.GetService(typeof(ILogger<ObjectResult>))) services.Setup(s => s.GetService(typeof(ILogger<ObjectResult>)))
.Returns(new Mock<ILogger<ObjectResult>>().Object); .Returns(new Mock<ILogger<ObjectResult>>().Object);
var mockContextAccessor = new Mock<IScopedInstance<ActionBindingContext>>(); var actionBindingContext = new ActionBindingContext
mockContextAccessor {
.SetupGet(o => o.Value) OutputFormatters = optionsAccessor.Options.OutputFormatters
.Returns(new ActionBindingContext() { OutputFormatters = optionsAccessor.Options.OutputFormatters }); };
services.Setup(o => o.GetService(typeof(IActionBindingContextAccessor)))
services.Setup(o => o.GetService(typeof(IScopedInstance<ActionBindingContext>))) .Returns(new ActionBindingContextAccessor() { ActionBindingContext = actionBindingContext });
.Returns(mockContextAccessor.Object);
return httpContext; return httpContext;
} }

View File

@ -110,14 +110,8 @@ namespace Microsoft.AspNet.Mvc
httpContext.Setup(o => o.Response) httpContext.Setup(o => o.Response)
.Returns(response); .Returns(response);
httpContext.Setup(o => o.RequestServices.GetService(typeof(IActionBindingContextAccessor)))
var mockContextAccessor = new Mock<IScopedInstance<ActionBindingContext>>(); .Returns(new ActionBindingContextAccessor());
mockContextAccessor
.SetupGet(o => o.Value)
.Returns((ActionBindingContext)null);
httpContext.Setup(o => o.RequestServices.GetService(typeof(IScopedInstance<ActionBindingContext>)))
.Returns(mockContextAccessor.Object);
return httpContext.Object; return httpContext.Object;
} }

View File

@ -97,13 +97,13 @@ namespace Microsoft.AspNet.Mvc
.Setup(p => p.RequestServices.GetService(typeof(ILogger<ObjectResult>))) .Setup(p => p.RequestServices.GetService(typeof(ILogger<ObjectResult>)))
.Returns(new Mock<ILogger<ObjectResult>>().Object); .Returns(new Mock<ILogger<ObjectResult>>().Object);
var mockActionBindingContext = new Mock<IScopedInstance<ActionBindingContext>>(); var actionBindingContext = new ActionBindingContext()
mockActionBindingContext {
.SetupGet(o=> o.Value) OutputFormatters = optionsAccessor.Options.OutputFormatters
.Returns(new ActionBindingContext() { OutputFormatters = optionsAccessor.Options.OutputFormatters }); };
httpContext httpContext
.Setup(o => o.RequestServices.GetService(typeof(IScopedInstance<ActionBindingContext>))) .Setup(o => o.RequestServices.GetService(typeof(IActionBindingContextAccessor)))
.Returns(mockActionBindingContext.Object); .Returns(new ActionBindingContextAccessor() { ActionBindingContext = actionBindingContext });
return httpContext.Object; return httpContext.Object;
} }

View File

@ -102,7 +102,7 @@ namespace Microsoft.AspNet.Mvc.Core
var bindingContext = new ActionBindingContext(); var bindingContext = new ActionBindingContext();
var services = GetServices(); var services = GetServices();
services.GetRequiredService<IScopedInstance<ActionBindingContext>>().Value = bindingContext; services.GetRequiredService<IActionBindingContextAccessor>().ActionBindingContext = bindingContext;
var httpContext = new DefaultHttpContext var httpContext = new DefaultHttpContext
{ {
RequestServices = services RequestServices = services
@ -255,11 +255,10 @@ namespace Microsoft.AspNet.Mvc.Core
.Returns(metadataProvider); .Returns(metadataProvider);
services.Setup(s => s.GetService(typeof(IObjectModelValidator))) services.Setup(s => s.GetService(typeof(IObjectModelValidator)))
.Returns(new DefaultObjectValidator(new IExcludeTypeValidationFilter[0], metadataProvider)); .Returns(new DefaultObjectValidator(new IExcludeTypeValidationFilter[0], metadataProvider));
services
.Setup(s => s.GetService(typeof(IScopedInstance<ActionBindingContext>)))
.Returns(new MockScopedInstance<ActionBindingContext>());
services.Setup(s => s.GetService(typeof(ITempDataDictionary))) services.Setup(s => s.GetService(typeof(ITempDataDictionary)))
.Returns(new Mock<ITempDataDictionary>().Object); .Returns(new Mock<ITempDataDictionary>().Object);
services.Setup(s => s.GetService(typeof(IActionBindingContextAccessor)))
.Returns(new ActionBindingContextAccessor());
return services.Object; return services.Object;
} }
@ -268,7 +267,7 @@ namespace Microsoft.AspNet.Mvc.Core
controllerActivator = controllerActivator ?? Mock.Of<IControllerActivator>(); controllerActivator = controllerActivator ?? Mock.Of<IControllerActivator>();
var propertyActivators = new IControllerPropertyActivator[] var propertyActivators = new IControllerPropertyActivator[]
{ {
new DefaultControllerPropertyActivator(), new DefaultControllerPropertyActivator(new ActionBindingContextAccessor()),
}; };
return new DefaultControllerFactory(controllerActivator, propertyActivators); return new DefaultControllerFactory(controllerActivator, propertyActivators);

View File

@ -43,7 +43,7 @@ namespace Microsoft.AspNet.Mvc
var resultExecutingContext = mockObjects.CreateResultExecutingContext(); var resultExecutingContext = mockObjects.CreateResultExecutingContext();
var resourceExecutingContext = mockObjects.CreateResourceExecutingContext(new IFilterMetadata[] { }); var resourceExecutingContext = mockObjects.CreateResourceExecutingContext(new IFilterMetadata[] { });
var filter = new FormatFilter(mockObjects.OptionsManager, mockObjects.ScopedInstance); var filter = new FormatFilter(mockObjects.OptionsManager, mockObjects.ActionContextAccessor);
// Act // Act
filter.OnResourceExecuting(resourceExecutingContext); filter.OnResourceExecuting(resourceExecutingContext);
@ -90,7 +90,7 @@ namespace Microsoft.AspNet.Mvc
ac, ac,
new IFilterMetadata[] { }); new IFilterMetadata[] { });
var filter = new FormatFilter(mockObjects.OptionsManager, mockObjects.ScopedInstance); var filter = new FormatFilter(mockObjects.OptionsManager, mockObjects.ActionContextAccessor);
// Act // Act
filter.OnResourceExecuting(resourceExecutingContext); filter.OnResourceExecuting(resourceExecutingContext);
@ -122,7 +122,7 @@ namespace Microsoft.AspNet.Mvc
format, format,
MediaTypeHeaderValue.Parse(contentType)); MediaTypeHeaderValue.Parse(contentType));
var filter = new FormatFilter(mockObjects.OptionsManager, mockObjects.ScopedInstance); var filter = new FormatFilter(mockObjects.OptionsManager, mockObjects.ActionContextAccessor);
// Act // Act
filter.OnResourceExecuting(resourceExecutingContext); filter.OnResourceExecuting(resourceExecutingContext);
@ -145,7 +145,7 @@ namespace Microsoft.AspNet.Mvc
var mockObjects = new MockObjects(format, place); var mockObjects = new MockObjects(format, place);
var resourceExecutingContext = mockObjects.CreateResourceExecutingContext(new IFilterMetadata[] { }); var resourceExecutingContext = mockObjects.CreateResourceExecutingContext(new IFilterMetadata[] { });
var filter = new FormatFilter(mockObjects.OptionsManager, mockObjects.ScopedInstance); var filter = new FormatFilter(mockObjects.OptionsManager, mockObjects.ActionContextAccessor);
// Act // Act
filter.OnResourceExecuting(resourceExecutingContext); filter.OnResourceExecuting(resourceExecutingContext);
@ -162,7 +162,7 @@ namespace Microsoft.AspNet.Mvc
var mockObjects = new MockObjects(); var mockObjects = new MockObjects();
var resourceExecutingContext = mockObjects.CreateResourceExecutingContext(new IFilterMetadata[] { }); var resourceExecutingContext = mockObjects.CreateResourceExecutingContext(new IFilterMetadata[] { });
var filter = new FormatFilter(mockObjects.OptionsManager, mockObjects.ScopedInstance); var filter = new FormatFilter(mockObjects.OptionsManager, mockObjects.ActionContextAccessor);
// Act // Act
filter.OnResourceExecuting(resourceExecutingContext); filter.OnResourceExecuting(resourceExecutingContext);
@ -184,7 +184,7 @@ namespace Microsoft.AspNet.Mvc
var mockObjects = new MockObjects(format, place); var mockObjects = new MockObjects(format, place);
var resourceExecutingContext = mockObjects.CreateResourceExecutingContext(new IFilterMetadata[] { produces }); var resourceExecutingContext = mockObjects.CreateResourceExecutingContext(new IFilterMetadata[] { produces });
var filter = new FormatFilter(mockObjects.OptionsManager, mockObjects.ScopedInstance); var filter = new FormatFilter(mockObjects.OptionsManager, mockObjects.ActionContextAccessor);
// Act // Act
filter.OnResourceExecuting(resourceExecutingContext); filter.OnResourceExecuting(resourceExecutingContext);
@ -205,7 +205,7 @@ namespace Microsoft.AspNet.Mvc
"xml", "xml",
MediaTypeHeaderValue.Parse("application/xml")); MediaTypeHeaderValue.Parse("application/xml"));
var filter = new FormatFilter(mockObjects.OptionsManager, mockObjects.ScopedInstance); var filter = new FormatFilter(mockObjects.OptionsManager, mockObjects.ActionContextAccessor);
// Act // Act
filter.OnResourceExecuting(resourceExecutingContext); filter.OnResourceExecuting(resourceExecutingContext);
@ -226,7 +226,7 @@ namespace Microsoft.AspNet.Mvc
"xml", "xml",
MediaTypeHeaderValue.Parse("application/xml;version=1")); MediaTypeHeaderValue.Parse("application/xml;version=1"));
var filter = new FormatFilter(mockObjects.OptionsManager, mockObjects.ScopedInstance); var filter = new FormatFilter(mockObjects.OptionsManager, mockObjects.ActionContextAccessor);
// Act // Act
filter.OnResourceExecuting(resourceExecutingContext); filter.OnResourceExecuting(resourceExecutingContext);
@ -252,7 +252,7 @@ namespace Microsoft.AspNet.Mvc
"xml", "xml",
MediaTypeHeaderValue.Parse("application/xml")); MediaTypeHeaderValue.Parse("application/xml"));
var filter = new FormatFilter(mockObjects.OptionsManager, mockObjects.ScopedInstance); var filter = new FormatFilter(mockObjects.OptionsManager, mockObjects.ActionContextAccessor);
// Act // Act
filter.OnResourceExecuting(resourceExecutingContext); filter.OnResourceExecuting(resourceExecutingContext);
@ -271,7 +271,7 @@ namespace Microsoft.AspNet.Mvc
// Arrange // Arrange
var mockObjects = new MockObjects(format, place); var mockObjects = new MockObjects(format, place);
var resourceExecutingContext = mockObjects.CreateResourceExecutingContext(new IFilterMetadata[] { }); var resourceExecutingContext = mockObjects.CreateResourceExecutingContext(new IFilterMetadata[] { });
var filter = new FormatFilter(mockObjects.OptionsManager, mockObjects.ScopedInstance); var filter = new FormatFilter(mockObjects.OptionsManager, mockObjects.ActionContextAccessor);
// Act // Act
filter.OnResourceExecuting(resourceExecutingContext); filter.OnResourceExecuting(resourceExecutingContext);
@ -294,7 +294,7 @@ namespace Microsoft.AspNet.Mvc
var mockObjects = new MockObjects(format, place); var mockObjects = new MockObjects(format, place);
var resultExecutingContext = mockObjects.CreateResultExecutingContext(); var resultExecutingContext = mockObjects.CreateResultExecutingContext();
var filterAttribute = new FormatFilterAttribute(); var filterAttribute = new FormatFilterAttribute();
var filter = new FormatFilter(mockObjects.OptionsManager, mockObjects.ScopedInstance); var filter = new FormatFilter(mockObjects.OptionsManager, mockObjects.ActionContextAccessor);
// Act and Assert // Act and Assert
Assert.Equal(expected, filter.IsActive); Assert.Equal(expected, filter.IsActive);
@ -322,7 +322,7 @@ namespace Microsoft.AspNet.Mvc
public HttpContext MockHttpContext { get; private set; } public HttpContext MockHttpContext { get; private set; }
public ActionContext MockActionContext { get; private set; } public ActionContext MockActionContext { get; private set; }
public IScopedInstance<ActionContext> ScopedInstance { get; private set; } public IActionContextAccessor ActionContextAccessor { get; private set; }
public IOptions<MvcOptions> OptionsManager { get; private set; } public IOptions<MvcOptions> OptionsManager { get; private set; }
public MockObjects(string format = null, FormatSource? place = null) public MockObjects(string format = null, FormatSource? place = null)
@ -399,9 +399,10 @@ namespace Microsoft.AspNet.Mvc
// Setup MVC services on mock service provider // Setup MVC services on mock service provider
MockActionContext = CreateMockActionContext(httpContext, format, place); MockActionContext = CreateMockActionContext(httpContext, format, place);
var scopedInstance = new Mock<IScopedInstance<ActionContext>>(); ActionContextAccessor = new ActionContextAccessor()
scopedInstance.Setup(s => s.Value).Returns(MockActionContext); {
ScopedInstance = scopedInstance.Object; ActionContext = MockActionContext,
};
} }
} }
#endif #endif

View File

@ -91,16 +91,16 @@ namespace Microsoft.AspNet.Mvc
optionsAccessor.Options.OutputFormatters.Add(new StringOutputFormatter()); optionsAccessor.Options.OutputFormatters.Add(new StringOutputFormatter());
optionsAccessor.Options.OutputFormatters.Add(new JsonOutputFormatter()); optionsAccessor.Options.OutputFormatters.Add(new JsonOutputFormatter());
optionsAccessor.Options.RespectBrowserAcceptHeader = respectBrowserAcceptHeader; optionsAccessor.Options.RespectBrowserAcceptHeader = respectBrowserAcceptHeader;
var mockContextAccessor = new Mock<IScopedInstance<ActionBindingContext>>(); var actionBindingContextAccessor = new ActionBindingContextAccessor()
mockContextAccessor {
.SetupGet(o => o.Value) ActionBindingContext = new ActionBindingContext()
.Returns(new ActionBindingContext()
{ {
OutputFormatters = optionsAccessor.Options.OutputFormatters OutputFormatters = optionsAccessor.Options.OutputFormatters
}); }
};
httpContext.Setup(o => o.RequestServices.GetService(typeof(IScopedInstance<ActionBindingContext>))) httpContext.Setup(o => o.RequestServices.GetService(typeof(IActionBindingContextAccessor)))
.Returns(mockContextAccessor.Object); .Returns(actionBindingContextAccessor);
httpContext.Setup(o => o.RequestServices.GetService(typeof(IOptions<MvcOptions>))) httpContext.Setup(o => o.RequestServices.GetService(typeof(IOptions<MvcOptions>)))
.Returns(optionsAccessor); .Returns(optionsAccessor);
httpContext.Setup(o => o.RequestServices.GetService(typeof(ILogger<ObjectResult>))) httpContext.Setup(o => o.RequestServices.GetService(typeof(ILogger<ObjectResult>)))

View File

@ -81,11 +81,11 @@ namespace Microsoft.AspNet.Mvc
{ {
OutputFormatters = optionsAccessor.Options.OutputFormatters, OutputFormatters = optionsAccessor.Options.OutputFormatters,
}; };
var bindingContextAccessor = new MockScopedInstance<ActionBindingContext> var bindingContextAccessor = new ActionBindingContextAccessor
{ {
Value = bindingContext, ActionBindingContext = bindingContext,
}; };
services.Add(new ServiceDescriptor(typeof(IScopedInstance<ActionBindingContext>), bindingContextAccessor)); services.Add(new ServiceDescriptor(typeof(IActionBindingContextAccessor), bindingContextAccessor));
return services.BuildServiceProvider(); return services.BuildServiceProvider();
} }

View File

@ -1,16 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.Framework.DependencyInjection;
namespace Microsoft.AspNet.Mvc
{
public class MockScopedInstance<T> : IScopedInstance<T>
{
public T Value { get; set; }
public void Dispose()
{
}
}
}

View File

@ -290,25 +290,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return bindingContext; return bindingContext;
} }
private static IScopedInstance<ActionContext> CreateActionContext(HttpContext context)
{
return CreateActionContext(context, (new Mock<IRouter>()).Object);
}
private static IScopedInstance<ActionContext> CreateActionContext(HttpContext context, IRouter router)
{
var routeData = new RouteData();
routeData.Routers.Add(router);
var actionContext = new ActionContext(context,
routeData,
new ActionDescriptor());
var contextAccessor = new Mock<IScopedInstance<ActionContext>>();
contextAccessor.SetupGet(c => c.Value)
.Returns(actionContext);
return contextAccessor.Object;
}
private class Person private class Person
{ {
public string Name { get; set; } public string Name { get; set; }

View File

@ -205,8 +205,6 @@ namespace Microsoft.AspNet.Mvc
IOptions<MvcOptions> optionsAccessor = null, IOptions<MvcOptions> optionsAccessor = null,
object notificationListener = null) object notificationListener = null)
{ {
var mockContextAccessor = new Mock<IScopedInstance<ActionContext>>();
if (actionDescriptor == null) if (actionDescriptor == null)
{ {
var mockAction = new Mock<ActionDescriptor>(); var mockAction = new Mock<ActionDescriptor>();
@ -252,8 +250,8 @@ namespace Microsoft.AspNet.Mvc
} }
var httpContext = new Mock<HttpContext>(); var httpContext = new Mock<HttpContext>();
httpContext.Setup(h => h.RequestServices.GetService(typeof(IScopedInstance<ActionContext>))) httpContext.Setup(h => h.RequestServices.GetService(typeof(IActionContextAccessor)))
.Returns(mockContextAccessor.Object); .Returns(new ActionContextAccessor());
httpContext.Setup(h => h.RequestServices.GetService(typeof(IActionSelector))) httpContext.Setup(h => h.RequestServices.GetService(typeof(IActionSelector)))
.Returns(actionSelector); .Returns(actionSelector);
httpContext.Setup(h => h.RequestServices.GetService(typeof(IActionInvokerFactory))) httpContext.Setup(h => h.RequestServices.GetService(typeof(IActionInvokerFactory)))

View File

@ -904,20 +904,14 @@ namespace Microsoft.AspNet.Mvc.Core.Test.ActionResults
httpContext.Setup(o => o.RequestServices.GetService(typeof(ILogger<ObjectResult>))) httpContext.Setup(o => o.RequestServices.GetService(typeof(ILogger<ObjectResult>)))
.Returns(new Mock<ILogger<ObjectResult>>().Object); .Returns(new Mock<ILogger<ObjectResult>>().Object);
var mockActionBindingContext = new Mock<IScopedInstance<ActionBindingContext>>(); ActionBindingContext actionBindingContext = null;
ActionBindingContext bindingContext = null;
if (setupActionBindingContext) if (setupActionBindingContext)
{ {
bindingContext = new ActionBindingContext { OutputFormatters = outputFormatters.ToList() }; actionBindingContext = new ActionBindingContext { OutputFormatters = outputFormatters.ToList() };
} }
mockActionBindingContext httpContext.Setup(o => o.RequestServices.GetService(typeof(IActionBindingContextAccessor)))
.SetupGet(o => o.Value) .Returns(new ActionBindingContextAccessor() { ActionBindingContext = actionBindingContext });
.Returns(bindingContext);
httpContext.Setup(o => o.RequestServices.GetService(typeof(IScopedInstance<ActionBindingContext>)))
.Returns(mockActionBindingContext.Object);
return new ActionContext(httpContext.Object, new RouteData(), new ActionDescriptor()); return new ActionContext(httpContext.Object, new RouteData(), new ActionDescriptor());
} }

View File

@ -115,10 +115,9 @@ namespace Microsoft.AspNet.Mvc.Core.Test
{ {
var httpContext = new Mock<HttpContext>(); var httpContext = new Mock<HttpContext>();
var actionContext = GetActionContext(httpContext.Object); var actionContext = GetActionContext(httpContext.Object);
var mockContentAccessor = new Mock<IScopedInstance<ActionContext>>(); var actionContextAccessor = new ActionContextAccessor() { ActionContext = actionContext };
mockContentAccessor.SetupGet(o => o.Value).Returns(actionContext);
var mockActionSelector = new Mock<IActionSelector>(); var mockActionSelector = new Mock<IActionSelector>();
var urlHelper = new UrlHelper(mockContentAccessor.Object, mockActionSelector.Object); var urlHelper = new UrlHelper(actionContextAccessor, mockActionSelector.Object);
var serviceProvider = GetServiceProvider(urlHelper); var serviceProvider = GetServiceProvider(urlHelper);
httpContext.Setup(o => o.Response) httpContext.Setup(o => o.Response)

View File

@ -1,68 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Xunit;
namespace Microsoft.AspNet.Mvc
{
public class ScopedInstanceTest
{
[Fact]
public void ScopedInstanceDisposesIDisposables()
{
var disposable = new Disposable();
// Arrange
var scopedInstance = new ScopedInstance<Disposable>
{
Value = disposable,
};
// Act
scopedInstance.Dispose();
// Assert
Assert.True(disposable.IsDisposed);
}
[Fact]
public void ScopedInstanceDoesNotThrowOnNonIDisposable()
{
// Arrange
var scopedInstance = new ScopedInstance<object>()
{
Value = new object(),
};
// Act
scopedInstance.Dispose();
}
[Fact]
public void ScopedInstanceDoesNotThrowOnNull()
{
// Arrange
var scopedInstance = new ScopedInstance<Disposable>()
{
Value = null, // just making it explicit that there is not value set yet.
};
// Act
scopedInstance.Dispose();
// Assert
Assert.Null(scopedInstance.Value);
}
private class Disposable : IDisposable
{
public bool IsDisposed { get; set; }
public void Dispose()
{
IsDisposed = true;
}
}
}
}

View File

@ -820,23 +820,18 @@ namespace Microsoft.AspNet.Mvc
return context; return context;
} }
private static IScopedInstance<ActionContext> CreateActionContext(HttpContext context) private static IActionContextAccessor CreateActionContext(HttpContext context)
{ {
return CreateActionContext(context, (new Mock<IRouter>()).Object); return CreateActionContext(context, (new Mock<IRouter>()).Object);
} }
private static IScopedInstance<ActionContext> CreateActionContext(HttpContext context, IRouter router) private static IActionContextAccessor CreateActionContext(HttpContext context, IRouter router)
{ {
var routeData = new RouteData(); var routeData = new RouteData();
routeData.Routers.Add(router); routeData.Routers.Add(router);
var actionContext = new ActionContext(context, var actionContext = new ActionContext(context, routeData, new ActionDescriptor());
routeData, return new ActionContextAccessor() { ActionContext = actionContext };
new ActionDescriptor());
var contextAccessor = new Mock<IScopedInstance<ActionContext>>();
contextAccessor.SetupGet(c => c.Value)
.Returns(actionContext);
return contextAccessor.Object;
} }
private static UrlHelper CreateUrlHelper() private static UrlHelper CreateUrlHelper()
@ -874,7 +869,7 @@ namespace Microsoft.AspNet.Mvc
return new UrlHelper(actionContext, actionSelector.Object); return new UrlHelper(actionContext, actionSelector.Object);
} }
private static UrlHelper CreateUrlHelper(IScopedInstance<ActionContext> contextAccessor) private static UrlHelper CreateUrlHelper(IActionContextAccessor contextAccessor)
{ {
var actionSelector = new Mock<IActionSelector>(MockBehavior.Strict); var actionSelector = new Mock<IActionSelector>(MockBehavior.Strict);
return new UrlHelper(contextAccessor, actionSelector.Object); return new UrlHelper(contextAccessor, actionSelector.Object);

View File

@ -35,7 +35,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
var httpContext = GetHttpContext(updateRequest, updateOptions); var httpContext = GetHttpContext(updateRequest, updateOptions);
var services = httpContext.RequestServices; var services = httpContext.RequestServices;
var actionBindingContext = services.GetRequiredService<IScopedInstance<ActionBindingContext>>().Value; var actionBindingContext = services.GetRequiredService<IActionBindingContextAccessor>().ActionBindingContext;
return new OperationBindingContext() return new OperationBindingContext()
{ {
@ -77,8 +77,8 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
var actionContext = new ActionContext(httpContext, new RouteData(), new ControllerActionDescriptor()); var actionContext = new ActionContext(httpContext, new RouteData(), new ControllerActionDescriptor());
var actionContextAccessor = var actionContextAccessor =
httpContext.RequestServices.GetRequiredService<IScopedInstance<ActionContext>>(); httpContext.RequestServices.GetRequiredService<IActionContextAccessor>();
actionContextAccessor.Value = actionContext; actionContextAccessor.ActionContext = actionContext;
var options = new TestMvcOptions().Options; var options = new TestMvcOptions().Options;
if (updateOptions != null) if (updateOptions != null)
@ -87,8 +87,8 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
} }
var actionBindingContextAccessor = var actionBindingContextAccessor =
httpContext.RequestServices.GetRequiredService<IScopedInstance<ActionBindingContext>>(); httpContext.RequestServices.GetRequiredService<IActionBindingContextAccessor>();
actionBindingContextAccessor.Value = GetActionBindingContext(options, actionContext); actionBindingContextAccessor.ActionBindingContext = GetActionBindingContext(options, actionContext);
} }
private static ActionBindingContext GetActionBindingContext(MvcOptions options, ActionContext actionContext) private static ActionBindingContext GetActionBindingContext(MvcOptions options, ActionContext actionContext)

View File

@ -272,7 +272,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
public string Name { get; set; } public string Name { get; set; }
[FromServices] [FromServices]
public IScopedInstance<ActionBindingContext> BindingContext { get; set; } public IActionBindingContextAccessor BindingContext { get; set; }
} }
[Fact] [Fact]
@ -1528,7 +1528,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
public Address1 Address { get; set; } public Address1 Address { get; set; }
[FromServices] [FromServices]
public IScopedInstance<ActionBindingContext> BindingContext { get; set; } public IActionBindingContextAccessor BindingContext { get; set; }
} }
// If a nested POCO object has all properties bound from a greedy source, then it should be populated // If a nested POCO object has all properties bound from a greedy source, then it should be populated

View File

@ -536,7 +536,7 @@ namespace Microsoft.AspNet.Mvc
return builder; return builder;
} }
private static IScopedInstance<ActionContext> GetContextAccessor( private static IActionContextAccessor GetContextAccessor(
IServiceProvider serviceProvider, IServiceProvider serviceProvider,
RouteData routeData = null) RouteData routeData = null)
{ {
@ -557,12 +557,12 @@ namespace Microsoft.AspNet.Mvc
} }
var actionContext = new ActionContext(httpContext, routeData, new ActionDescriptor()); var actionContext = new ActionContext(httpContext, routeData, new ActionDescriptor());
var contextAccessor = new Mock<IScopedInstance<ActionContext>>(); var contextAccessor = new ActionContextAccessor()
contextAccessor {
.SetupGet(accessor => accessor.Value) ActionContext = actionContext,
.Returns(actionContext); };
return contextAccessor.Object; return contextAccessor;
} }
private static ServiceCollection GetServiceCollection() private static ServiceCollection GetServiceCollection()

View File

@ -72,15 +72,10 @@ namespace System.Web.Http
var optionsAccessor = new Mock<IOptions<MvcOptions>>(); var optionsAccessor = new Mock<IOptions<MvcOptions>>();
optionsAccessor.SetupGet(o => o.Options) optionsAccessor.SetupGet(o => o.Options)
.Returns(options); .Returns(options);
var mockActionBindingContext = new Mock<IScopedInstance<ActionBindingContext>>(); var actionBindingContext = new ActionBindingContext { OutputFormatters = options.OutputFormatters };
var bindingContext = new ActionBindingContext { OutputFormatters = options.OutputFormatters }; services.Setup(o => o.GetService(typeof(IActionBindingContextAccessor)))
mockActionBindingContext .Returns(new ActionBindingContextAccessor() { ActionBindingContext = actionBindingContext });
.SetupGet(o => o.Value)
.Returns(bindingContext);
services.Setup(o => o.GetService(typeof(IScopedInstance<ActionBindingContext>)))
.Returns(mockActionBindingContext.Object);
services.Setup(s => s.GetService(typeof(IOptions<MvcOptions>))) services.Setup(s => s.GetService(typeof(IOptions<MvcOptions>)))
.Returns(optionsAccessor.Object); .Returns(optionsAccessor.Object);

View File

@ -73,14 +73,9 @@ namespace System.Web.Http
optionsAccessor.SetupGet(o => o.Options) optionsAccessor.SetupGet(o => o.Options)
.Returns(options); .Returns(options);
var mockActionBindingContext = new Mock<IScopedInstance<ActionBindingContext>>(); var actionBindingContext = new ActionBindingContext { OutputFormatters = options.OutputFormatters };
var bindingContext = new ActionBindingContext { OutputFormatters = options.OutputFormatters }; services.Setup(o => o.GetService(typeof(IActionBindingContextAccessor)))
mockActionBindingContext .Returns(new ActionBindingContextAccessor() { ActionBindingContext = actionBindingContext });
.SetupGet(o => o.Value)
.Returns(bindingContext);
services.Setup(o => o.GetService(typeof(IScopedInstance<ActionBindingContext>)))
.Returns(mockActionBindingContext.Object);
services.Setup(s => s.GetService(typeof(IOptions<MvcOptions>))) services.Setup(s => s.GetService(typeof(IOptions<MvcOptions>)))
.Returns(optionsAccessor.Object); .Returns(optionsAccessor.Object);

View File

@ -86,15 +86,9 @@ namespace System.Web.Http
optionsAccessor.SetupGet(o => o.Options) optionsAccessor.SetupGet(o => o.Options)
.Returns(options); .Returns(options);
var mockActionBindingContext = new Mock<IScopedInstance<ActionBindingContext>>(); var actionBindingContext = new ActionBindingContext { OutputFormatters = options.OutputFormatters };
services.Setup(o => o.GetService(typeof(IActionBindingContextAccessor)))
var bindingContext = new ActionBindingContext { OutputFormatters = options.OutputFormatters }; .Returns(new ActionBindingContextAccessor() { ActionBindingContext = actionBindingContext });
mockActionBindingContext
.SetupGet(o => o.Value)
.Returns(bindingContext);
services.Setup(o => o.GetService(typeof(IScopedInstance<ActionBindingContext>)))
.Returns(mockActionBindingContext.Object);
services.Setup(s => s.GetService(typeof(IOptions<MvcOptions>))) services.Setup(s => s.GetService(typeof(IOptions<MvcOptions>)))
.Returns(optionsAccessor.Object); .Returns(optionsAccessor.Object);

View File

@ -74,14 +74,9 @@ namespace System.Web.Http
optionsAccessor.SetupGet(o => o.Options) optionsAccessor.SetupGet(o => o.Options)
.Returns(options); .Returns(options);
var mockActionBindingContext = new Mock<IScopedInstance<ActionBindingContext>>(); var actionBindingContext = new ActionBindingContext { OutputFormatters = options.OutputFormatters };
var bindingContext = new ActionBindingContext { OutputFormatters = options.OutputFormatters }; services.Setup(o => o.GetService(typeof(IActionBindingContextAccessor)))
mockActionBindingContext .Returns(new ActionBindingContextAccessor() { ActionBindingContext = actionBindingContext });
.SetupGet(o => o.Value)
.Returns(bindingContext);
services.Setup(o => o.GetService(typeof(IScopedInstance<ActionBindingContext>)))
.Returns(mockActionBindingContext.Object);
services.Setup(s => s.GetService(typeof(IOptions<MvcOptions>))) services.Setup(s => s.GetService(typeof(IOptions<MvcOptions>)))
.Returns(optionsAccessor.Object); .Returns(optionsAccessor.Object);

View File

@ -14,9 +14,9 @@ namespace RoutingWebSite
{ {
private readonly ActionContext _actionContext; private readonly ActionContext _actionContext;
public TestResponseGenerator(IScopedInstance<ActionContext> contextAccessor) public TestResponseGenerator(IActionContextAccessor contextAccessor)
{ {
_actionContext = contextAccessor.Value; _actionContext = contextAccessor.ActionContext;
if (_actionContext == null) if (_actionContext == null)
{ {
throw new InvalidOperationException("ActionContext should not be null here."); throw new InvalidOperationException("ActionContext should not be null here.");

View File

@ -4,7 +4,6 @@
using System; using System;
using Microsoft.AspNet.Http; using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc; using Microsoft.AspNet.Mvc;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.OptionsModel; using Microsoft.Framework.OptionsModel;
namespace UrlHelperWebSite namespace UrlHelperWebSite
@ -19,12 +18,14 @@ namespace UrlHelperWebSite
private readonly IOptions<AppOptions> _appOptions; private readonly IOptions<AppOptions> _appOptions;
private readonly HttpContext _httpContext; private readonly HttpContext _httpContext;
public CustomUrlHelper(IScopedInstance<ActionContext> contextAccessor, IActionSelector actionSelector, public CustomUrlHelper(
IOptions<AppOptions> appOptions) IActionContextAccessor contextAccessor,
IActionSelector actionSelector,
IOptions<AppOptions> appOptions)
: base(contextAccessor, actionSelector) : base(contextAccessor, actionSelector)
{ {
_appOptions = appOptions; _appOptions = appOptions;
_httpContext = contextAccessor.Value.HttpContext; _httpContext = contextAccessor.ActionContext.HttpContext;
} }
/// <summary> /// <summary>

View File

@ -14,9 +14,9 @@ namespace VersioningWebSite
{ {
private readonly ActionContext _actionContext; private readonly ActionContext _actionContext;
public TestResponseGenerator(IScopedInstance<ActionContext> contextAccessor) public TestResponseGenerator(IActionContextAccessor contextAccessor)
{ {
_actionContext = contextAccessor.Value; _actionContext = contextAccessor.ActionContext;
if (_actionContext == null) if (_actionContext == null)
{ {
throw new InvalidOperationException("ActionContext should not be null here."); throw new InvalidOperationException("ActionContext should not be null here.");