| File: RazorPageActivator.cs | Web Access |
| Project: src\aspnetcore\src\Mvc\Mvc.Razor\src\Microsoft.AspNetCore.Mvc.Razor.csproj (Microsoft.AspNetCore.Mvc.Razor) |
// 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(); } } }