Add optional filter caching
This commit is contained in:
parent
c5cab7a786
commit
70cee90186
|
|
@ -1,16 +1,30 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<<<<<<< HEAD
|
||||||
<Project ToolsVersion="14.0.24720" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="14.0.24720" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0.24720</VisualStudioVersion>
|
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0.24720</VisualStudioVersion>
|
||||||
|
=======
|
||||||
|
<Project ToolsVersion="14.0.24711" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0.24711</VisualStudioVersion>
|
||||||
|
>>>>>>> CR feedback
|
||||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
|
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||||
<PropertyGroup Label="Globals">
|
<PropertyGroup Label="Globals">
|
||||||
|
<<<<<<< HEAD
|
||||||
<ProjectGuid>45F6B3B6-D114-4D77-84D6-561B3957F341</ProjectGuid>
|
<ProjectGuid>45F6B3B6-D114-4D77-84D6-561B3957F341</ProjectGuid>
|
||||||
|
=======
|
||||||
|
<ProjectGuid>e26979a8-56fb-41f7-8da5-a49a570acd39</ProjectGuid>
|
||||||
|
>>>>>>> CR feedback
|
||||||
<RootNamespace>MvcSubAreaSample.Web</RootNamespace>
|
<RootNamespace>MvcSubAreaSample.Web</RootNamespace>
|
||||||
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
|
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
|
||||||
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
|
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<<<<<<< HEAD
|
||||||
|
=======
|
||||||
|
|
||||||
|
>>>>>>> CR feedback
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<SchemaVersion>2.0</SchemaVersion>
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,19 @@ using System.Diagnostics;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.Filters
|
namespace Microsoft.AspNet.Mvc.Filters
|
||||||
{
|
{
|
||||||
// Used to flow filters back from the FilterProviderContext
|
/// <summary>
|
||||||
|
/// Used to associate executable filters with <see cref="IFilterMetadata"/> instances
|
||||||
|
/// as part of <see cref="FilterProviderContext"/>. An <see cref="IFilterProvider"/> should
|
||||||
|
/// inspect <see cref="FilterProviderContext.Results"/> and set <see cref="Filter"/> and
|
||||||
|
/// <see cref="IsReusable"/> as appropriate.
|
||||||
|
/// </summary>
|
||||||
[DebuggerDisplay("FilterItem: {Filter}")]
|
[DebuggerDisplay("FilterItem: {Filter}")]
|
||||||
public class FilterItem
|
public class FilterItem
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="FilterItem"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="descriptor">The <see cref="FilterDescriptor"/>.</param>
|
||||||
public FilterItem(FilterDescriptor descriptor)
|
public FilterItem(FilterDescriptor descriptor)
|
||||||
{
|
{
|
||||||
if (descriptor == null)
|
if (descriptor == null)
|
||||||
|
|
@ -20,6 +29,11 @@ namespace Microsoft.AspNet.Mvc.Filters
|
||||||
Descriptor = descriptor;
|
Descriptor = descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="FilterItem"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="descriptor">The <see cref="FilterDescriptor"/>.</param>
|
||||||
|
/// <param name="filter"></param>
|
||||||
public FilterItem(FilterDescriptor descriptor, IFilterMetadata filter)
|
public FilterItem(FilterDescriptor descriptor, IFilterMetadata filter)
|
||||||
: this(descriptor)
|
: this(descriptor)
|
||||||
{
|
{
|
||||||
|
|
@ -31,8 +45,19 @@ namespace Microsoft.AspNet.Mvc.Filters
|
||||||
Filter = filter;
|
Filter = filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FilterDescriptor Descriptor { get; set; }
|
/// <summary>
|
||||||
|
/// Gets the <see cref="FilterDescriptor"/> containing the filter metadata.
|
||||||
|
/// </summary>
|
||||||
|
public FilterDescriptor Descriptor { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the executable <see cref="IFilterMetadata"/> associated with <see cref="Descriptor"/>.
|
||||||
|
/// </summary>
|
||||||
public IFilterMetadata Filter { get; set; }
|
public IFilterMetadata Filter { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether or not <see cref="Filter"/> can be reused across requests.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsReusable { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,22 @@ using System;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.Filters
|
namespace Microsoft.AspNet.Mvc.Filters
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An interface for for filter metadata which can create an instance of an executable filter.
|
||||||
|
/// </summary>
|
||||||
public interface IFilterFactory : IFilterMetadata
|
public interface IFilterFactory : IFilterMetadata
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value that indicates if the result of <see cref="CreateInstance(IServiceProvider)"/>
|
||||||
|
/// can be reused across requests.
|
||||||
|
/// </summary>
|
||||||
|
bool IsReusable { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates an instance of the executable filter.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="serviceProvider">The request <see cref="IServiceProvider"/>.</param>
|
||||||
|
/// <returns>An instance of the executable filter.</returns>
|
||||||
IFilterMetadata CreateInstance(IServiceProvider serviceProvider);
|
IFilterMetadata CreateInstance(IServiceProvider serviceProvider);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Mvc.Core;
|
using Microsoft.AspNet.Mvc.Core;
|
||||||
using Microsoft.AspNet.Mvc.Filters;
|
using Microsoft.AspNet.Mvc.Filters;
|
||||||
using Microsoft.AspNet.Mvc.Formatters;
|
using Microsoft.AspNet.Mvc.Formatters;
|
||||||
|
using Microsoft.AspNet.Mvc.Internal;
|
||||||
using Microsoft.AspNet.Mvc.Logging;
|
using Microsoft.AspNet.Mvc.Logging;
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||||
|
|
@ -25,11 +26,11 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
||||||
|
|
||||||
public ControllerActionInvoker(
|
public ControllerActionInvoker(
|
||||||
ActionContext actionContext,
|
ActionContext actionContext,
|
||||||
IReadOnlyList<IFilterProvider> filterProviders,
|
FilterCache filterCache,
|
||||||
IControllerFactory controllerFactory,
|
IControllerFactory controllerFactory,
|
||||||
ControllerActionDescriptor descriptor,
|
ControllerActionDescriptor descriptor,
|
||||||
IReadOnlyList<IInputFormatter> inputFormatters,
|
IReadOnlyList<IInputFormatter> inputFormatters,
|
||||||
IControllerActionArgumentBinder controllerActionArgumentBinder,
|
IControllerActionArgumentBinder argumentBinder,
|
||||||
IReadOnlyList<IModelBinder> modelBinders,
|
IReadOnlyList<IModelBinder> modelBinders,
|
||||||
IReadOnlyList<IModelValidatorProvider> modelValidatorProviders,
|
IReadOnlyList<IModelValidatorProvider> modelValidatorProviders,
|
||||||
IReadOnlyList<IValueProviderFactory> valueProviderFactories,
|
IReadOnlyList<IValueProviderFactory> valueProviderFactories,
|
||||||
|
|
@ -38,7 +39,7 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
||||||
int maxModelValidationErrors)
|
int maxModelValidationErrors)
|
||||||
: base(
|
: base(
|
||||||
actionContext,
|
actionContext,
|
||||||
filterProviders,
|
filterCache,
|
||||||
inputFormatters,
|
inputFormatters,
|
||||||
modelBinders,
|
modelBinders,
|
||||||
modelValidatorProviders,
|
modelValidatorProviders,
|
||||||
|
|
@ -47,16 +48,6 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
||||||
diagnosticSource,
|
diagnosticSource,
|
||||||
maxModelValidationErrors)
|
maxModelValidationErrors)
|
||||||
{
|
{
|
||||||
if (actionContext == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(actionContext));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filterProviders == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(filterProviders));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (controllerFactory == null)
|
if (controllerFactory == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(controllerFactory));
|
throw new ArgumentNullException(nameof(controllerFactory));
|
||||||
|
|
@ -67,51 +58,22 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
||||||
throw new ArgumentNullException(nameof(descriptor));
|
throw new ArgumentNullException(nameof(descriptor));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inputFormatters == null)
|
if (argumentBinder == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(inputFormatters));
|
throw new ArgumentNullException(nameof(argumentBinder));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (controllerActionArgumentBinder == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(controllerActionArgumentBinder));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (modelBinders == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(modelBinders));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (modelValidatorProviders == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(modelValidatorProviders));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (valueProviderFactories == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(valueProviderFactories));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (logger == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(logger));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (diagnosticSource == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(diagnosticSource));
|
|
||||||
}
|
|
||||||
|
|
||||||
_descriptor = descriptor;
|
|
||||||
_controllerFactory = controllerFactory;
|
_controllerFactory = controllerFactory;
|
||||||
_argumentBinder = controllerActionArgumentBinder;
|
_descriptor = descriptor;
|
||||||
|
_argumentBinder = argumentBinder;
|
||||||
|
|
||||||
if (descriptor.MethodInfo == null)
|
if (descriptor.MethodInfo == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentException(
|
throw new ArgumentException(
|
||||||
Resources.FormatPropertyOfTypeCannotBeNull("MethodInfo",
|
Resources.FormatPropertyOfTypeCannotBeNull(
|
||||||
typeof(ControllerActionDescriptor)),
|
nameof(descriptor.MethodInfo),
|
||||||
"descriptor");
|
typeof(ControllerActionDescriptor)),
|
||||||
|
nameof(descriptor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.AspNet.Mvc.Abstractions;
|
using Microsoft.AspNet.Mvc.Abstractions;
|
||||||
using Microsoft.AspNet.Mvc.Filters;
|
|
||||||
using Microsoft.AspNet.Mvc.Formatters;
|
using Microsoft.AspNet.Mvc.Formatters;
|
||||||
|
using Microsoft.AspNet.Mvc.Internal;
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
@ -19,7 +19,7 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
||||||
{
|
{
|
||||||
private readonly IControllerActionArgumentBinder _argumentBinder;
|
private readonly IControllerActionArgumentBinder _argumentBinder;
|
||||||
private readonly IControllerFactory _controllerFactory;
|
private readonly IControllerFactory _controllerFactory;
|
||||||
private readonly IFilterProvider[] _filterProviders;
|
private readonly FilterCache _filterCache;
|
||||||
private readonly IReadOnlyList<IInputFormatter> _inputFormatters;
|
private readonly IReadOnlyList<IInputFormatter> _inputFormatters;
|
||||||
private readonly IReadOnlyList<IModelBinder> _modelBinders;
|
private readonly IReadOnlyList<IModelBinder> _modelBinders;
|
||||||
private readonly IReadOnlyList<IModelValidatorProvider> _modelValidatorProviders;
|
private readonly IReadOnlyList<IModelValidatorProvider> _modelValidatorProviders;
|
||||||
|
|
@ -30,14 +30,14 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
||||||
|
|
||||||
public ControllerActionInvokerProvider(
|
public ControllerActionInvokerProvider(
|
||||||
IControllerFactory controllerFactory,
|
IControllerFactory controllerFactory,
|
||||||
IEnumerable<IFilterProvider> filterProviders,
|
FilterCache filterCache,
|
||||||
IControllerActionArgumentBinder argumentBinder,
|
IControllerActionArgumentBinder argumentBinder,
|
||||||
IOptions<MvcOptions> optionsAccessor,
|
IOptions<MvcOptions> optionsAccessor,
|
||||||
ILoggerFactory loggerFactory,
|
ILoggerFactory loggerFactory,
|
||||||
DiagnosticSource diagnosticSource)
|
DiagnosticSource diagnosticSource)
|
||||||
{
|
{
|
||||||
_controllerFactory = controllerFactory;
|
_controllerFactory = controllerFactory;
|
||||||
_filterProviders = filterProviders.OrderBy(item => item.Order).ToArray();
|
_filterCache = filterCache;
|
||||||
_argumentBinder = argumentBinder;
|
_argumentBinder = argumentBinder;
|
||||||
_inputFormatters = optionsAccessor.Value.InputFormatters.ToArray();
|
_inputFormatters = optionsAccessor.Value.InputFormatters.ToArray();
|
||||||
_modelBinders = optionsAccessor.Value.ModelBinders.ToArray();
|
_modelBinders = optionsAccessor.Value.ModelBinders.ToArray();
|
||||||
|
|
@ -67,7 +67,7 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
||||||
{
|
{
|
||||||
context.Result = new ControllerActionInvoker(
|
context.Result = new ControllerActionInvoker(
|
||||||
context.ActionContext,
|
context.ActionContext,
|
||||||
_filterProviders,
|
_filterCache,
|
||||||
_controllerFactory,
|
_controllerFactory,
|
||||||
actionDescriptor,
|
actionDescriptor,
|
||||||
_inputFormatters,
|
_inputFormatters,
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,7 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
||||||
{
|
{
|
||||||
public abstract class FilterActionInvoker : IActionInvoker
|
public abstract class FilterActionInvoker : IActionInvoker
|
||||||
{
|
{
|
||||||
private static readonly IFilterMetadata[] EmptyFilterArray = new IFilterMetadata[0];
|
private readonly FilterCache _filterCache;
|
||||||
|
|
||||||
private readonly IReadOnlyList<IFilterProvider> _filterProviders;
|
|
||||||
private readonly IReadOnlyList<IInputFormatter> _inputFormatters;
|
private readonly IReadOnlyList<IInputFormatter> _inputFormatters;
|
||||||
private readonly IReadOnlyList<IModelBinder> _modelBinders;
|
private readonly IReadOnlyList<IModelBinder> _modelBinders;
|
||||||
private readonly IReadOnlyList<IModelValidatorProvider> _modelValidatorProviders;
|
private readonly IReadOnlyList<IModelValidatorProvider> _modelValidatorProviders;
|
||||||
|
|
@ -49,7 +47,7 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
||||||
|
|
||||||
public FilterActionInvoker(
|
public FilterActionInvoker(
|
||||||
ActionContext actionContext,
|
ActionContext actionContext,
|
||||||
IReadOnlyList<IFilterProvider> filterProviders,
|
FilterCache filterCache,
|
||||||
IReadOnlyList<IInputFormatter> inputFormatters,
|
IReadOnlyList<IInputFormatter> inputFormatters,
|
||||||
IReadOnlyList<IModelBinder> modelBinders,
|
IReadOnlyList<IModelBinder> modelBinders,
|
||||||
IReadOnlyList<IModelValidatorProvider> modelValidatorProviders,
|
IReadOnlyList<IModelValidatorProvider> modelValidatorProviders,
|
||||||
|
|
@ -63,9 +61,9 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
||||||
throw new ArgumentNullException(nameof(actionContext));
|
throw new ArgumentNullException(nameof(actionContext));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filterProviders == null)
|
if (filterCache == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(filterProviders));
|
throw new ArgumentNullException(nameof(filterCache));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inputFormatters == null)
|
if (inputFormatters == null)
|
||||||
|
|
@ -100,7 +98,7 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
||||||
|
|
||||||
Context = new ControllerContext(actionContext);
|
Context = new ControllerContext(actionContext);
|
||||||
|
|
||||||
_filterProviders = filterProviders;
|
_filterCache = filterCache;
|
||||||
_inputFormatters = inputFormatters;
|
_inputFormatters = inputFormatters;
|
||||||
_modelBinders = modelBinders;
|
_modelBinders = modelBinders;
|
||||||
_modelValidatorProviders = modelValidatorProviders;
|
_modelValidatorProviders = modelValidatorProviders;
|
||||||
|
|
@ -184,51 +182,7 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
||||||
|
|
||||||
private IFilterMetadata[] GetFilters()
|
private IFilterMetadata[] GetFilters()
|
||||||
{
|
{
|
||||||
var filterDescriptors = Context.ActionDescriptor.FilterDescriptors;
|
return _filterCache.GetFilters(Context);
|
||||||
var items = new List<FilterItem>(filterDescriptors.Count);
|
|
||||||
for (var i = 0; i < filterDescriptors.Count; i++)
|
|
||||||
{
|
|
||||||
items.Add(new FilterItem(filterDescriptors[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
var context = new FilterProviderContext(Context, items);
|
|
||||||
for (var i = 0; i < _filterProviders.Count; i++)
|
|
||||||
{
|
|
||||||
_filterProviders[i].OnProvidersExecuting(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = _filterProviders.Count - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
_filterProviders[i].OnProvidersExecuted(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
var count = 0;
|
|
||||||
for (var i = 0; i < items.Count; i++)
|
|
||||||
{
|
|
||||||
if (items[i].Filter != null)
|
|
||||||
{
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count == 0)
|
|
||||||
{
|
|
||||||
return EmptyFilterArray;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var filters = new IFilterMetadata[count];
|
|
||||||
for (int i = 0, j = 0; i < items.Count; i++)
|
|
||||||
{
|
|
||||||
var filter = items[i].Filter;
|
|
||||||
if (filter != null)
|
|
||||||
{
|
|
||||||
filters[j++] = filter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return filters;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task InvokeAllAuthorizationFiltersAsync()
|
private Task InvokeAllAuthorizationFiltersAsync()
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||||
|
|
||||||
// These are stateless
|
// These are stateless
|
||||||
services.TryAddSingleton<IControllerActionArgumentBinder, DefaultControllerActionArgumentBinder>();
|
services.TryAddSingleton<IControllerActionArgumentBinder, DefaultControllerActionArgumentBinder>();
|
||||||
|
services.TryAddSingleton<FilterCache>();
|
||||||
services.TryAddEnumerable(
|
services.TryAddEnumerable(
|
||||||
ServiceDescriptor.Singleton<IFilterProvider, DefaultFilterProvider>());
|
ServiceDescriptor.Singleton<IFilterProvider, DefaultFilterProvider>());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,11 +50,13 @@ namespace Microsoft.AspNet.Mvc.Filters
|
||||||
if (filterFactory == null)
|
if (filterFactory == null)
|
||||||
{
|
{
|
||||||
filterItem.Filter = filter;
|
filterItem.Filter = filter;
|
||||||
|
filterItem.IsReusable = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var services = context.ActionContext.HttpContext.RequestServices;
|
var services = context.ActionContext.HttpContext.RequestServices;
|
||||||
filterItem.Filter = filterFactory.CreateInstance(services);
|
filterItem.Filter = filterFactory.CreateInstance(services);
|
||||||
|
filterItem.IsReusable = filterFactory.IsReusable;
|
||||||
|
|
||||||
if (filterItem.Filter == null)
|
if (filterItem.Filter == null)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,9 @@ namespace Microsoft.AspNet.Mvc
|
||||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
|
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
|
||||||
public class FormatFilterAttribute : Attribute, IFilterFactory
|
public class FormatFilterAttribute : Attribute, IFilterFactory
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool IsReusable => true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates an instance of <see cref="FormatFilter"/>.
|
/// Creates an instance of <see cref="FormatFilter"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,213 @@
|
||||||
|
// 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.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using Microsoft.AspNet.Mvc.Abstractions;
|
||||||
|
using Microsoft.AspNet.Mvc.Filters;
|
||||||
|
using Microsoft.AspNet.Mvc.Infrastructure;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.Internal
|
||||||
|
{
|
||||||
|
public class FilterCache
|
||||||
|
{
|
||||||
|
private readonly IFilterMetadata[] EmptyFilterArray = new IFilterMetadata[0];
|
||||||
|
|
||||||
|
private readonly IActionDescriptorCollectionProvider _collectionProvider;
|
||||||
|
private readonly IFilterProvider[] _filterProviders;
|
||||||
|
|
||||||
|
private volatile InnerCache _currentCache;
|
||||||
|
|
||||||
|
public FilterCache(
|
||||||
|
IActionDescriptorCollectionProvider collectionProvider,
|
||||||
|
IEnumerable<IFilterProvider> filterProviders)
|
||||||
|
{
|
||||||
|
_collectionProvider = collectionProvider;
|
||||||
|
_filterProviders = filterProviders.OrderBy(item => item.Order).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private InnerCache CurrentCache
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var current = _currentCache;
|
||||||
|
var actionDescriptors = _collectionProvider.ActionDescriptors;
|
||||||
|
|
||||||
|
if (current == null || current.Version != actionDescriptors.Version)
|
||||||
|
{
|
||||||
|
current = new InnerCache(actionDescriptors.Version);
|
||||||
|
_currentCache = current;
|
||||||
|
}
|
||||||
|
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IFilterMetadata[] GetFilters(ActionContext actionContext)
|
||||||
|
{
|
||||||
|
var cache = CurrentCache;
|
||||||
|
var actionDescriptor = actionContext.ActionDescriptor;
|
||||||
|
|
||||||
|
CacheEntry entry;
|
||||||
|
if (cache.Entries.TryGetValue(actionDescriptor, out entry))
|
||||||
|
{
|
||||||
|
return GetFiltersFromEntry(entry, actionContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
var items = new List<FilterItem>(actionDescriptor.FilterDescriptors.Count);
|
||||||
|
for (var i = 0; i < actionDescriptor.FilterDescriptors.Count; i++)
|
||||||
|
{
|
||||||
|
items.Add(new FilterItem(actionDescriptor.FilterDescriptors[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
ExecuteProviders(actionContext, items);
|
||||||
|
|
||||||
|
var filters = ExtractFilters(items);
|
||||||
|
|
||||||
|
var allFiltersCached = true;
|
||||||
|
for (var i = 0; i < items.Count; i++)
|
||||||
|
{
|
||||||
|
var item = items[i];
|
||||||
|
if (!item.IsReusable)
|
||||||
|
{
|
||||||
|
item.Filter = null;
|
||||||
|
allFiltersCached = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allFiltersCached)
|
||||||
|
{
|
||||||
|
entry = new CacheEntry(filters);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
entry = new CacheEntry(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
cache.Entries.TryAdd(actionDescriptor, entry);
|
||||||
|
return filters;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IFilterMetadata[] GetFiltersFromEntry(CacheEntry entry, ActionContext actionContext)
|
||||||
|
{
|
||||||
|
Debug.Assert(entry.Filters != null || entry.Items != null);
|
||||||
|
|
||||||
|
if (entry.Filters != null)
|
||||||
|
{
|
||||||
|
return entry.Filters;
|
||||||
|
}
|
||||||
|
|
||||||
|
var items = new List<FilterItem>(entry.Items.Length);
|
||||||
|
for (var i = 0; i < entry.Items.Length; i++)
|
||||||
|
{
|
||||||
|
var item = entry.Items[i];
|
||||||
|
if (item.IsReusable)
|
||||||
|
{
|
||||||
|
items.Add(item);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
items.Add(new FilterItem(item.Descriptor));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ExecuteProviders(actionContext, items);
|
||||||
|
|
||||||
|
return ExtractFilters(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ExecuteProviders(ActionContext actionContext, List<FilterItem> items)
|
||||||
|
{
|
||||||
|
var context = new FilterProviderContext(actionContext, items);
|
||||||
|
|
||||||
|
for (var i = 0; i < _filterProviders.Length; i++)
|
||||||
|
{
|
||||||
|
_filterProviders[i].OnProvidersExecuting(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = _filterProviders.Length - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
_filterProviders[i].OnProvidersExecuted(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IFilterMetadata[] ExtractFilters(List<FilterItem> items)
|
||||||
|
{
|
||||||
|
var count = 0;
|
||||||
|
for (var i = 0; i < items.Count; i++)
|
||||||
|
{
|
||||||
|
if (items[i].Filter != null)
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
return EmptyFilterArray;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var filters = new IFilterMetadata[count];
|
||||||
|
for (int i = 0, j = 0; i < items.Count; i++)
|
||||||
|
{
|
||||||
|
var filter = items[i].Filter;
|
||||||
|
if (filter != null)
|
||||||
|
{
|
||||||
|
filters[j++] = filter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filters;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class InnerCache
|
||||||
|
{
|
||||||
|
public InnerCache(int version)
|
||||||
|
{
|
||||||
|
Version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConcurrentDictionary<ActionDescriptor, CacheEntry> Entries { get; } =
|
||||||
|
new ConcurrentDictionary<ActionDescriptor, CacheEntry>();
|
||||||
|
|
||||||
|
public int Version { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct CacheEntry
|
||||||
|
{
|
||||||
|
public CacheEntry(IFilterMetadata[] filters)
|
||||||
|
{
|
||||||
|
Filters = filters;
|
||||||
|
Items = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CacheEntry(List<FilterItem> items)
|
||||||
|
{
|
||||||
|
Items = new FilterItem[items.Count];
|
||||||
|
for (var i = 0; i < Items.Length; i++)
|
||||||
|
{
|
||||||
|
var item = items[i];
|
||||||
|
if (item.IsReusable)
|
||||||
|
{
|
||||||
|
Items[i] = item;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Items[i] = new FilterItem(item.Descriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Filters = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IFilterMetadata[] Filters { get; }
|
||||||
|
|
||||||
|
public FilterItem[] Items { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -81,11 +81,12 @@ namespace Microsoft.AspNet.Mvc
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string CacheProfileName { get; set; }
|
public string CacheProfileName { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// The order of the filter.
|
|
||||||
/// </summary>
|
|
||||||
public int Order { get; set; }
|
public int Order { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool IsReusable => true;
|
||||||
|
|
||||||
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
|
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
if (serviceProvider == null)
|
if (serviceProvider == null)
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,13 @@ namespace Microsoft.AspNet.Mvc
|
||||||
ServiceType = type;
|
ServiceType = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public int Order { get; set; }
|
||||||
|
|
||||||
public Type ServiceType { get; private set; }
|
public Type ServiceType { get; private set; }
|
||||||
|
|
||||||
public int Order { get; set; }
|
/// <inheritdoc />
|
||||||
|
public bool IsReusable { get; set; }
|
||||||
|
|
||||||
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
|
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,12 @@ namespace Microsoft.AspNet.Mvc
|
||||||
|
|
||||||
public Type ImplementationType { get; private set; }
|
public Type ImplementationType { get; private set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public int Order { get; set; }
|
public int Order { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool IsReusable { get; set; }
|
||||||
|
|
||||||
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
|
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
if (serviceProvider == null)
|
if (serviceProvider == null)
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,9 @@ namespace Microsoft.AspNet.Mvc.Cors
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool IsReusable => true;
|
||||||
|
|
||||||
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
|
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
if (serviceProvider == null)
|
if (serviceProvider == null)
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,9 @@ namespace Microsoft.AspNet.Mvc
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public int Order { get; set; }
|
public int Order { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool IsReusable => true;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
|
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,12 @@ namespace Microsoft.AspNet.Mvc
|
||||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
|
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
|
||||||
public class ValidateAntiForgeryTokenAttribute : Attribute, IFilterFactory, IOrderedFilter
|
public class ValidateAntiForgeryTokenAttribute : Attribute, IFilterFactory, IOrderedFilter
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
public int Order { get; set; }
|
public int Order { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool IsReusable => true;
|
||||||
|
|
||||||
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
|
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
return serviceProvider.GetRequiredService<ValidateAntiforgeryTokenAuthorizationFilter>();
|
return serviceProvider.GetRequiredService<ValidateAntiforgeryTokenAuthorizationFilter>();
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,14 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
|
||||||
/// Adds a filter which will save the <see cref="ITempDataDictionary"/> for a request.
|
/// Adds a filter which will save the <see cref="ITempDataDictionary"/> for a request.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
|
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
|
||||||
public class SaveTempDataAttribute : Attribute, IFilterFactory
|
public class SaveTempDataAttribute : Attribute, IFilterFactory, IOrderedFilter
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public int Order { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool IsReusable => true;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
|
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ using Microsoft.AspNet.Mvc.Abstractions;
|
||||||
using Microsoft.AspNet.Mvc.Filters;
|
using Microsoft.AspNet.Mvc.Filters;
|
||||||
using Microsoft.AspNet.Mvc.Formatters;
|
using Microsoft.AspNet.Mvc.Formatters;
|
||||||
using Microsoft.AspNet.Mvc.Infrastructure;
|
using Microsoft.AspNet.Mvc.Infrastructure;
|
||||||
|
using Microsoft.AspNet.Mvc.Internal;
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||||
using Microsoft.AspNet.Routing;
|
using Microsoft.AspNet.Routing;
|
||||||
|
|
@ -22,7 +23,6 @@ using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Logging.Testing;
|
using Microsoft.Extensions.Logging.Testing;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Microsoft.Net.Http.Headers;
|
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -2027,10 +2027,10 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
||||||
{
|
{
|
||||||
foreach (var filterMetadata in filters)
|
foreach (var filterMetadata in filters)
|
||||||
{
|
{
|
||||||
var filter = new FilterItem(
|
context.Results.Add(new FilterItem(new FilterDescriptor(filterMetadata, FilterScope.Action))
|
||||||
new FilterDescriptor(filterMetadata, FilterScope.Action),
|
{
|
||||||
filterMetadata);
|
Filter = filterMetadata,
|
||||||
context.Results.Add(filter);
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -2106,7 +2106,7 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
||||||
|
|
||||||
var invoker = new ControllerActionInvoker(
|
var invoker = new ControllerActionInvoker(
|
||||||
actionContext,
|
actionContext,
|
||||||
new List<IFilterProvider>(),
|
CreateFilterCache(),
|
||||||
controllerFactory.Object,
|
controllerFactory.Object,
|
||||||
actionDescriptor,
|
actionDescriptor,
|
||||||
new IInputFormatter[0],
|
new IInputFormatter[0],
|
||||||
|
|
@ -2222,11 +2222,18 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static FilterCache CreateFilterCache(IFilterProvider[] filterProviders = null)
|
||||||
|
{
|
||||||
|
var services = new ServiceCollection().BuildServiceProvider();
|
||||||
|
var descriptorProvider = new DefaultActionDescriptorCollectionProvider(services);
|
||||||
|
return new FilterCache(descriptorProvider, filterProviders.AsEnumerable() ?? new List<IFilterProvider>());
|
||||||
|
}
|
||||||
|
|
||||||
private class TestControllerActionInvoker : ControllerActionInvoker
|
private class TestControllerActionInvoker : ControllerActionInvoker
|
||||||
{
|
{
|
||||||
public TestControllerActionInvoker(
|
public TestControllerActionInvoker(
|
||||||
ActionContext actionContext,
|
ActionContext actionContext,
|
||||||
IFilterProvider[] filterProvider,
|
IFilterProvider[] filterProviders,
|
||||||
MockControllerFactory controllerFactory,
|
MockControllerFactory controllerFactory,
|
||||||
ControllerActionDescriptor descriptor,
|
ControllerActionDescriptor descriptor,
|
||||||
IReadOnlyList<IInputFormatter> inputFormatters,
|
IReadOnlyList<IInputFormatter> inputFormatters,
|
||||||
|
|
@ -2239,7 +2246,7 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
||||||
int maxAllowedErrorsInModelState)
|
int maxAllowedErrorsInModelState)
|
||||||
: base(
|
: base(
|
||||||
actionContext,
|
actionContext,
|
||||||
filterProvider,
|
CreateFilterCache(filterProviders),
|
||||||
controllerFactory,
|
controllerFactory,
|
||||||
descriptor,
|
descriptor,
|
||||||
inputFormatters,
|
inputFormatters,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,157 @@
|
||||||
|
// 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 Microsoft.AspNet.Http.Internal;
|
||||||
|
using Microsoft.AspNet.Mvc.Controllers;
|
||||||
|
using Microsoft.AspNet.Mvc.Filters;
|
||||||
|
using Microsoft.AspNet.Mvc.Infrastructure;
|
||||||
|
using Microsoft.AspNet.Routing;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.Internal
|
||||||
|
{
|
||||||
|
public class FilterCacheTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void GetFilters_CachesAllFilters()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var services = CreateServices();
|
||||||
|
var cache = CreateCache(new DefaultFilterProvider());
|
||||||
|
|
||||||
|
var action = new ControllerActionDescriptor()
|
||||||
|
{
|
||||||
|
FilterDescriptors = new[]
|
||||||
|
{
|
||||||
|
new FilterDescriptor(new TestFilter(), FilterScope.Action),
|
||||||
|
new FilterDescriptor(new TestFilter(), FilterScope.Action),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
var context = new ActionContext(new DefaultHttpContext(), new RouteData(), action);
|
||||||
|
|
||||||
|
// Act - 1
|
||||||
|
var filters1 = cache.GetFilters(context);
|
||||||
|
|
||||||
|
// Assert - 1
|
||||||
|
Assert.Collection(
|
||||||
|
filters1,
|
||||||
|
f => Assert.Same(action.FilterDescriptors[0].Filter, f), // Copied by provider
|
||||||
|
f => Assert.Same(action.FilterDescriptors[1].Filter, f)); // Copied by provider
|
||||||
|
|
||||||
|
// Act - 2
|
||||||
|
var filters2 = cache.GetFilters(context);
|
||||||
|
|
||||||
|
Assert.Same(filters1, filters2);
|
||||||
|
|
||||||
|
Assert.Collection(
|
||||||
|
filters2,
|
||||||
|
f => Assert.Same(action.FilterDescriptors[0].Filter, f), // Cached
|
||||||
|
f => Assert.Same(action.FilterDescriptors[1].Filter, f)); // Cached
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetFilters_CachesFilterFromFactory()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var services = CreateServices();
|
||||||
|
var cache = CreateCache(new DefaultFilterProvider());
|
||||||
|
|
||||||
|
var action = new ControllerActionDescriptor()
|
||||||
|
{
|
||||||
|
FilterDescriptors = new[]
|
||||||
|
{
|
||||||
|
new FilterDescriptor(new TestFilterFactory() { IsReusable = true }, FilterScope.Action),
|
||||||
|
new FilterDescriptor(new TestFilter(), FilterScope.Action),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
var context = new ActionContext(new DefaultHttpContext(), new RouteData(), action);
|
||||||
|
|
||||||
|
// Act - 1
|
||||||
|
var filters1 = cache.GetFilters(context);
|
||||||
|
|
||||||
|
// Assert - 1
|
||||||
|
Assert.Collection(
|
||||||
|
filters1,
|
||||||
|
f => Assert.NotSame(action.FilterDescriptors[0].Filter, f), // Created by factory
|
||||||
|
f => Assert.Same(action.FilterDescriptors[1].Filter, f)); // Copied by provider
|
||||||
|
|
||||||
|
// Act - 2
|
||||||
|
var filters2 = cache.GetFilters(context);
|
||||||
|
|
||||||
|
Assert.Same(filters1, filters2);
|
||||||
|
|
||||||
|
Assert.Collection(
|
||||||
|
filters2,
|
||||||
|
f => Assert.Same(filters1[0], f), // Cached
|
||||||
|
f => Assert.Same(filters1[1], f)); // Cached
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetFilters_DoesNotCacheFiltersWithIsReusableFalse()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var services = CreateServices();
|
||||||
|
var cache = CreateCache(new DefaultFilterProvider());
|
||||||
|
|
||||||
|
var action = new ControllerActionDescriptor()
|
||||||
|
{
|
||||||
|
FilterDescriptors = new[]
|
||||||
|
{
|
||||||
|
new FilterDescriptor(new TestFilterFactory() { IsReusable = false }, FilterScope.Action),
|
||||||
|
new FilterDescriptor(new TestFilter(), FilterScope.Action),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
var context = new ActionContext(new DefaultHttpContext(), new RouteData(), action);
|
||||||
|
|
||||||
|
// Act - 1
|
||||||
|
var filters1 = cache.GetFilters(context);
|
||||||
|
|
||||||
|
// Assert - 1
|
||||||
|
Assert.Collection(
|
||||||
|
filters1,
|
||||||
|
f => Assert.NotSame(action.FilterDescriptors[0].Filter, f), // Created by factory
|
||||||
|
f => Assert.Same(action.FilterDescriptors[1].Filter, f)); // Copied by provider
|
||||||
|
|
||||||
|
// Act - 2
|
||||||
|
var filters2 = cache.GetFilters(context);
|
||||||
|
|
||||||
|
Assert.NotSame(filters1, filters2);
|
||||||
|
|
||||||
|
Assert.Collection(
|
||||||
|
filters2,
|
||||||
|
f => Assert.NotSame(filters1[0], f), // Created by factory (again)
|
||||||
|
f => Assert.Same(filters1[1], f)); // Cached
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestFilter : IFilterMetadata
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestFilterFactory : IFilterFactory
|
||||||
|
{
|
||||||
|
public bool IsReusable { get; set; }
|
||||||
|
|
||||||
|
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
return new TestFilter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IServiceProvider CreateServices()
|
||||||
|
{
|
||||||
|
return new ServiceCollection().BuildServiceProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FilterCache CreateCache(params IFilterProvider[] providers)
|
||||||
|
{
|
||||||
|
var services = CreateServices();
|
||||||
|
var descriptorProvider = new DefaultActionDescriptorCollectionProvider(services);
|
||||||
|
return new FilterCache(descriptorProvider, providers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue