diff --git a/src/Microsoft.AspNetCore.Mvc.Core/MvcOptions.cs b/src/Microsoft.AspNetCore.Mvc.Core/MvcOptions.cs
index e7e55a8bbb..bad17f6849 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/MvcOptions.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/MvcOptions.cs
@@ -205,7 +205,7 @@ namespace Microsoft.AspNetCore.Mvc
///
/// Gets or sets an value indicating whether the model binding system will bind undefined values to
- /// enum types. The default value of the property is false.
+ /// enum types. The default value of the property is false.
///
///
///
diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageSaveTempDataPropertyFilter.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageSaveTempDataPropertyFilter.cs
index a7b45146df..73633bf59c 100644
--- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageSaveTempDataPropertyFilter.cs
+++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageSaveTempDataPropertyFilter.cs
@@ -1,48 +1,29 @@
// 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.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
-namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
+namespace Microsoft.AspNetCore.Mvc.RazorPages
{
- public class PageSaveTempDataPropertyFilter : SaveTempDataPropertyFilterBase, IPageFilter
+ internal class PageSaveTempDataPropertyFilter : SaveTempDataPropertyFilterBase, IPageFilter
{
public PageSaveTempDataPropertyFilter(ITempDataDictionaryFactory factory)
: base(factory)
{
}
- public PageSaveTempDataPropertyFilterFactory FilterFactory { get; set; }
-
public void OnPageHandlerSelected(PageHandlerSelectedContext context)
{
}
public void OnPageHandlerExecuting(PageHandlerExecutingContext context)
{
- if (context.HandlerInstance == null)
- {
- throw new InvalidOperationException(Resources.FormatPropertyOfTypeCannotBeNull(
- nameof(PageHandlerExecutingContext.HandlerInstance),
- typeof(PageHandlerExecutingContext).Name));
- }
-
- if (FilterFactory == null)
- {
- throw new InvalidOperationException(Resources.FormatPropertyOfTypeCannotBeNull(
- nameof(FilterFactory),
- typeof(PageSaveTempDataPropertyFilter).Name));
- }
-
- var tempData = _factory.GetTempData(context.HttpContext);
-
Subject = context.HandlerInstance;
- Properties = FilterFactory.GetTempDataProperties(Subject.GetType());
+ var tempData = _tempDataFactory.GetTempData(context.HttpContext);
- SetPropertyVaules(tempData, Subject);
+ SetPropertyVaules(tempData);
}
public void OnPageHandlerExecuted(PageHandlerExecutedContext context)
diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageSaveTempDataPropertyFilterFactory.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageSaveTempDataPropertyFilterFactory.cs
index 66618a722a..e33e9ff537 100644
--- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageSaveTempDataPropertyFilterFactory.cs
+++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageSaveTempDataPropertyFilterFactory.cs
@@ -4,14 +4,21 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc.Filters;
+using Microsoft.AspNetCore.Mvc.RazorPages.Internal;
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
using Microsoft.Extensions.DependencyInjection;
-namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
+namespace Microsoft.AspNetCore.Mvc.RazorPages
{
- public class PageSaveTempDataPropertyFilterFactory : IFilterFactory
+ internal class PageSaveTempDataPropertyFilterFactory : IFilterFactory
{
- public IList Properties { get; set; }
+
+ public PageSaveTempDataPropertyFilterFactory(IReadOnlyList properties)
+ {
+ Properties = properties;
+ }
+
+ public IReadOnlyList Properties { get; }
public bool IsReusable => false;
@@ -23,22 +30,9 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
}
var service = serviceProvider.GetRequiredService();
- service.FilterFactory = this;
+ service.Properties = Properties;
return service;
}
-
- public IList GetTempDataProperties(Type modelType)
- {
- // TempDataProperties are stored here as a cache for the filter. But in pages by the time we know the type
- // of our model we no longer have access to the factory, so we store the factory on the filter so it can
- // call this method to populate its TempDataProperties.
- if (Properties == null)
- {
- Properties = SaveTempDataPropertyFilterBase.GetTempDataProperties(modelType);
- }
-
- return Properties;
- }
}
}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/TempDataFilterPageApplicationModelProvider.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/TempDataFilterPageApplicationModelProvider.cs
index 7cfaaedfbd..411c13c608 100644
--- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/TempDataFilterPageApplicationModelProvider.cs
+++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/TempDataFilterPageApplicationModelProvider.cs
@@ -3,11 +3,20 @@
using System;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
+using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
+using Microsoft.Extensions.Options;
-namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
+namespace Microsoft.AspNetCore.Mvc.RazorPages
{
- public class TempDataFilterPageApplicationModelProvider : IPageApplicationModelProvider
+ internal class TempDataFilterPageApplicationModelProvider : IPageApplicationModelProvider
{
+ private readonly MvcViewOptions _options;
+
+ public TempDataFilterPageApplicationModelProvider(IOptions options)
+ {
+ _options = options.Value;
+ }
+
// The order is set to execute after the DefaultPageApplicationModelProvider.
public int Order => -1000 + 10;
@@ -23,9 +32,16 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
}
var pageApplicationModel = context.PageApplicationModel;
+ var handlerType = pageApplicationModel.HandlerType.AsType();
- // Support for [TempData] on properties
- pageApplicationModel.Filters.Add(new PageSaveTempDataPropertyFilterFactory());
+ var tempDataProperties = SaveTempDataPropertyFilterBase.GetTempDataProperties(handlerType, _options);
+ if (tempDataProperties == null)
+ {
+ return;
+ }
+
+ var filter = new PageSaveTempDataPropertyFilterFactory(tempDataProperties);
+ pageApplicationModel.Filters.Add(filter);
}
}
}
diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Properties/AssemblyInfo.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Properties/AssemblyInfo.cs
index a5c333c08d..458502d802 100644
--- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Properties/AssemblyInfo.cs
+++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Properties/AssemblyInfo.cs
@@ -3,4 +3,5 @@
using System.Runtime.CompilerServices;
+[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.RazorPages.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
diff --git a/src/Microsoft.AspNetCore.Mvc.ViewFeatures/DependencyInjection/MvcViewFeaturesMvcCoreBuilderExtensions.cs b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/DependencyInjection/MvcViewFeaturesMvcCoreBuilderExtensions.cs
index 7e7ce79fb2..8cbcd02974 100644
--- a/src/Microsoft.AspNetCore.Mvc.ViewFeatures/DependencyInjection/MvcViewFeaturesMvcCoreBuilderExtensions.cs
+++ b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/DependencyInjection/MvcViewFeaturesMvcCoreBuilderExtensions.cs
@@ -142,6 +142,8 @@ namespace Microsoft.Extensions.DependencyInjection
services.TryAddEnumerable(
ServiceDescriptor.Transient, MvcViewOptionsSetup>());
+ services.TryAddEnumerable(
+ ServiceDescriptor.Transient, MvcViewOptionsConfigureCompatibilityOptions>());
services.TryAddEnumerable(
ServiceDescriptor.Transient, TempDataMvcOptionsSetup>());
diff --git a/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/ControllerSaveTempDataPropertyFilter.cs b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/ControllerSaveTempDataPropertyFilter.cs
index ff35fac0f4..96228d5b7b 100644
--- a/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/ControllerSaveTempDataPropertyFilter.cs
+++ b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/ControllerSaveTempDataPropertyFilter.cs
@@ -2,10 +2,11 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Mvc.Filters;
+using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
-namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
+namespace Microsoft.AspNetCore.Mvc.ViewFeatures
{
- public class ControllerSaveTempDataPropertyFilter : SaveTempDataPropertyFilterBase, IActionFilter
+ internal class ControllerSaveTempDataPropertyFilter : SaveTempDataPropertyFilterBase, IActionFilter
{
public ControllerSaveTempDataPropertyFilter(ITempDataDictionaryFactory factory)
: base(factory)
@@ -20,10 +21,9 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
public void OnActionExecuting(ActionExecutingContext context)
{
Subject = context.Controller;
- var tempData = _factory.GetTempData(context.HttpContext);
+ var tempData = _tempDataFactory.GetTempData(context.HttpContext);
- SetPropertyVaules(tempData, Subject);
+ SetPropertyVaules(tempData);
}
}
}
-
diff --git a/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/ControllerSaveTempDataPropertyFilterFactory.cs b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/ControllerSaveTempDataPropertyFilterFactory.cs
index 98e1272b21..22b7754f72 100644
--- a/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/ControllerSaveTempDataPropertyFilterFactory.cs
+++ b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/ControllerSaveTempDataPropertyFilterFactory.cs
@@ -4,13 +4,19 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc.Filters;
+using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
using Microsoft.Extensions.DependencyInjection;
-namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
+namespace Microsoft.AspNetCore.Mvc.ViewFeatures
{
- public class ControllerSaveTempDataPropertyFilterFactory : IFilterFactory
+ internal class ControllerSaveTempDataPropertyFilterFactory : IFilterFactory
{
- public IList TempDataProperties { get; set; }
+ public ControllerSaveTempDataPropertyFilterFactory(IReadOnlyList properties)
+ {
+ TempDataProperties = properties;
+ }
+
+ public IReadOnlyList TempDataProperties { get; }
public bool IsReusable => false;
diff --git a/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/LifecycleProperty.cs b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/LifecycleProperty.cs
new file mode 100644
index 0000000000..5eaf6e1491
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/LifecycleProperty.cs
@@ -0,0 +1,39 @@
+// 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 System.Diagnostics;
+using System.Reflection;
+using Microsoft.Extensions.Internal;
+
+namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
+{
+ [DebuggerDisplay("{PropertyInfo, nq}")]
+ public readonly struct LifecycleProperty
+ {
+ private readonly PropertyHelper _propertyHelper;
+ private readonly bool _isReferenceTypeOrNullable;
+
+ public LifecycleProperty(PropertyInfo propertyInfo, string key)
+ {
+ Key = key;
+ _propertyHelper = new PropertyHelper(propertyInfo);
+ var propertyType = propertyInfo.PropertyType;
+ _isReferenceTypeOrNullable = !propertyType.IsValueType || Nullable.GetUnderlyingType(propertyType) != null;
+ }
+
+ public string Key { get; }
+
+ public PropertyInfo PropertyInfo => _propertyHelper.Property;
+
+ public object GetValue(object instance) => _propertyHelper.GetValue(instance);
+
+ public void SetValue(object instance, object value)
+ {
+ if (value != null || _isReferenceTypeOrNullable)
+ {
+ _propertyHelper.SetValue(instance, value);
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/SaveTempDataPropertyFilterBase.cs b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/SaveTempDataPropertyFilterBase.cs
index 85643a33e8..c560584798 100644
--- a/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/SaveTempDataPropertyFilterBase.cs
+++ b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/SaveTempDataPropertyFilterBase.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Reflection;
using Microsoft.Extensions.Internal;
@@ -10,14 +11,17 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
{
public abstract class SaveTempDataPropertyFilterBase : ISaveTempDataCallback
{
- protected const string Prefix = "TempDataProperty-";
+ protected readonly ITempDataDictionaryFactory _tempDataFactory;
- protected readonly ITempDataDictionaryFactory _factory;
+ public SaveTempDataPropertyFilterBase(ITempDataDictionaryFactory tempDataFactory)
+ {
+ _tempDataFactory = tempDataFactory;
+ }
///
/// Describes the temp data properties which exist on
///
- public IList Properties { get; set; }
+ public IReadOnlyList Properties { get; set; }
///
/// The which has the temp data properties.
@@ -29,64 +33,90 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
///
public IDictionary OriginalValues { get; } = new Dictionary();
- public SaveTempDataPropertyFilterBase(ITempDataDictionaryFactory factory)
- {
- _factory = factory;
- }
-
///
/// Puts the modified values of into .
///
/// The to be updated.
public void OnTempDataSaving(ITempDataDictionary tempData)
{
- if (Subject != null && Properties != null)
+ if (Subject == null)
{
- for (var i = 0; i < Properties.Count; i++)
- {
- var property = Properties[i];
- OriginalValues.TryGetValue(property.PropertyInfo, out var originalValue);
+ return;
+ }
- var newValue = property.GetValue(Subject);
- if (newValue != null && !newValue.Equals(originalValue))
- {
- tempData[property.TempDataKey] = newValue;
- }
+ for (var i = 0; i < Properties.Count; i++)
+ {
+ var property = Properties[i];
+ OriginalValues.TryGetValue(property.PropertyInfo, out var originalValue);
+
+ var newValue = property.GetValue(Subject);
+ if (newValue != null && !newValue.Equals(originalValue))
+ {
+ tempData[property.Key] = newValue;
}
}
}
- public static IList GetTempDataProperties(Type type)
+ ///
+ /// Sets the values of the properties of from .
+ ///
+ /// The .
+ protected void SetPropertyVaules(ITempDataDictionary tempData)
{
- List results = null;
+ if (Properties == null)
+ {
+ return;
+ }
+
+ Debug.Assert(Subject != null, "Subject must be set before this method is invoked.");
+
+ for (var i = 0; i < Properties.Count; i++)
+ {
+ var property = Properties[i];
+ var value = tempData[property.Key];
+
+ OriginalValues[property.PropertyInfo] = value;
+ property.SetValue(Subject, value);
+ }
+ }
+
+ public static IReadOnlyList GetTempDataProperties(Type type, MvcViewOptions viewOptions)
+ {
+ List results = null;
var propertyHelpers = PropertyHelper.GetVisibleProperties(type: type);
+ var prefix = viewOptions.SuppressTempDataAttributePrefix ?
+ string.Empty :
+ "TempDataProperty-";
for (var i = 0; i < propertyHelpers.Length; i++)
{
var propertyHelper = propertyHelpers[i];
- if (propertyHelper.Property.IsDefined(typeof(TempDataAttribute)))
+ var property = propertyHelper.Property;
+ var tempDataAttribute = property.GetCustomAttribute();
+ if (tempDataAttribute != null)
{
- ValidateProperty(propertyHelper);
+ ValidateProperty(propertyHelper.Property);
if (results == null)
{
- results = new List();
+ results = new List();
}
- results.Add(new TempDataProperty(
- Prefix + propertyHelper.Name,
- propertyHelper.Property,
- propertyHelper.GetValue,
- propertyHelper.SetValue));
+ var key = tempDataAttribute.Key;
+ if (string.IsNullOrEmpty(key))
+ {
+ key = prefix + property.Name;
+ }
+
+ results.Add(new LifecycleProperty(property, key));
}
}
return results;
}
- private static void ValidateProperty(PropertyHelper propertyHelper)
+ private static void ValidateProperty(PropertyInfo property)
{
- var property = propertyHelper.Property;
if (!(property.SetMethod != null &&
property.SetMethod.IsPublic &&
property.GetMethod != null &&
@@ -108,34 +138,5 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
throw new InvalidOperationException($"{messageWithPropertyInfo} {errorMessage}");
}
}
-
- ///
- /// Sets the values of the properties of from .
- ///
- /// The with the data to set on .
- /// The which will have it's properties set.
- protected void SetPropertyVaules(ITempDataDictionary tempData, object subject)
- {
- if (Properties == null)
- {
- return;
- }
-
- for (var i = 0; i < Properties.Count; i++)
- {
- var property = Properties[i];
- var value = tempData[Prefix + property.PropertyInfo.Name];
-
- OriginalValues[property.PropertyInfo] = value;
-
- var propertyTypeInfo = property.PropertyInfo.PropertyType.GetTypeInfo();
-
- var isReferenceTypeOrNullable = !propertyTypeInfo.IsValueType || Nullable.GetUnderlyingType(property.GetType()) != null;
- if (value != null || isReferenceTypeOrNullable)
- {
- property.SetValue(subject, value);
- }
- }
- }
}
}
diff --git a/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/TempDataApplicationModelProvider.cs b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/TempDataApplicationModelProvider.cs
index a1c4e4ec99..7a111b4919 100644
--- a/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/TempDataApplicationModelProvider.cs
+++ b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/TempDataApplicationModelProvider.cs
@@ -4,11 +4,20 @@
using System;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.Internal;
+using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
+using Microsoft.Extensions.Options;
-namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
+namespace Microsoft.AspNetCore.Mvc.ViewFeatures
{
- public class TempDataApplicationModelProvider : IApplicationModelProvider
+ internal class TempDataApplicationModelProvider : IApplicationModelProvider
{
+ private readonly MvcViewOptions _options;
+
+ public TempDataApplicationModelProvider(IOptions options)
+ {
+ _options = options.Value;
+ }
+
///
/// This order ensures that runs after the .
public int Order => -1000 + 10;
@@ -28,19 +37,16 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
foreach (var controllerModel in context.Result.Controllers)
{
-
var modelType = controllerModel.ControllerType.AsType();
- var tempDataProperties = SaveTempDataPropertyFilterBase.GetTempDataProperties(modelType);
- if (tempDataProperties != null)
+ var tempDataProperties = SaveTempDataPropertyFilterBase.GetTempDataProperties(modelType, _options);
+ if (tempDataProperties == null)
{
- var factory = new ControllerSaveTempDataPropertyFilterFactory()
- {
- TempDataProperties = tempDataProperties
- };
-
- controllerModel.Filters.Add(factory);
+ continue;
}
+
+ var filter = new ControllerSaveTempDataPropertyFilterFactory(tempDataProperties);
+ controllerModel.Filters.Add(filter);
}
}
}
diff --git a/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/TempDataProperty.cs b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/TempDataProperty.cs
deleted file mode 100644
index 85a9027b7d..0000000000
--- a/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/TempDataProperty.cs
+++ /dev/null
@@ -1,37 +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 System.Reflection;
-
-namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
-{
- public struct TempDataProperty
- {
- private readonly Func