Skip to content
This repository has been archived by the owner on Dec 14, 2018. It is now read-only.

Commit

Permalink
TempData property attribute
Browse files Browse the repository at this point in the history
Addresses #5600
  • Loading branch information
jbagga authored Mar 16, 2017
1 parent ce28117 commit 1197657
Show file tree
Hide file tree
Showing 26 changed files with 950 additions and 240 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
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;

Expand Down Expand Up @@ -75,7 +76,7 @@ private void AddActionDescriptors(IList<ActionDescriptor> actions, RazorProjectI
var index = parentDirectoryPath.LastIndexOf('/');
if (index == -1)
{
parentDirectoryPath = string.Empty;
parentDirectoryPath = string.Empty;
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,6 @@ private async Task ExecutePageAsync()
{
propertyFilter.OriginalValues = originalValues;
propertyFilter.Subject = _page;
propertyFilter.Prefix = TempDataPropertyProvider.Prefix;
}

IActionResult result = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public void Configure(RazorPagesOptions options)
}

// Support for [TempData] on properties
options.ConfigureFilter(new SaveTempDataPropertyFilter());
options.ConfigureFilter(new SaveTempDataPropertyFilterFactory());
// Always require an antiforgery token on post
options.ConfigureFilter(new AutoValidateAntiforgeryTokenAttribute());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
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;
Expand Down Expand Up @@ -197,6 +198,11 @@ internal static void AddViewServices(IServiceCollection services)
//
// Temp Data
//
services.TryAddEnumerable(
ServiceDescriptor.Transient<IApplicationModelProvider, TempDataApplicationModelProvider>());
services.TryAddSingleton<SaveTempDataFilter>();
services.TryAddTransient<SaveTempDataPropertyFilter>();

// This does caching so it should stay singleton
services.TryAddSingleton<ITempDataProvider, CookieTempDataProvider>();

Expand All @@ -208,8 +214,6 @@ internal static void AddViewServices(IServiceCollection services)

// These are stateless so their lifetime isn't really important.
services.TryAddSingleton<ITempDataDictionaryFactory, TempDataDictionaryFactory>();
services.TryAddSingleton<SaveTempDataFilter>();

services.TryAddSingleton(ArrayPool<ViewBufferValue>.Shared);
services.TryAddScoped<IViewBufferScope, MemoryPoolViewBufferScope>();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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 <c>PropertyHelper</c> is an internal shared source type
internal IList<PropertyHelper> PropertyHelpers { get; set; }

public object Subject { get; set; }

Expand All @@ -24,12 +36,46 @@ public void OnTempDataSaving(ITempDataDictionary tempData)
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<PropertyInfo, object>();

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)
{
}
}
}

Original file line number Diff line number Diff line change
@@ -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 <c>PropertyHelper</c> is an internal shared source type
internal IList<PropertyHelper> TempDataProperties { get; set; }

public bool IsReusable => false;

public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
{
if (serviceProvider == null)
{
throw new ArgumentNullException(nameof(serviceProvider));
}

var service = serviceProvider.GetRequiredService<SaveTempDataPropertyFilter>();
service.PropertyHelpers = TempDataProperties;
return service;
}
}
}
Original file line number Diff line number Diff line change
@@ -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
{
/// <inheritdoc />
/// <remarks>This order ensures that <see cref="TempDataApplicationModelProvider"/> runs after the <see cref="DefaultApplicationModelProvider"/>.</remarks>
public int Order => -1000 + 10;

/// <inheritdoc />
public void OnProvidersExecuted(ApplicationModelProviderContext context)
{
}

/// <inheritdoc />
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<PropertyHelper>()
};
}

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)));
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@
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
{
Expand All @@ -18,6 +16,12 @@ public class TempDataPropertyProvider
private ConcurrentDictionary<Type, IEnumerable<PropertyInfo>> _subjectProperties =
new ConcurrentDictionary<Type, IEnumerable<PropertyInfo>>();

/// <summary>
/// Loads and tracks any changes to the properties of the <paramref name="subject"/>.
/// </summary>
/// <param name="subject">The properties of the subject are loaded and tracked. May be a <see cref="Controller"/>.</param>
/// <param name="tempData">The <see cref="ITempDataDictionary"/>.</param>
/// <returns></returns>
public IDictionary<PropertyInfo, object> LoadAndTrackChanges(object subject, ITempDataDictionary tempData)
{
var properties = GetSubjectProperties(subject);
Expand Down
Loading

0 comments on commit 1197657

Please sign in to comment.