-
Notifications
You must be signed in to change notification settings - Fork 9.8k
/
RazorPageActivator.cs
134 lines (113 loc) · 4.71 KB
/
RazorPageActivator.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Reflection;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
namespace Microsoft.AspNetCore.Mvc.Razor;
/// <inheritdoc />
public class RazorPageActivator : IRazorPageActivator
{
// Name of the "public TModel Model" property on RazorPage<TModel>
private const string ModelPropertyName = "Model";
private readonly ConcurrentDictionary<CacheKey, RazorPagePropertyActivator> _activationInfo;
private readonly IModelMetadataProvider _metadataProvider;
// Value accessors for common singleton properties activated in a RazorPage.
private readonly RazorPagePropertyActivator.PropertyValueAccessors _propertyAccessors;
/// <summary>
/// Initializes a new instance of the <see cref="RazorPageActivator"/> class.
/// </summary>
public RazorPageActivator(
IModelMetadataProvider metadataProvider,
IUrlHelperFactory urlHelperFactory,
IJsonHelper jsonHelper,
DiagnosticSource diagnosticSource,
HtmlEncoder htmlEncoder,
IModelExpressionProvider modelExpressionProvider)
{
_activationInfo = new ConcurrentDictionary<CacheKey, RazorPagePropertyActivator>();
_metadataProvider = metadataProvider;
_propertyAccessors = new RazorPagePropertyActivator.PropertyValueAccessors
{
UrlHelperAccessor = urlHelperFactory.GetUrlHelper,
JsonHelperAccessor = context => jsonHelper,
DiagnosticSourceAccessor = context => diagnosticSource,
HtmlEncoderAccessor = context => htmlEncoder,
ModelExpressionProviderAccessor = context => modelExpressionProvider,
};
}
internal void ClearCache()
{
_activationInfo.Clear();
}
internal ConcurrentDictionary<CacheKey, RazorPagePropertyActivator> ActivationInfo => _activationInfo;
/// <inheritdoc />
public void Activate(IRazorPage page, ViewContext context)
{
ArgumentNullException.ThrowIfNull(page);
ArgumentNullException.ThrowIfNull(context);
var propertyActivator = GetOrAddCacheEntry(page);
propertyActivator.Activate(page, context);
}
internal RazorPagePropertyActivator GetOrAddCacheEntry(IRazorPage page)
{
var pageType = page.GetType();
Type? providedModelType = null;
if (page is IModelTypeProvider modelTypeProvider)
{
providedModelType = modelTypeProvider.GetModelType();
}
// We only need to vary by providedModelType since it varies at runtime. Defined model type
// is synonymous with the pageType and consequently does not need to be accounted for in the cache key.
var cacheKey = new CacheKey(pageType, providedModelType);
if (!_activationInfo.TryGetValue(cacheKey, out var propertyActivator))
{
// Look for a property named "Model". If it is non-null, we'll assume this is
// the equivalent of TModel Model property on RazorPage<TModel>.
//
// Otherwise if we don't have a model property the activator will just skip setting
// the view data.
var modelType = providedModelType;
if (modelType == null)
{
modelType = pageType.GetRuntimeProperty(ModelPropertyName)?.PropertyType;
}
propertyActivator = new RazorPagePropertyActivator(
pageType,
modelType,
_metadataProvider,
_propertyAccessors);
propertyActivator = _activationInfo.GetOrAdd(cacheKey, propertyActivator);
}
return propertyActivator;
}
internal readonly struct CacheKey : IEquatable<CacheKey>
{
public CacheKey(Type pageType, Type? providedModelType)
{
PageType = pageType;
ProvidedModelType = providedModelType;
}
public Type PageType { get; }
public Type? ProvidedModelType { get; }
public bool Equals(CacheKey other)
{
return PageType == other.PageType &&
ProvidedModelType == other.ProvidedModelType;
}
public override int GetHashCode()
{
var hashCode = new HashCode();
hashCode.Add(PageType);
if (ProvidedModelType != null)
{
hashCode.Add(ProvidedModelType);
}
return hashCode.ToHashCode();
}
}
}