[Perf] Optimize creation of VDD

Fixes #3906
This commit is contained in:
mnltejaswini 2016-03-09 10:50:42 -08:00
parent 75df24e357
commit 45a72a0e18
1 changed files with 50 additions and 5 deletions

View File

@ -3,7 +3,9 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Diagnostics;
using System.Reflection; using System.Reflection;
using System.Linq.Expressions;
using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Razor.Internal; using Microsoft.AspNetCore.Mvc.Razor.Internal;
using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.AspNetCore.Mvc.Rendering;
@ -17,6 +19,11 @@ namespace Microsoft.AspNetCore.Mvc.Razor
/// <inheritdoc /> /// <inheritdoc />
public class RazorPageActivator : IRazorPageActivator public class RazorPageActivator : IRazorPageActivator
{ {
private delegate ViewDataDictionary CreateViewDataNested(ViewDataDictionary source);
private delegate ViewDataDictionary CreateViewDataRoot(
IModelMetadataProvider metadataProvider,
ModelStateDictionary modelState);
// Name of the "public TModel Model" property on RazorPage<TModel> // Name of the "public TModel Model" property on RazorPage<TModel>
private const string ModelPropertyName = "Model"; private const string ModelPropertyName = "Model";
private readonly ConcurrentDictionary<Type, PageActivationInfo> _activationInfo; private readonly ConcurrentDictionary<Type, PageActivationInfo> _activationInfo;
@ -63,17 +70,14 @@ namespace Microsoft.AspNetCore.Mvc.Razor
if (context.ViewData == null) if (context.ViewData == null)
{ {
// Create ViewDataDictionary<TModel>(IModelMetadataProvider, ModelStateDictionary). // Create ViewDataDictionary<TModel>(IModelMetadataProvider, ModelStateDictionary).
return (ViewDataDictionary)Activator.CreateInstance( return activationInfo.CreateViewDataRoot(
activationInfo.ViewDataDictionaryType,
_metadataProvider, _metadataProvider,
context.ModelState); context.ModelState);
} }
else if (context.ViewData.GetType() != activationInfo.ViewDataDictionaryType) else if (context.ViewData.GetType() != activationInfo.ViewDataDictionaryType)
{ {
// Create ViewDataDictionary<TModel>(ViewDataDictionary). // Create ViewDataDictionary<TModel>(ViewDataDictionary).
return (ViewDataDictionary)Activator.CreateInstance( return activationInfo.CreateViewDataNested(context.ViewData);
activationInfo.ViewDataDictionaryType,
context.ViewData);
} }
return context.ViewData; return context.ViewData;
@ -96,6 +100,8 @@ namespace Microsoft.AspNetCore.Mvc.Razor
return new PageActivationInfo return new PageActivationInfo
{ {
ViewDataDictionaryType = viewDataType, ViewDataDictionaryType = viewDataType,
CreateViewDataNested = GetCreateViewDataNested(viewDataType),
CreateViewDataRoot = GetCreateViewDataRoot(viewDataType),
PropertyActivators = PropertyActivator<ViewContext>.GetPropertiesToActivate( PropertyActivators = PropertyActivator<ViewContext>.GetPropertiesToActivate(
type, type,
typeof(RazorInjectAttribute), typeof(RazorInjectAttribute),
@ -104,6 +110,41 @@ namespace Microsoft.AspNetCore.Mvc.Razor
}; };
} }
private CreateViewDataNested GetCreateViewDataNested(Type viewDataDictionaryType)
{
var parameterTypes = new Type[] { typeof(ViewDataDictionary) };
var matchingConstructor = viewDataDictionaryType.GetConstructor(parameterTypes);
Debug.Assert(matchingConstructor != null);
var parameters = new ParameterExpression[] { Expression.Parameter(parameterTypes[0]) };
var newExpression = Expression.New(matchingConstructor, parameters);
var castNewCall = Expression.Convert(
newExpression,
typeof(ViewDataDictionary));
var lambda = Expression.Lambda<CreateViewDataNested>(castNewCall, parameters);
return lambda.Compile();
}
private CreateViewDataRoot GetCreateViewDataRoot(Type viewDataDictionaryType)
{
var parameterTypes = new Type[] {
typeof(IModelMetadataProvider),
typeof(ModelStateDictionary) };
var matchingConstructor = viewDataDictionaryType.GetConstructor(parameterTypes);
Debug.Assert(matchingConstructor != null);
var parameters = new ParameterExpression[] {
Expression.Parameter(parameterTypes[0]),
Expression.Parameter(parameterTypes[1]) };
var newExpression = Expression.New(matchingConstructor, parameters);
var castNewCall = Expression.Convert(
newExpression,
typeof(ViewDataDictionary));
var lambda = Expression.Lambda<CreateViewDataRoot>(castNewCall, parameters);
return lambda.Compile();
}
private PropertyActivator<ViewContext> CreateActivateInfo(PropertyInfo property) private PropertyActivator<ViewContext> CreateActivateInfo(PropertyInfo property)
{ {
Func<ViewContext, object> valueAccessor; Func<ViewContext, object> valueAccessor;
@ -147,6 +188,10 @@ namespace Microsoft.AspNetCore.Mvc.Razor
public Type ViewDataDictionaryType { get; set; } public Type ViewDataDictionaryType { get; set; }
public CreateViewDataNested CreateViewDataNested { get; set; }
public CreateViewDataRoot CreateViewDataRoot { get; set; }
public Action<object, object> ViewDataDictionarySetter { get; set; } public Action<object, object> ViewDataDictionarySetter { get; set; }
} }
} }