diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/DependencyInjection/MvcRazorPagesMvcCoreBuilderExtensions.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/DependencyInjection/MvcRazorPagesMvcCoreBuilderExtensions.cs index b16d8e6cd2..ac90544772 100644 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/DependencyInjection/MvcRazorPagesMvcCoreBuilderExtensions.cs +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/DependencyInjection/MvcRazorPagesMvcCoreBuilderExtensions.cs @@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure; using Microsoft.AspNetCore.Mvc.RazorPages.Internal; +using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/PageActionDescriptorProvider.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/PageActionDescriptorProvider.cs index 3b90d9e58e..3066d03b26 100644 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/PageActionDescriptorProvider.cs +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/PageActionDescriptorProvider.cs @@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Routing; +using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal; using Microsoft.AspNetCore.Razor.Evolution; using Microsoft.Extensions.Options; @@ -75,7 +76,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure var index = parentDirectoryPath.LastIndexOf('/'); if (index == -1) { - parentDirectoryPath = string.Empty; + parentDirectoryPath = string.Empty; } else { diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageActionInvoker.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageActionInvoker.cs index 9810871212..041be31f9d 100644 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageActionInvoker.cs +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageActionInvoker.cs @@ -357,7 +357,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal { propertyFilter.OriginalValues = originalValues; propertyFilter.Subject = _page; - propertyFilter.Prefix = TempDataPropertyProvider.Prefix; } IActionResult result = null; diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageActionInvokerProvider.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageActionInvokerProvider.cs index db34a9e27e..4b6aec96ca 100644 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageActionInvokerProvider.cs +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageActionInvokerProvider.cs @@ -15,6 +15,7 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure; using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal; using Microsoft.AspNetCore.Razor.Evolution; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/RazorPagesOptionsSetup.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/RazorPagesOptionsSetup.cs index 3bb0920310..134075a2e4 100644 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/RazorPagesOptionsSetup.cs +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/RazorPagesOptionsSetup.cs @@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal } // Support for [TempData] on properties - options.ConfigureFilter(new SaveTempDataPropertyFilter()); + options.ConfigureFilter(new SaveTempDataPropertyFilterFactory()); // Always require an antiforgery token on post options.ConfigureFilter(new AutoValidateAntiforgeryTokenAttribute()); } diff --git a/src/Microsoft.AspNetCore.Mvc.ViewFeatures/DependencyInjection/MvcViewFeaturesMvcCoreBuilderExtensions.cs b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/DependencyInjection/MvcViewFeaturesMvcCoreBuilderExtensions.cs index 3c4b4e96e8..232bbe532e 100644 --- a/src/Microsoft.AspNetCore.Mvc.ViewFeatures/DependencyInjection/MvcViewFeaturesMvcCoreBuilderExtensions.cs +++ b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/DependencyInjection/MvcViewFeaturesMvcCoreBuilderExtensions.cs @@ -5,6 +5,7 @@ using System; using System.Buffers; using System.Linq; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.AspNetCore.Mvc.ApplicationParts; using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.AspNetCore.Mvc.Internal; @@ -197,6 +198,11 @@ namespace Microsoft.Extensions.DependencyInjection // // Temp Data // + services.TryAddEnumerable( + ServiceDescriptor.Transient()); + services.TryAddSingleton(); + services.TryAddTransient(); + // This does caching so it should stay singleton services.TryAddSingleton(); @@ -208,8 +214,6 @@ namespace Microsoft.Extensions.DependencyInjection // These are stateless so their lifetime isn't really important. services.TryAddSingleton(); - services.TryAddSingleton(); - services.TryAddSingleton(ArrayPool.Shared); services.TryAddScoped(); } diff --git a/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/SaveTempDataFilter.cs b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/SaveTempDataFilter.cs index f5ca08c3ab..413a009cb3 100644 --- a/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/SaveTempDataFilter.cs +++ b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/SaveTempDataFilter.cs @@ -2,8 +2,8 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; -using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Internal; namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal diff --git a/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/SaveTempDataPropertyFilter.cs b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/SaveTempDataPropertyFilter.cs index 82b794e143..4079e6f378 100644 --- a/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/SaveTempDataPropertyFilter.cs +++ b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/SaveTempDataPropertyFilter.cs @@ -1,14 +1,26 @@ // 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.Collections.Generic; using System.Reflection; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.Internal; namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal { - public class SaveTempDataPropertyFilter : ISaveTempDataCallback + public class SaveTempDataPropertyFilter : ISaveTempDataCallback, IActionFilter { - public string Prefix { get; set; } + private const string Prefix = "TempDataProperty-"; + private readonly ITempDataDictionaryFactory _factory; + + public SaveTempDataPropertyFilter(ITempDataDictionaryFactory factory) + { + _factory = factory; + } + + // Cannot be public as PropertyHelper is an internal shared source type + internal IList PropertyHelpers { get; set; } public object Subject { get; set; } @@ -24,12 +36,46 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal var originalValue = kvp.Value; var newValue = property.GetValue(Subject); - if (newValue != null && newValue != originalValue) + if (newValue != null && !newValue.Equals(originalValue)) { tempData[Prefix + property.Name] = newValue; } } } } + + public void OnActionExecuting(ActionExecutingContext context) + { + if (PropertyHelpers == null) + { + throw new ArgumentNullException(nameof(PropertyHelpers)); + } + + Subject = context.Controller; + var tempData = _factory.GetTempData(context.HttpContext); + + OriginalValues = new Dictionary(); + + for (var i = 0; i < PropertyHelpers.Count; i++) + { + var property = PropertyHelpers[i].Property; + var value = tempData[Prefix + property.Name]; + + OriginalValues[property] = value; + + var propertyTypeInfo = property.PropertyType.GetTypeInfo(); + + var isReferenceTypeOrNullable = !propertyTypeInfo.IsValueType || Nullable.GetUnderlyingType(property.GetType()) != null; + if (value != null || isReferenceTypeOrNullable) + { + property.SetValue(Subject, value); + } + } + } + + public void OnActionExecuted(ActionExecutedContext context) + { + } } } + diff --git a/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/SaveTempDataPropertyFilterFactory.cs b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/SaveTempDataPropertyFilterFactory.cs new file mode 100644 index 0000000000..27286dcb91 --- /dev/null +++ b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/SaveTempDataPropertyFilterFactory.cs @@ -0,0 +1,31 @@ +// 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.Collections.Generic; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Internal; + +namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal +{ + public class SaveTempDataPropertyFilterFactory : IFilterFactory + { + // Cannot be public as PropertyHelper is an internal shared source type + internal IList TempDataProperties { get; set; } + + public bool IsReusable => false; + + public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) + { + if (serviceProvider == null) + { + throw new ArgumentNullException(nameof(serviceProvider)); + } + + var service = serviceProvider.GetRequiredService(); + service.PropertyHelpers = TempDataProperties; + return service; + } + } +} diff --git a/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/TempDataApplicationModelProvider.cs b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/TempDataApplicationModelProvider.cs new file mode 100644 index 0000000000..240d88fc3f --- /dev/null +++ b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/TempDataApplicationModelProvider.cs @@ -0,0 +1,80 @@ +// 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.Collections.Generic; +using System.Reflection; +using Microsoft.AspNetCore.Mvc.ApplicationModels; +using Microsoft.AspNetCore.Mvc.Internal; +using Microsoft.Extensions.Internal; + +namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal +{ + public class TempDataApplicationModelProvider : IApplicationModelProvider + { + /// + /// This order ensures that runs after the . + public int Order => -1000 + 10; + + /// + public void OnProvidersExecuted(ApplicationModelProviderContext context) + { + } + + /// + public void OnProvidersExecuting(ApplicationModelProviderContext context) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + foreach (var controllerModel in context.Result.Controllers) + { + SaveTempDataPropertyFilterFactory factory = null; + var propertyHelpers = PropertyHelper.GetVisibleProperties(type: controllerModel.ControllerType.AsType()); + for (var i = 0; i < propertyHelpers.Length; i++) + { + var propertyHelper = propertyHelpers[i]; + if (propertyHelper.Property.IsDefined(typeof(TempDataAttribute))) + { + ValidateProperty(propertyHelper); + if (factory == null) + { + factory = new SaveTempDataPropertyFilterFactory() + { + TempDataProperties = new List() + }; + } + + factory.TempDataProperties.Add(propertyHelper); + } + } + + if (factory != null) + { + controllerModel.Filters.Add(factory); + } + } + } + + private void ValidateProperty(PropertyHelper propertyHelper) + { + var property = propertyHelper.Property; + if (!(property.SetMethod != null && + property.SetMethod.IsPublic && + property.GetMethod != null && + property.GetMethod.IsPublic)) + { + throw new InvalidOperationException( + Resources.FormatTempDataProperties_PublicGetterSetter(property.DeclaringType.FullName, property.Name, nameof(TempDataAttribute))); + } + + if (!(property.PropertyType.GetTypeInfo().IsPrimitive || property.PropertyType == typeof(string))) + { + throw new InvalidOperationException( + Resources.FormatTempDataProperties_PrimitiveTypeOrString(property.DeclaringType.FullName, property.Name, nameof(TempDataAttribute))); + } + } + } +} diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/TempDataPropertyProvider.cs b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/TempDataPropertyProvider.cs similarity index 83% rename from src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/TempDataPropertyProvider.cs rename to src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/TempDataPropertyProvider.cs index 12c20e265e..4a4297746c 100644 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/TempDataPropertyProvider.cs +++ b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/TempDataPropertyProvider.cs @@ -6,10 +6,8 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Reflection; -using Microsoft.AspNetCore.Mvc.Rendering; -using Microsoft.AspNetCore.Mvc.ViewFeatures; -namespace Microsoft.AspNetCore.Mvc.RazorPages +namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal { public class TempDataPropertyProvider { @@ -18,6 +16,12 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages private ConcurrentDictionary> _subjectProperties = new ConcurrentDictionary>(); + /// + /// Loads and tracks any changes to the properties of the . + /// + /// The properties of the subject are loaded and tracked. May be a . + /// The . + /// public IDictionary LoadAndTrackChanges(object subject, ITempDataDictionary tempData) { var properties = GetSubjectProperties(subject); diff --git a/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Properties/Resources.Designer.cs b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Properties/Resources.Designer.cs index 88113eae82..04f1f32312 100644 --- a/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Properties/Resources.Designer.cs @@ -15,897 +15,813 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures /// internal static string ViewComponent_AmbiguousTypeMatch { - get { return GetString("ViewComponent_AmbiguousTypeMatch"); } + get => GetString("ViewComponent_AmbiguousTypeMatch"); } /// /// The view component name '{0}' matched multiple types:{1}{2} /// internal static string FormatViewComponent_AmbiguousTypeMatch(object p0, object p1, object p2) - { - return string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_AmbiguousTypeMatch"), p0, p1, p2); - } + => string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_AmbiguousTypeMatch"), p0, p1, p2); /// /// Method '{0}' of view component '{1}' should be declared to return {2}<T>. /// internal static string ViewComponent_AsyncMethod_ShouldReturnTask { - get { return GetString("ViewComponent_AsyncMethod_ShouldReturnTask"); } + get => GetString("ViewComponent_AsyncMethod_ShouldReturnTask"); } /// /// Method '{0}' of view component '{1}' should be declared to return {2}<T>. /// internal static string FormatViewComponent_AsyncMethod_ShouldReturnTask(object p0, object p1, object p2) - { - return string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_AsyncMethod_ShouldReturnTask"), p0, p1, p2); - } + => string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_AsyncMethod_ShouldReturnTask"), p0, p1, p2); /// /// A view component must return a non-null value. /// internal static string ViewComponent_MustReturnValue { - get { return GetString("ViewComponent_MustReturnValue"); } + get => GetString("ViewComponent_MustReturnValue"); } /// /// A view component must return a non-null value. /// internal static string FormatViewComponent_MustReturnValue() - { - return GetString("ViewComponent_MustReturnValue"); - } + => GetString("ViewComponent_MustReturnValue"); /// /// Method '{0}' of view component '{1}' should be declared to return a value. /// internal static string ViewComponent_SyncMethod_ShouldReturnValue { - get { return GetString("ViewComponent_SyncMethod_ShouldReturnValue"); } + get => GetString("ViewComponent_SyncMethod_ShouldReturnValue"); } /// /// Method '{0}' of view component '{1}' should be declared to return a value. /// internal static string FormatViewComponent_SyncMethod_ShouldReturnValue(object p0, object p1) - { - return string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_SyncMethod_ShouldReturnValue"), p0, p1); - } + => string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_SyncMethod_ShouldReturnValue"), p0, p1); /// /// A view component named '{0}' could not be found. /// internal static string ViewComponent_CannotFindComponent { - get { return GetString("ViewComponent_CannotFindComponent"); } + get => GetString("ViewComponent_CannotFindComponent"); } /// /// A view component named '{0}' could not be found. /// internal static string FormatViewComponent_CannotFindComponent(object p0) - { - return string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_CannotFindComponent"), p0); - } + => string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_CannotFindComponent"), p0); /// /// An invoker could not be created for the view component '{0}'. /// internal static string ViewComponent_IViewComponentFactory_ReturnedNull { - get { return GetString("ViewComponent_IViewComponentFactory_ReturnedNull"); } + get => GetString("ViewComponent_IViewComponentFactory_ReturnedNull"); } /// /// An invoker could not be created for the view component '{0}'. /// internal static string FormatViewComponent_IViewComponentFactory_ReturnedNull(object p0) - { - return string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_IViewComponentFactory_ReturnedNull"), p0); - } + => string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_IViewComponentFactory_ReturnedNull"), p0); /// /// Could not find an '{0}' or '{1}' method for the view component '{2}'. /// internal static string ViewComponent_CannotFindMethod { - get { return GetString("ViewComponent_CannotFindMethod"); } + get => GetString("ViewComponent_CannotFindMethod"); } /// /// Could not find an '{0}' or '{1}' method for the view component '{2}'. /// internal static string FormatViewComponent_CannotFindMethod(object p0, object p1, object p2) - { - return string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_CannotFindMethod"), p0, p1, p2); - } + => string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_CannotFindMethod"), p0, p1, p2); /// /// View components only support returning {0}, {1} or {2}. /// internal static string ViewComponent_InvalidReturnValue { - get { return GetString("ViewComponent_InvalidReturnValue"); } + get => GetString("ViewComponent_InvalidReturnValue"); } /// /// View components only support returning {0}, {1} or {2}. /// internal static string FormatViewComponent_InvalidReturnValue(object p0, object p1, object p2) - { - return string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_InvalidReturnValue"), p0, p1, p2); - } + => string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_InvalidReturnValue"), p0, p1, p2); /// /// Value cannot be null or empty. /// internal static string ArgumentCannotBeNullOrEmpty { - get { return GetString("ArgumentCannotBeNullOrEmpty"); } + get => GetString("ArgumentCannotBeNullOrEmpty"); } /// /// Value cannot be null or empty. /// internal static string FormatArgumentCannotBeNullOrEmpty() - { - return GetString("ArgumentCannotBeNullOrEmpty"); - } + => GetString("ArgumentCannotBeNullOrEmpty"); /// /// The '{0}' property of '{1}' must not be null. /// internal static string PropertyOfTypeCannotBeNull { - get { return GetString("PropertyOfTypeCannotBeNull"); } + get => GetString("PropertyOfTypeCannotBeNull"); } /// /// The '{0}' property of '{1}' must not be null. /// internal static string FormatPropertyOfTypeCannotBeNull(object p0, object p1) - { - return string.Format(CultureInfo.CurrentCulture, GetString("PropertyOfTypeCannotBeNull"), p0, p1); - } + => string.Format(CultureInfo.CurrentCulture, GetString("PropertyOfTypeCannotBeNull"), p0, p1); /// /// The '{0}' method of type '{1}' cannot return a null value. /// internal static string TypeMethodMustReturnNotNullValue { - get { return GetString("TypeMethodMustReturnNotNullValue"); } + get => GetString("TypeMethodMustReturnNotNullValue"); } /// /// The '{0}' method of type '{1}' cannot return a null value. /// internal static string FormatTypeMethodMustReturnNotNullValue(object p0, object p1) - { - return string.Format(CultureInfo.CurrentCulture, GetString("TypeMethodMustReturnNotNullValue"), p0, p1); - } + => string.Format(CultureInfo.CurrentCulture, GetString("TypeMethodMustReturnNotNullValue"), p0, p1); /// /// Property '{0}' is of type '{1}', but this method requires a value of type '{2}'. /// internal static string ArgumentPropertyUnexpectedType { - get { return GetString("ArgumentPropertyUnexpectedType"); } + get => GetString("ArgumentPropertyUnexpectedType"); } /// /// Property '{0}' is of type '{1}', but this method requires a value of type '{2}'. /// internal static string FormatArgumentPropertyUnexpectedType(object p0, object p1, object p2) - { - return string.Format(CultureInfo.CurrentCulture, GetString("ArgumentPropertyUnexpectedType"), p0, p1, p2); - } + => string.Format(CultureInfo.CurrentCulture, GetString("ArgumentPropertyUnexpectedType"), p0, p1, p2); /// /// The partial view '{0}' was not found or no view engine supports the searched locations. The following locations were searched:{1} /// internal static string Common_PartialViewNotFound { - get { return GetString("Common_PartialViewNotFound"); } + get => GetString("Common_PartialViewNotFound"); } /// /// The partial view '{0}' was not found or no view engine supports the searched locations. The following locations were searched:{1} /// internal static string FormatCommon_PartialViewNotFound(object p0, object p1) - { - return string.Format(CultureInfo.CurrentCulture, GetString("Common_PartialViewNotFound"), p0, p1); - } + => string.Format(CultureInfo.CurrentCulture, GetString("Common_PartialViewNotFound"), p0, p1); /// /// False /// internal static string Common_TriState_False { - get { return GetString("Common_TriState_False"); } + get => GetString("Common_TriState_False"); } /// /// False /// internal static string FormatCommon_TriState_False() - { - return GetString("Common_TriState_False"); - } + => GetString("Common_TriState_False"); /// /// Not Set /// internal static string Common_TriState_NotSet { - get { return GetString("Common_TriState_NotSet"); } + get => GetString("Common_TriState_NotSet"); } /// /// Not Set /// internal static string FormatCommon_TriState_NotSet() - { - return GetString("Common_TriState_NotSet"); - } + => GetString("Common_TriState_NotSet"); /// /// True /// internal static string Common_TriState_True { - get { return GetString("Common_TriState_True"); } + get => GetString("Common_TriState_True"); } /// /// True /// internal static string FormatCommon_TriState_True() - { - return GetString("Common_TriState_True"); - } + => GetString("Common_TriState_True"); /// /// ViewData value must not be null. /// internal static string DynamicViewData_ViewDataNull { - get { return GetString("DynamicViewData_ViewDataNull"); } + get => GetString("DynamicViewData_ViewDataNull"); } /// /// ViewData value must not be null. /// internal static string FormatDynamicViewData_ViewDataNull() - { - return GetString("DynamicViewData_ViewDataNull"); - } + => GetString("DynamicViewData_ViewDataNull"); /// /// The expression compiler was unable to evaluate the indexer expression '{0}' because it references the model parameter '{1}' which is unavailable. /// internal static string ExpressionHelper_InvalidIndexerExpression { - get { return GetString("ExpressionHelper_InvalidIndexerExpression"); } + get => GetString("ExpressionHelper_InvalidIndexerExpression"); } /// /// The expression compiler was unable to evaluate the indexer expression '{0}' because it references the model parameter '{1}' which is unavailable. /// internal static string FormatExpressionHelper_InvalidIndexerExpression(object p0, object p1) - { - return string.Format(CultureInfo.CurrentCulture, GetString("ExpressionHelper_InvalidIndexerExpression"), p0, p1); - } + => string.Format(CultureInfo.CurrentCulture, GetString("ExpressionHelper_InvalidIndexerExpression"), p0, p1); /// /// The IModelMetadataProvider was unable to provide metadata for expression '{0}'. /// internal static string HtmlHelper_NullModelMetadata { - get { return GetString("HtmlHelper_NullModelMetadata"); } + get => GetString("HtmlHelper_NullModelMetadata"); } /// /// The IModelMetadataProvider was unable to provide metadata for expression '{0}'. /// internal static string FormatHtmlHelper_NullModelMetadata(object p0) - { - return string.Format(CultureInfo.CurrentCulture, GetString("HtmlHelper_NullModelMetadata"), p0); - } + => string.Format(CultureInfo.CurrentCulture, GetString("HtmlHelper_NullModelMetadata"), p0); /// /// Must call 'Contextualize' method before using this HtmlHelper instance. /// internal static string HtmlHelper_NotContextualized { - get { return GetString("HtmlHelper_NotContextualized"); } + get => GetString("HtmlHelper_NotContextualized"); } /// /// Must call 'Contextualize' method before using this HtmlHelper instance. /// internal static string FormatHtmlHelper_NotContextualized() - { - return GetString("HtmlHelper_NotContextualized"); - } + => GetString("HtmlHelper_NotContextualized"); /// /// There is no ViewData item of type '{0}' that has the key '{1}'. /// internal static string HtmlHelper_MissingSelectData { - get { return GetString("HtmlHelper_MissingSelectData"); } + get => GetString("HtmlHelper_MissingSelectData"); } /// /// There is no ViewData item of type '{0}' that has the key '{1}'. /// internal static string FormatHtmlHelper_MissingSelectData(object p0, object p1) - { - return string.Format(CultureInfo.CurrentCulture, GetString("HtmlHelper_MissingSelectData"), p0, p1); - } + => string.Format(CultureInfo.CurrentCulture, GetString("HtmlHelper_MissingSelectData"), p0, p1); /// /// The parameter '{0}' must evaluate to an IEnumerable when multiple selection is allowed. /// internal static string HtmlHelper_SelectExpressionNotEnumerable { - get { return GetString("HtmlHelper_SelectExpressionNotEnumerable"); } + get => GetString("HtmlHelper_SelectExpressionNotEnumerable"); } /// /// The parameter '{0}' must evaluate to an IEnumerable when multiple selection is allowed. /// internal static string FormatHtmlHelper_SelectExpressionNotEnumerable(object p0) - { - return string.Format(CultureInfo.CurrentCulture, GetString("HtmlHelper_SelectExpressionNotEnumerable"), p0); - } + => string.Format(CultureInfo.CurrentCulture, GetString("HtmlHelper_SelectExpressionNotEnumerable"), p0); /// /// The type '{0}' is not supported. Type must be an {1} that does not have an associated {2}. /// internal static string HtmlHelper_TypeNotSupported_ForGetEnumSelectList { - get { return GetString("HtmlHelper_TypeNotSupported_ForGetEnumSelectList"); } + get => GetString("HtmlHelper_TypeNotSupported_ForGetEnumSelectList"); } /// /// The type '{0}' is not supported. Type must be an {1} that does not have an associated {2}. /// internal static string FormatHtmlHelper_TypeNotSupported_ForGetEnumSelectList(object p0, object p1, object p2) - { - return string.Format(CultureInfo.CurrentCulture, GetString("HtmlHelper_TypeNotSupported_ForGetEnumSelectList"), p0, p1, p2); - } + => string.Format(CultureInfo.CurrentCulture, GetString("HtmlHelper_TypeNotSupported_ForGetEnumSelectList"), p0, p1, p2); /// /// The ViewData item that has the key '{0}' is of type '{1}' but must be of type '{2}'. /// internal static string HtmlHelper_WrongSelectDataType { - get { return GetString("HtmlHelper_WrongSelectDataType"); } + get => GetString("HtmlHelper_WrongSelectDataType"); } /// /// The ViewData item that has the key '{0}' is of type '{1}' but must be of type '{2}'. /// internal static string FormatHtmlHelper_WrongSelectDataType(object p0, object p1, object p2) - { - return string.Format(CultureInfo.CurrentCulture, GetString("HtmlHelper_WrongSelectDataType"), p0, p1, p2); - } + => string.Format(CultureInfo.CurrentCulture, GetString("HtmlHelper_WrongSelectDataType"), p0, p1, p2); /// /// The '{0}' template was used with an object of type '{1}', which does not implement '{2}'. /// internal static string Templates_TypeMustImplementIEnumerable { - get { return GetString("Templates_TypeMustImplementIEnumerable"); } + get => GetString("Templates_TypeMustImplementIEnumerable"); } /// /// The '{0}' template was used with an object of type '{1}', which does not implement '{2}'. /// internal static string FormatTemplates_TypeMustImplementIEnumerable(object p0, object p1, object p2) - { - return string.Format(CultureInfo.CurrentCulture, GetString("Templates_TypeMustImplementIEnumerable"), p0, p1, p2); - } + => string.Format(CultureInfo.CurrentCulture, GetString("Templates_TypeMustImplementIEnumerable"), p0, p1, p2); /// /// Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions. /// internal static string TemplateHelpers_TemplateLimitations { - get { return GetString("TemplateHelpers_TemplateLimitations"); } + get => GetString("TemplateHelpers_TemplateLimitations"); } /// /// Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions. /// internal static string FormatTemplateHelpers_TemplateLimitations() - { - return GetString("TemplateHelpers_TemplateLimitations"); - } + => GetString("TemplateHelpers_TemplateLimitations"); /// /// Unable to locate an appropriate template for type {0}. /// internal static string TemplateHelpers_NoTemplate { - get { return GetString("TemplateHelpers_NoTemplate"); } + get => GetString("TemplateHelpers_NoTemplate"); } /// /// Unable to locate an appropriate template for type {0}. /// internal static string FormatTemplateHelpers_NoTemplate(object p0) - { - return string.Format(CultureInfo.CurrentCulture, GetString("TemplateHelpers_NoTemplate"), p0); - } + => string.Format(CultureInfo.CurrentCulture, GetString("TemplateHelpers_NoTemplate"), p0); /// /// The model item passed is null, but this ViewDataDictionary instance requires a non-null model item of type '{0}'. /// internal static string ViewData_ModelCannotBeNull { - get { return GetString("ViewData_ModelCannotBeNull"); } + get => GetString("ViewData_ModelCannotBeNull"); } /// /// The model item passed is null, but this ViewDataDictionary instance requires a non-null model item of type '{0}'. /// internal static string FormatViewData_ModelCannotBeNull(object p0) - { - return string.Format(CultureInfo.CurrentCulture, GetString("ViewData_ModelCannotBeNull"), p0); - } + => string.Format(CultureInfo.CurrentCulture, GetString("ViewData_ModelCannotBeNull"), p0); /// /// The model item passed into the ViewDataDictionary is of type '{0}', but this ViewDataDictionary instance requires a model item of type '{1}'. /// internal static string ViewData_WrongTModelType { - get { return GetString("ViewData_WrongTModelType"); } + get => GetString("ViewData_WrongTModelType"); } /// /// The model item passed into the ViewDataDictionary is of type '{0}', but this ViewDataDictionary instance requires a model item of type '{1}'. /// internal static string FormatViewData_WrongTModelType(object p0, object p1) - { - return string.Format(CultureInfo.CurrentCulture, GetString("ViewData_WrongTModelType"), p0, p1); - } + => string.Format(CultureInfo.CurrentCulture, GetString("ViewData_WrongTModelType"), p0, p1); /// /// The partial view '{0}' was not found. The following locations were searched:{1} /// internal static string ViewEngine_PartialViewNotFound { - get { return GetString("ViewEngine_PartialViewNotFound"); } + get => GetString("ViewEngine_PartialViewNotFound"); } /// /// The partial view '{0}' was not found. The following locations were searched:{1} /// internal static string FormatViewEngine_PartialViewNotFound(object p0, object p1) - { - return string.Format(CultureInfo.CurrentCulture, GetString("ViewEngine_PartialViewNotFound"), p0, p1); - } + => string.Format(CultureInfo.CurrentCulture, GetString("ViewEngine_PartialViewNotFound"), p0, p1); /// /// The view '{0}' was not found. The following locations were searched:{1} /// internal static string ViewEngine_ViewNotFound { - get { return GetString("ViewEngine_ViewNotFound"); } + get => GetString("ViewEngine_ViewNotFound"); } /// /// The view '{0}' was not found. The following locations were searched:{1} /// internal static string FormatViewEngine_ViewNotFound(object p0, object p1) - { - return string.Format(CultureInfo.CurrentCulture, GetString("ViewEngine_ViewNotFound"), p0, p1); - } + => string.Format(CultureInfo.CurrentCulture, GetString("ViewEngine_ViewNotFound"), p0, p1); /// /// The value must be greater than or equal to zero. /// internal static string HtmlHelper_TextAreaParameterOutOfRange { - get { return GetString("HtmlHelper_TextAreaParameterOutOfRange"); } + get => GetString("HtmlHelper_TextAreaParameterOutOfRange"); } /// /// The value must be greater than or equal to zero. /// internal static string FormatHtmlHelper_TextAreaParameterOutOfRange() - { - return GetString("HtmlHelper_TextAreaParameterOutOfRange"); - } + => GetString("HtmlHelper_TextAreaParameterOutOfRange"); /// /// Validation parameter names in unobtrusive client validation rules cannot be empty. Client rule type: {0} /// internal static string UnobtrusiveJavascript_ValidationParameterCannotBeEmpty { - get { return GetString("UnobtrusiveJavascript_ValidationParameterCannotBeEmpty"); } + get => GetString("UnobtrusiveJavascript_ValidationParameterCannotBeEmpty"); } /// /// Validation parameter names in unobtrusive client validation rules cannot be empty. Client rule type: {0} /// internal static string FormatUnobtrusiveJavascript_ValidationParameterCannotBeEmpty(object p0) - { - return string.Format(CultureInfo.CurrentCulture, GetString("UnobtrusiveJavascript_ValidationParameterCannotBeEmpty"), p0); - } + => string.Format(CultureInfo.CurrentCulture, GetString("UnobtrusiveJavascript_ValidationParameterCannotBeEmpty"), p0); /// /// Validation parameter names in unobtrusive client validation rules must start with a lowercase letter and consist of only lowercase letters or digits. Validation parameter name: {0}, client rule type: {1} /// internal static string UnobtrusiveJavascript_ValidationParameterMustBeLegal { - get { return GetString("UnobtrusiveJavascript_ValidationParameterMustBeLegal"); } + get => GetString("UnobtrusiveJavascript_ValidationParameterMustBeLegal"); } /// /// Validation parameter names in unobtrusive client validation rules must start with a lowercase letter and consist of only lowercase letters or digits. Validation parameter name: {0}, client rule type: {1} /// internal static string FormatUnobtrusiveJavascript_ValidationParameterMustBeLegal(object p0, object p1) - { - return string.Format(CultureInfo.CurrentCulture, GetString("UnobtrusiveJavascript_ValidationParameterMustBeLegal"), p0, p1); - } + => string.Format(CultureInfo.CurrentCulture, GetString("UnobtrusiveJavascript_ValidationParameterMustBeLegal"), p0, p1); /// /// Validation type names in unobtrusive client validation rules cannot be empty. Client rule type: {0} /// internal static string UnobtrusiveJavascript_ValidationTypeCannotBeEmpty { - get { return GetString("UnobtrusiveJavascript_ValidationTypeCannotBeEmpty"); } + get => GetString("UnobtrusiveJavascript_ValidationTypeCannotBeEmpty"); } /// /// Validation type names in unobtrusive client validation rules cannot be empty. Client rule type: {0} /// internal static string FormatUnobtrusiveJavascript_ValidationTypeCannotBeEmpty(object p0) - { - return string.Format(CultureInfo.CurrentCulture, GetString("UnobtrusiveJavascript_ValidationTypeCannotBeEmpty"), p0); - } + => string.Format(CultureInfo.CurrentCulture, GetString("UnobtrusiveJavascript_ValidationTypeCannotBeEmpty"), p0); /// /// Validation type names in unobtrusive client validation rules must consist of only lowercase letters. Invalid name: "{0}", client rule type: {1} /// internal static string UnobtrusiveJavascript_ValidationTypeMustBeLegal { - get { return GetString("UnobtrusiveJavascript_ValidationTypeMustBeLegal"); } + get => GetString("UnobtrusiveJavascript_ValidationTypeMustBeLegal"); } /// /// Validation type names in unobtrusive client validation rules must consist of only lowercase letters. Invalid name: "{0}", client rule type: {1} /// internal static string FormatUnobtrusiveJavascript_ValidationTypeMustBeLegal(object p0, object p1) - { - return string.Format(CultureInfo.CurrentCulture, GetString("UnobtrusiveJavascript_ValidationTypeMustBeLegal"), p0, p1); - } + => string.Format(CultureInfo.CurrentCulture, GetString("UnobtrusiveJavascript_ValidationTypeMustBeLegal"), p0, p1); /// /// Validation type names in unobtrusive client validation rules must be unique. The following validation type was seen more than once: {0} /// internal static string UnobtrusiveJavascript_ValidationTypeMustBeUnique { - get { return GetString("UnobtrusiveJavascript_ValidationTypeMustBeUnique"); } + get => GetString("UnobtrusiveJavascript_ValidationTypeMustBeUnique"); } /// /// Validation type names in unobtrusive client validation rules must be unique. The following validation type was seen more than once: {0} /// internal static string FormatUnobtrusiveJavascript_ValidationTypeMustBeUnique(object p0) - { - return string.Format(CultureInfo.CurrentCulture, GetString("UnobtrusiveJavascript_ValidationTypeMustBeUnique"), p0); - } + => string.Format(CultureInfo.CurrentCulture, GetString("UnobtrusiveJavascript_ValidationTypeMustBeUnique"), p0); /// /// The type '{0}' must derive from '{1}'. /// internal static string TypeMustDeriveFromType { - get { return GetString("TypeMustDeriveFromType"); } + get => GetString("TypeMustDeriveFromType"); } /// /// The type '{0}' must derive from '{1}'. /// internal static string FormatTypeMustDeriveFromType(object p0, object p1) - { - return string.Format(CultureInfo.CurrentCulture, GetString("TypeMustDeriveFromType"), p0, p1); - } + => string.Format(CultureInfo.CurrentCulture, GetString("TypeMustDeriveFromType"), p0, p1); /// /// Could not find a replacement for view expansion token '{0}'. /// internal static string TemplatedViewLocationExpander_NoReplacementToken { - get { return GetString("TemplatedViewLocationExpander_NoReplacementToken"); } + get => GetString("TemplatedViewLocationExpander_NoReplacementToken"); } /// /// Could not find a replacement for view expansion token '{0}'. /// internal static string FormatTemplatedViewLocationExpander_NoReplacementToken(object p0) - { - return string.Format(CultureInfo.CurrentCulture, GetString("TemplatedViewLocationExpander_NoReplacementToken"), p0); - } + => string.Format(CultureInfo.CurrentCulture, GetString("TemplatedViewLocationExpander_NoReplacementToken"), p0); /// /// {0} must be executed before {1} can be invoked. /// internal static string TemplatedExpander_PopulateValuesMustBeInvokedFirst { - get { return GetString("TemplatedExpander_PopulateValuesMustBeInvokedFirst"); } + get => GetString("TemplatedExpander_PopulateValuesMustBeInvokedFirst"); } /// /// {0} must be executed before {1} can be invoked. /// internal static string FormatTemplatedExpander_PopulateValuesMustBeInvokedFirst(object p0, object p1) - { - return string.Format(CultureInfo.CurrentCulture, GetString("TemplatedExpander_PopulateValuesMustBeInvokedFirst"), p0, p1); - } + => string.Format(CultureInfo.CurrentCulture, GetString("TemplatedExpander_PopulateValuesMustBeInvokedFirst"), p0, p1); /// /// The result of value factory cannot be null. /// internal static string TemplatedExpander_ValueFactoryCannotReturnNull { - get { return GetString("TemplatedExpander_ValueFactoryCannotReturnNull"); } + get => GetString("TemplatedExpander_ValueFactoryCannotReturnNull"); } /// /// The result of value factory cannot be null. /// internal static string FormatTemplatedExpander_ValueFactoryCannotReturnNull() - { - return GetString("TemplatedExpander_ValueFactoryCannotReturnNull"); - } + => GetString("TemplatedExpander_ValueFactoryCannotReturnNull"); /// /// Type: '{0}' - Name: '{1}' /// internal static string ViewComponent_AmbiguousTypeMatch_Item { - get { return GetString("ViewComponent_AmbiguousTypeMatch_Item"); } + get => GetString("ViewComponent_AmbiguousTypeMatch_Item"); } /// /// Type: '{0}' - Name: '{1}' /// internal static string FormatViewComponent_AmbiguousTypeMatch_Item(object p0, object p1) - { - return string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_AmbiguousTypeMatch_Item"), p0, p1); - } + => string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_AmbiguousTypeMatch_Item"), p0, p1); /// /// The property {0}.{1} could not be found. /// internal static string Common_PropertyNotFound { - get { return GetString("Common_PropertyNotFound"); } + get => GetString("Common_PropertyNotFound"); } /// /// The property {0}.{1} could not be found. /// internal static string FormatCommon_PropertyNotFound(object p0, object p1) - { - return string.Format(CultureInfo.CurrentCulture, GetString("Common_PropertyNotFound"), p0, p1); - } + => string.Format(CultureInfo.CurrentCulture, GetString("Common_PropertyNotFound"), p0, p1); /// /// No URL for remote validation could be found. /// internal static string RemoteAttribute_NoUrlFound { - get { return GetString("RemoteAttribute_NoUrlFound"); } + get => GetString("RemoteAttribute_NoUrlFound"); } /// /// No URL for remote validation could be found. /// internal static string FormatRemoteAttribute_NoUrlFound() - { - return GetString("RemoteAttribute_NoUrlFound"); - } + => GetString("RemoteAttribute_NoUrlFound"); /// /// '{0}' is invalid. /// internal static string RemoteAttribute_RemoteValidationFailed { - get { return GetString("RemoteAttribute_RemoteValidationFailed"); } + get => GetString("RemoteAttribute_RemoteValidationFailed"); } /// /// '{0}' is invalid. /// internal static string FormatRemoteAttribute_RemoteValidationFailed(object p0) - { - return string.Format(CultureInfo.CurrentCulture, GetString("RemoteAttribute_RemoteValidationFailed"), p0); - } + => string.Format(CultureInfo.CurrentCulture, GetString("RemoteAttribute_RemoteValidationFailed"), p0); /// /// The name of an HTML field cannot be null or empty. Instead use methods {0}.{1} or {2}.{3} with a non-empty {4} argument value. /// internal static string HtmlGenerator_FieldNameCannotBeNullOrEmpty { - get { return GetString("HtmlGenerator_FieldNameCannotBeNullOrEmpty"); } + get => GetString("HtmlGenerator_FieldNameCannotBeNullOrEmpty"); } /// /// The name of an HTML field cannot be null or empty. Instead use methods {0}.{1} or {2}.{3} with a non-empty {4} argument value. /// internal static string FormatHtmlGenerator_FieldNameCannotBeNullOrEmpty(object p0, object p1, object p2, object p3, object p4) - { - return string.Format(CultureInfo.CurrentCulture, GetString("HtmlGenerator_FieldNameCannotBeNullOrEmpty"), p0, p1, p2, p3, p4); - } + => string.Format(CultureInfo.CurrentCulture, GetString("HtmlGenerator_FieldNameCannotBeNullOrEmpty"), p0, p1, p2, p3, p4); /// /// Either the '{0}' or '{1}' property must be set in order to invoke a view component. /// internal static string ViewComponentResult_NameOrTypeMustBeSet { - get { return GetString("ViewComponentResult_NameOrTypeMustBeSet"); } + get => GetString("ViewComponentResult_NameOrTypeMustBeSet"); } /// /// Either the '{0}' or '{1}' property must be set in order to invoke a view component. /// internal static string FormatViewComponentResult_NameOrTypeMustBeSet(object p0, object p1) - { - return string.Format(CultureInfo.CurrentCulture, GetString("ViewComponentResult_NameOrTypeMustBeSet"), p0, p1); - } + => string.Format(CultureInfo.CurrentCulture, GetString("ViewComponentResult_NameOrTypeMustBeSet"), p0, p1); /// /// Cannot deserialize {0} of type '{1}'. /// internal static string TempData_CannotDeserializeToken { - get { return GetString("TempData_CannotDeserializeToken"); } + get => GetString("TempData_CannotDeserializeToken"); } /// /// Cannot deserialize {0} of type '{1}'. /// internal static string FormatTempData_CannotDeserializeToken(object p0, object p1) - { - return string.Format(CultureInfo.CurrentCulture, GetString("TempData_CannotDeserializeToken"), p0, p1); - } + => string.Format(CultureInfo.CurrentCulture, GetString("TempData_CannotDeserializeToken"), p0, p1); /// /// The '{0}' cannot serialize a dictionary with a key of type '{1}'. /// internal static string TempData_CannotSerializeDictionary { - get { return GetString("TempData_CannotSerializeDictionary"); } + get => GetString("TempData_CannotSerializeDictionary"); } /// /// The '{0}' cannot serialize a dictionary with a key of type '{1}'. /// internal static string FormatTempData_CannotSerializeDictionary(object p0, object p1) - { - return string.Format(CultureInfo.CurrentCulture, GetString("TempData_CannotSerializeDictionary"), p0, p1); - } + => string.Format(CultureInfo.CurrentCulture, GetString("TempData_CannotSerializeDictionary"), p0, p1); /// /// The '{0}' cannot serialize an object of type '{1}'. /// internal static string TempData_CannotSerializeType { - get { return GetString("TempData_CannotSerializeType"); } + get => GetString("TempData_CannotSerializeType"); } /// /// The '{0}' cannot serialize an object of type '{1}'. /// internal static string FormatTempData_CannotSerializeType(object p0, object p1) - { - return string.Format(CultureInfo.CurrentCulture, GetString("TempData_CannotSerializeType"), p0, p1); - } + => string.Format(CultureInfo.CurrentCulture, GetString("TempData_CannotSerializeType"), p0, p1); /// /// The collection already contains an entry with key '{0}'. /// internal static string Dictionary_DuplicateKey { - get { return GetString("Dictionary_DuplicateKey"); } + get => GetString("Dictionary_DuplicateKey"); } /// /// The collection already contains an entry with key '{0}'. /// internal static string FormatDictionary_DuplicateKey(object p0) - { - return string.Format(CultureInfo.CurrentCulture, GetString("Dictionary_DuplicateKey"), p0); - } + => string.Format(CultureInfo.CurrentCulture, GetString("Dictionary_DuplicateKey"), p0); /// /// Method '{0}' of view component '{1}' cannot return a {2}. /// internal static string ViewComponent_SyncMethod_CannotReturnTask { - get { return GetString("ViewComponent_SyncMethod_CannotReturnTask"); } + get => GetString("ViewComponent_SyncMethod_CannotReturnTask"); } /// /// Method '{0}' of view component '{1}' cannot return a {2}. /// internal static string FormatViewComponent_SyncMethod_CannotReturnTask(object p0, object p1, object p2) - { - return string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_SyncMethod_CannotReturnTask"), p0, p1, p2); - } + => string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_SyncMethod_CannotReturnTask"), p0, p1, p2); /// /// View component '{0}' must have exactly one public method named '{1}' or '{2}'. /// internal static string ViewComponent_AmbiguousMethods { - get { return GetString("ViewComponent_AmbiguousMethods"); } + get => GetString("ViewComponent_AmbiguousMethods"); } /// /// View component '{0}' must have exactly one public method named '{1}' or '{2}'. /// internal static string FormatViewComponent_AmbiguousMethods(object p0, object p1, object p2) - { - return string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_AmbiguousMethods"), p0, p1, p2); - } + => string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_AmbiguousMethods"), p0, p1, p2); /// /// The type '{0}' cannot be activated by '{1}' because it is either a value type, an interface, an abstract class or an open generic type. /// internal static string ValueInterfaceAbstractOrOpenGenericTypesCannotBeActivated { - get { return GetString("ValueInterfaceAbstractOrOpenGenericTypesCannotBeActivated"); } + get => GetString("ValueInterfaceAbstractOrOpenGenericTypesCannotBeActivated"); } /// /// The type '{0}' cannot be activated by '{1}' because it is either a value type, an interface, an abstract class or an open generic type. /// internal static string FormatValueInterfaceAbstractOrOpenGenericTypesCannotBeActivated(object p0, object p1) - { - return string.Format(CultureInfo.CurrentCulture, GetString("ValueInterfaceAbstractOrOpenGenericTypesCannotBeActivated"), p0, p1); - } + => string.Format(CultureInfo.CurrentCulture, GetString("ValueInterfaceAbstractOrOpenGenericTypesCannotBeActivated"), p0, p1); /// /// The {0} was unable to provide metadata for expression '{1}'. /// internal static string CreateModelExpression_NullModelMetadata { - get { return GetString("CreateModelExpression_NullModelMetadata"); } + get => GetString("CreateModelExpression_NullModelMetadata"); } /// /// The {0} was unable to provide metadata for expression '{1}'. /// internal static string FormatCreateModelExpression_NullModelMetadata(object p0, object p1) - { - return string.Format(CultureInfo.CurrentCulture, GetString("CreateModelExpression_NullModelMetadata"), p0, p1); - } + => string.Format(CultureInfo.CurrentCulture, GetString("CreateModelExpression_NullModelMetadata"), p0, p1); /// /// '{0}.{1}' must not be empty. At least one '{2}' is required to locate a view for rendering. /// internal static string ViewEnginesAreRequired { - get { return GetString("ViewEnginesAreRequired"); } + get => GetString("ViewEnginesAreRequired"); } /// /// '{0}.{1}' must not be empty. At least one '{2}' is required to locate a view for rendering. /// internal static string FormatViewEnginesAreRequired(object p0, object p1, object p2) + => string.Format(CultureInfo.CurrentCulture, GetString("ViewEnginesAreRequired"), p0, p1, p2); + + /// + /// TempData property {0} is not declared as a primitive type or string. + /// + internal static string TempDataProperties_PrimitiveTypeOrString { - return string.Format(CultureInfo.CurrentCulture, GetString("ViewEnginesAreRequired"), p0, p1, p2); + get => GetString("TempDataProperties_PrimitiveTypeOrString"); } + /// + /// TempData property {0} is not declared as a primitive type or string. + /// + internal static string FormatTempDataProperties_PrimitiveTypeOrString(object p0, object p1, object p2) + => string.Format(CultureInfo.CurrentCulture, GetString("TempDataProperties_PrimitiveTypeOrString"), p0, p1, p2); + + /// + /// TempData property {0} does not have a public getter or setter. + /// + internal static string TempDataProperties_PublicGetterSetter + { + get => GetString("TempDataProperties_PublicGetterSetter"); + } + + /// + /// TempData property {0} does not have a public getter or setter. + /// + internal static string FormatTempDataProperties_PublicGetterSetter(object p0, object p1, object p2) + => string.Format(CultureInfo.CurrentCulture, GetString("TempDataProperties_PublicGetterSetter"), p0, p1, p2); + private static string GetString(string name, params string[] formatterNames) { var value = _resourceManager.GetString(name); diff --git a/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Resources.resx b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Resources.resx index a1482ffde3..87c26e3d07 100644 --- a/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Resources.resx +++ b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Resources.resx @@ -286,4 +286,10 @@ '{0}.{1}' must not be empty. At least one '{2}' is required to locate a view for rendering. + + The '{0}.{1}' property with {2} is invalid. A property using {2} must be of primitive or string type. + + + The '{0}.{1}' property with {2} is invalid. A property using {2} must have a public getter and setter. + \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/TempDataAttribute.cs b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/TempDataAttribute.cs similarity index 63% rename from src/Microsoft.AspNetCore.Mvc.RazorPages/TempDataAttribute.cs rename to src/Microsoft.AspNetCore.Mvc.ViewFeatures/TempDataAttribute.cs index 938861eb7c..a1f0e8eb69 100644 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/TempDataAttribute.cs +++ b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/TempDataAttribute.cs @@ -3,8 +3,11 @@ using System; -namespace Microsoft.AspNetCore.Mvc.Rendering +namespace Microsoft.AspNetCore.Mvc.ViewFeatures { + /// + /// Properties with the are stored in the . + /// [AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)] public sealed class TempDataAttribute : Attribute { diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/TempDataPropertyTest.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/TempDataPropertyTest.cs new file mode 100644 index 0000000000..c7cece01b7 --- /dev/null +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/TempDataPropertyTest.cs @@ -0,0 +1,169 @@ +// 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.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using Microsoft.Net.Http.Headers; +using Xunit; + +namespace Microsoft.AspNetCore.Mvc.FunctionalTests +{ + public class TempDataPropertyTest : IClassFixture> + { + protected HttpClient Client { get; } + + public TempDataPropertyTest(MvcTestFixture fixture) + { + Client = fixture.Client; + } + + [Fact] + public async Task TempDataPropertyAttribute_RetainsTempDataWithView() + { + // Arrange + var message = "Success (from Temp Data)"; + var nameValueCollection = new List> + { + new KeyValuePair("FullName", "Bob"), + new KeyValuePair("id", "1"), + }; + var expected = $"{message} for person {nameValueCollection[0].Value} with id {nameValueCollection[1].Value}."; + var content = new FormUrlEncodedContent(nameValueCollection); + + // Act 1 + var redirectResponse = await Client.PostAsync("TempDataProperty/CreateForView", content); + + // Assert 1 + Assert.Equal(HttpStatusCode.Redirect, redirectResponse.StatusCode); + + // Act 2 + var response = await Client.SendAsync(GetRequest(redirectResponse.Headers.Location.ToString(), redirectResponse)); + + // Assert 2 + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var body = await response.Content.ReadAsStringAsync(); + Assert.Equal(expected, body.ToString().Trim()); + } + + [Fact] + public async Task TempDataPropertyAttribute_RetainsTempDataWithoutView() + { + // Arrange + var message = "Success (from Temp Data)"; + var nameValueCollection = new List> + { + new KeyValuePair("FullName", "Bob"), + new KeyValuePair("id", "1"), + }; + var expected = $"{message} for person {nameValueCollection[0].Value} with id {nameValueCollection[1].Value}."; + var content = new FormUrlEncodedContent(nameValueCollection); + + // Act 1 + var redirectResponse = await Client.PostAsync("TempDataProperty/Create", content); + + // Assert 1 + Assert.Equal(HttpStatusCode.Redirect, redirectResponse.StatusCode); + + // Act 2 + var response = await Client.SendAsync(GetRequest(redirectResponse.Headers.Location.ToString(), redirectResponse)); + + // Assert 2 + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var body = await response.Content.ReadAsStringAsync(); + Assert.Equal(expected, body); + } + + [Fact] + public async Task TempDataPropertyAttribute_TempDataKept() + { + // Arrange + var message = "Success (from Temp Data)"; + var nameValueCollection = new List> + { + new KeyValuePair("FullName", "Bob"), + new KeyValuePair("id", "1"), + }; + + var expected = $"{message} for person {nameValueCollection[0].Value} with id {nameValueCollection[1].Value}."; + var content = new FormUrlEncodedContent(nameValueCollection); + + // Act 1 + var response = await Client.PostAsync("TempDataProperty/CreateNoRedirect", content); + + // Assert 1 + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + + // Act 2 + response = await Client.SendAsync(GetRequest("TempDataProperty/TempDataKept", response)); + + // Assert 2 + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var body = await response.Content.ReadAsStringAsync(); + Assert.Equal(message, body); + + // Act 3 + response = await Client.SendAsync(GetRequest("TempDataProperty/ReadTempData", response)); + + // Assert 3 + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + body = await response.Content.ReadAsStringAsync(); + Assert.Equal(message, body); + } + + [Fact] + public async Task TempDataPropertyAttribute_TempDataNotKept() + { + // Arrange + var message = "Success (from Temp Data)"; + var nameValueCollection = new List> + { + new KeyValuePair("FullName", "Bob"), + new KeyValuePair("id", "1"), + }; + + var expected = $"{message} for person {nameValueCollection[0].Value} with id {nameValueCollection[1].Value}."; + var content = new FormUrlEncodedContent(nameValueCollection); + + // Act 1 + var response = await Client.PostAsync("TempDataProperty/CreateNoRedirect", content); + + // Assert 1 + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + + // Act 2 + response = await Client.SendAsync(GetRequest("TempDataProperty/ReadTempData", response)); + + // Assert 2 + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var body = await response.Content.ReadAsStringAsync(); + Assert.Equal(message, body); + + // Act 3 + response = await Client.SendAsync(GetRequest("TempDataProperty/ReadTempData", response)); + + // Assert 3 + body = await response.Content.ReadAsStringAsync(); + Assert.Empty(body); + } + + private HttpRequestMessage GetRequest(string path, HttpResponseMessage response) + { + var request = new HttpRequestMessage(HttpMethod.Get, path); + if (response.Headers.TryGetValues("Set-Cookie", out var values)) + { + foreach (var cookie in SetCookieHeaderValue.ParseList(values.ToList())) + { + if (cookie.Expires == null || cookie.Expires >= DateTimeOffset.UtcNow) + { + request.Headers.Add("Cookie", new CookieHeaderValue(cookie.Name, cookie.Value).ToString()); + } + } + } + return request; + } + } +} diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Infrastructure/PageActionDescriptorProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Infrastructure/PageActionDescriptorProviderTest.cs index 0fc3a36677..d97f10dc0a 100644 --- a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Infrastructure/PageActionDescriptorProviderTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Infrastructure/PageActionDescriptorProviderTest.cs @@ -292,7 +292,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure filterDescriptor => { Assert.Equal(FilterScope.Action, filterDescriptor.Scope); - Assert.IsType(filterDescriptor.Filter); + Assert.IsType(filterDescriptor.Filter); }, filterDescriptor => { @@ -343,7 +343,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure filterDescriptor => { Assert.Equal(FilterScope.Action, filterDescriptor.Scope); - Assert.IsType(filterDescriptor.Filter); + Assert.IsType(filterDescriptor.Filter); }, filterDescriptor => { @@ -396,7 +396,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure filterDescriptor => { Assert.Equal(FilterScope.Action, filterDescriptor.Scope); - Assert.IsType(filterDescriptor.Filter); + Assert.IsType(filterDescriptor.Filter); }, filterDescriptor => { diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageActionInvokerProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageActionInvokerProviderTest.cs index 1b5a6b186e..1f4c3b7ba0 100644 --- a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageActionInvokerProviderTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageActionInvokerProviderTest.cs @@ -14,6 +14,7 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure; using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal; using Microsoft.AspNetCore.Razor.Evolution; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.Logging.Abstractions; diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageActionInvokerTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageActionInvokerTest.cs index 1d27ff178c..5f6465a474 100644 --- a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageActionInvokerTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageActionInvokerTest.cs @@ -15,6 +15,7 @@ using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure; using Microsoft.AspNetCore.Mvc.ViewEngines; using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/RazorPagesOptionsSetupTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/RazorPagesOptionsSetupTest.cs index 54b3d4d863..0d9c219f71 100644 --- a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/RazorPagesOptionsSetupTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/RazorPagesOptionsSetupTest.cs @@ -26,7 +26,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal // Assert Assert.Collection(applicationModel.Filters, - filter => Assert.IsType(filter), + filter => Assert.IsType(filter), filter => Assert.IsType(filter)); } } diff --git a/test/Microsoft.AspNetCore.Mvc.Test/MvcServiceCollectionExtensionsTest.cs b/test/Microsoft.AspNetCore.Mvc.Test/MvcServiceCollectionExtensionsTest.cs index 1e19f32783..6c902cb0e7 100644 --- a/test/Microsoft.AspNetCore.Mvc.Test/MvcServiceCollectionExtensionsTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Test/MvcServiceCollectionExtensionsTest.cs @@ -409,6 +409,7 @@ namespace Microsoft.AspNetCore.Mvc typeof(DefaultApplicationModelProvider), typeof(CorsApplicationModelProvider), typeof(AuthorizationApplicationModelProvider), + typeof(TempDataApplicationModelProvider), } }, { diff --git a/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/Internal/SaveTempDataFilterTest.cs b/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/Internal/SaveTempDataFilterTest.cs index fe7d6f4b9e..00d4b86db3 100644 --- a/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/Internal/SaveTempDataFilterTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/Internal/SaveTempDataFilterTest.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; diff --git a/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/Internal/SaveTempDataPropertyFilterTest.cs b/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/Internal/SaveTempDataPropertyFilterTest.cs new file mode 100644 index 0000000000..bd4057029b --- /dev/null +++ b/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/Internal/SaveTempDataPropertyFilterTest.cs @@ -0,0 +1,125 @@ +// 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.Generic; +using System.Reflection; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Abstractions; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.Internal; +using Moq; +using Xunit; + +namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal +{ + public class SaveTempDataPropertyFilterTest + { + [Fact] + public void SaveTempDataPropertyFilter_PopulatesTempDataWithValuesFromControllerProperty() + { + // Arrange + var httpContext = new DefaultHttpContext(); + var tempData = new TempDataDictionary(httpContext, Mock.Of()) + { + ["TempDataProperty-Test"] = "FirstValue" + }; + + var factory = new Mock(); + factory.Setup(f => f.GetTempData(httpContext)) + .Returns(tempData); + + var filter = new SaveTempDataPropertyFilter(factory.Object); + + var controller = new TestController(); + var controllerType = controller.GetType().GetTypeInfo(); + + var propertyHelper1 = new PropertyHelper(controllerType.GetProperty(nameof(TestController.Test))); + var propertyHelper2 = new PropertyHelper(controllerType.GetProperty(nameof(TestController.Test2))); + var propertyHelpers = new List + { + propertyHelper1, + propertyHelper2, + }; + + filter.PropertyHelpers = propertyHelpers; + var context = new ActionExecutingContext( + new ActionContext + { + HttpContext = httpContext, + RouteData = new RouteData(), + ActionDescriptor = new ActionDescriptor(), + }, + new List(), + new Dictionary(), + controller); + + // Act + filter.OnActionExecuting(context); + controller.Test = "SecondValue"; + filter.OnTempDataSaving(tempData); + + // Assert + Assert.Equal("SecondValue", controller.Test); + Assert.Equal("SecondValue", tempData["TempDataProperty-Test"]); + Assert.Equal(0, controller.Test2); + } + + [Fact] + public void SaveTempDataPropertyFilter_ReadsTempDataFromTempDataDictionary() + { + // Arrange + var httpContext = new DefaultHttpContext(); + var tempData = new TempDataDictionary(httpContext, Mock.Of()) + { + ["TempDataProperty-Test"] = "FirstValue" + }; + + var factory = new Mock(); + factory.Setup(f => f.GetTempData(httpContext)) + .Returns(tempData); + + var filter = new SaveTempDataPropertyFilter(factory.Object); + var controller = new TestController(); + var controllerType = controller.GetType().GetTypeInfo(); + + var propertyHelper1 = new PropertyHelper(controllerType.GetProperty(nameof(TestController.Test))); + var propertyHelper2 = new PropertyHelper(controllerType.GetProperty(nameof(TestController.Test2))); + var propertyHelpers = new List + { + propertyHelper1, + propertyHelper2, + }; + + filter.PropertyHelpers = propertyHelpers; + + var context = new ActionExecutingContext( + new ActionContext + { + HttpContext = httpContext, + RouteData = new RouteData(), + ActionDescriptor = new ActionDescriptor(), + }, + new List(), + new Dictionary(), + controller); + + // Act + filter.OnActionExecuting(context); + filter.OnTempDataSaving(tempData); + + // Assert + Assert.Equal("FirstValue", controller.Test); + Assert.Equal(0, controller.Test2); + } + + public class TestController : Controller + { + [TempData] + public string Test { get; set; } + + [TempData] + public int Test2 { get; set; } + } + } +} diff --git a/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/Internal/TempDataApplicationModelProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/Internal/TempDataApplicationModelProviderTest.cs new file mode 100644 index 0000000000..3a24e5aa35 --- /dev/null +++ b/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/Internal/TempDataApplicationModelProviderTest.cs @@ -0,0 +1,148 @@ +// 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.Collections.Generic; +using System.Linq; +using System.Reflection; +using Microsoft.AspNetCore.Mvc.ApplicationModels; +using Microsoft.AspNetCore.Mvc.Internal; +using Xunit; + +namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal +{ + public class TempDataApplicationModelProviderTest + { + [Theory] + [InlineData(typeof(TestController_OneTempDataProperty))] + [InlineData(typeof(TestController_TwoTempDataProperties))] + public void AddsTempDataPropertyFilter_ForTempDataAttributeProperties(Type type) + { + // Arrange + var provider = new TempDataApplicationModelProvider(); + var defaultProvider = new DefaultApplicationModelProvider(new TestOptionsManager()); + + var context = new ApplicationModelProviderContext(new[] { type.GetTypeInfo() }); + defaultProvider.OnProvidersExecuting(context); + + // Act + provider.OnProvidersExecuting(context); + + // Assert + var controller = Assert.Single(context.Result.Controllers); + Assert.Single(controller.Filters, f => f is SaveTempDataPropertyFilterFactory); + } + + [Fact] + public void InitializeFilterFactory_WithExpectedPropertyHelpers_ForTempDataAttributeProperties() + { + // Arrange + var provider = new TempDataApplicationModelProvider(); + var defaultProvider = new DefaultApplicationModelProvider(new TestOptionsManager()); + + var context = new ApplicationModelProviderContext(new[] { typeof(TestController_OneTempDataProperty).GetTypeInfo() }); + defaultProvider.OnProvidersExecuting(context); + + // Act + provider.OnProvidersExecuting(context); + var controller = context.Result.Controllers.SingleOrDefault(); + var filter = controller.Filters.OfType(); + var saveTempDataPropertyFilterFactory = filter.SingleOrDefault(); + var expected = typeof(TestController_OneTempDataProperty).GetProperty(nameof(TestController_OneTempDataProperty.Test2)); + + // Assert + Assert.NotNull(saveTempDataPropertyFilterFactory); + var tempDataPropertyHelper = Assert.Single(saveTempDataPropertyFilterFactory.TempDataProperties); + Assert.Same(expected, tempDataPropertyHelper.Property); + } + + [Fact] + public void DoesNotInitializeFilterFactory_ThrowsInvalidOperationException_NonPrimitiveType() + { + // Arrange + var provider = new TempDataApplicationModelProvider(); + var defaultProvider = new DefaultApplicationModelProvider(new TestOptionsManager()); + + var context = new ApplicationModelProviderContext(new[] { typeof(TestController_OneValid_OneInvalidProperty).GetTypeInfo() }); + defaultProvider.OnProvidersExecuting(context); + + // Act + var exception = Assert.Throws(() => + provider.OnProvidersExecuting(context)); + + Assert.Equal($"The '{typeof(TestController_OneValid_OneInvalidProperty).FullName}.{nameof(TestController_OneValid_OneInvalidProperty.Test2)}' property with {nameof(TempDataAttribute)} is invalid. A property using {nameof(TempDataAttribute)} must be of primitive or string type.", exception.Message); + } + + [Fact] + public void ThrowsInvalidOperationException_PrivateSetter() + { + // Arrange + var provider = new TempDataApplicationModelProvider(); + var defaultProvider = new DefaultApplicationModelProvider(new TestOptionsManager()); + + var context = new ApplicationModelProviderContext(new[] { typeof(TestController_PrivateSet).GetTypeInfo() }); + defaultProvider.OnProvidersExecuting(context); + + // Act & Assert + var exception = Assert.Throws(() => + provider.OnProvidersExecuting(context)); + + Assert.Equal($"The '{typeof(TestController_PrivateSet).FullName}.{nameof(TestController_NonPrimitiveType.Test)}' property with {nameof(TempDataAttribute)} is invalid. A property using {nameof(TempDataAttribute)} must have a public getter and setter.", exception.Message); + } + + [Fact] + public void ThrowsInvalidOperationException_NonPrimitiveType() + { + // Arrange + var provider = new TempDataApplicationModelProvider(); + var defaultProvider = new DefaultApplicationModelProvider(new TestOptionsManager()); + + var context = new ApplicationModelProviderContext(new[] { typeof(TestController_NonPrimitiveType).GetTypeInfo() }); + defaultProvider.OnProvidersExecuting(context); + + // Act & Assert + var exception = Assert.Throws(() => + provider.OnProvidersExecuting(context)); + + Assert.Equal($"The '{typeof(TestController_NonPrimitiveType).FullName}.{nameof(TestController_NonPrimitiveType.Test)}' property with {nameof(TempDataAttribute)} is invalid. A property using {nameof(TempDataAttribute)} must be of primitive or string type.", exception.Message); + } + + public class TestController_OneTempDataProperty + { + public string Test { get; set; } + + [TempData] + public string Test2 { get; set; } + } + + public class TestController_TwoTempDataProperties + { + [TempData] + public string Test { get; set; } + + [TempData] + public int Test2 { get; set; } + } + + public class TestController_OneValid_OneInvalidProperty + { + [TempData] + public int Test { get; set; } + + [TempData] + public IList Test2 { get; set; } + } + + public class TestController_PrivateSet + { + [TempData] + public string Test { get; private set; } + } + + public class TestController_NonPrimitiveType + { + [TempData] + public object Test { get; set; } + } + } +} diff --git a/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/Internal/TempDataPropertyProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/Internal/TempDataPropertyProviderTest.cs new file mode 100644 index 0000000000..65dd5499bd --- /dev/null +++ b/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/Internal/TempDataPropertyProviderTest.cs @@ -0,0 +1,114 @@ +// 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.Collections.Generic; +using Microsoft.AspNetCore.Http; +using Moq; +using Xunit; + +namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal +{ + public class TempDataPropertyProviderTest + { + [Fact] + public void LoadAndTrackChanges_SetsPropertyValue() + { + // Arrange + var provider = new TempDataPropertyProvider(); + + var tempData = new TempDataDictionary(new DefaultHttpContext(), new NullTempDataProvider()); + tempData["TempDataProperty-TestString"] = "Value"; + tempData.Save(); + + var controller = new TestController() + { + TempData = tempData, + }; + + // Act + provider.LoadAndTrackChanges(controller, controller.TempData); + + // Assert + Assert.Equal("Value", controller.TestString); + Assert.Null(controller.TestString2); + } + + [Fact] + public void LoadAndTrackChanges_ThrowsInvalidOperationException_PrivateSetter() + { + // Arrange + var provider = new TempDataPropertyProvider(); + + var tempData = new TempDataDictionary(new DefaultHttpContext(), new NullTempDataProvider()); + tempData["TempDataProperty-Test"] = "Value"; + tempData.Save(); + + var controller = new TestController_PrivateSet() + { + TempData = tempData, + }; + + // Act & Assert + var exception = Assert.Throws(() => + provider.LoadAndTrackChanges(controller, controller.TempData)); + + Assert.Equal("TempData properties must have a public getter and setter.", exception.Message); + } + + [Fact] + public void LoadAndTrackChanges_ThrowsInvalidOperationException_NonPrimitiveType() + { + // Arrange + var provider = new TempDataPropertyProvider(); + + var tempData = new TempDataDictionary(new DefaultHttpContext(), new NullTempDataProvider()); + tempData["TempDataProperty-Test"] = new object(); + tempData.Save(); + + var controller = new TestController_NonPrimitiveType() + { + TempData = tempData, + }; + + // Act & Assert + var exception = Assert.Throws(() => + provider.LoadAndTrackChanges(controller, controller.TempData)); + + Assert.Equal("TempData properties must be declared as primitive types or string only.", exception.Message); + } + + public class TestController : Controller + { + [TempData] + public string TestString { get; set; } + + [TempData] + public string TestString2 { get; set; } + } + + public class TestController_PrivateSet : Controller + { + [TempData] + public string Test { get; private set; } + } + + public class TestController_NonPrimitiveType : Controller + { + [TempData] + public object Test { get; set; } + } + + private class NullTempDataProvider : ITempDataProvider + { + public IDictionary LoadTempData(HttpContext context) + { + return null; + } + + public void SaveTempData(HttpContext context, IDictionary values) + { + } + } + } +} diff --git a/test/WebSites/BasicWebSite/Controllers/TempDataPropertyController.cs b/test/WebSites/BasicWebSite/Controllers/TempDataPropertyController.cs new file mode 100644 index 0000000000..bc99885881 --- /dev/null +++ b/test/WebSites/BasicWebSite/Controllers/TempDataPropertyController.cs @@ -0,0 +1,58 @@ +// 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 BasicWebSite.Models; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using System.Net; + +namespace BasicWebSite.Controllers +{ + public class TempDataPropertyController : Controller + { + [TempData] + public string Message { get; set; } + + [HttpPost] + public IActionResult CreateForView(Person person) + { + Message = "Success (from Temp Data)"; + return RedirectToAction("DetailsView", person); + } + + [HttpPost] + public IActionResult Create(Person person) + { + Message = "Success (from Temp Data)"; + return RedirectToAction("Details", person); + } + + public IActionResult DetailsView(Person person) + { + ViewData["Message"] = Message; + return View(person); + } + + public string Details(Person person) + { + return $"{Message} for person {person.FullName} with id {person.id}."; + } + + public StatusCodeResult CreateNoRedirect(Person person) + { + Message = "Success (from Temp Data)"; + return new OkResult(); + } + + public string TempDataKept() + { + TempData.Keep(); + return Message; + } + + public string ReadTempData() + { + return Message; + } + } +} diff --git a/test/WebSites/BasicWebSite/Views/TempDataProperty/DetailsView.cshtml b/test/WebSites/BasicWebSite/Views/TempDataProperty/DetailsView.cshtml new file mode 100644 index 0000000000..cc23b5c6cb --- /dev/null +++ b/test/WebSites/BasicWebSite/Views/TempDataProperty/DetailsView.cshtml @@ -0,0 +1,2 @@ +@model BasicWebSite.Models.Person +@ViewData["Message"] for person @Model.FullName with id @Model.id.