// Copyright (c) Microsoft Open Technologies, Inc. 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.Concurrent; using System.Reflection; using Microsoft.AspNet.Mvc.Rendering; using Microsoft.Framework.DependencyInjection; namespace Microsoft.AspNet.Mvc.Razor { /// public class RazorPageActivator : IRazorPageActivator { // Name of the "public TModel Model" property on RazorPage private const string ModelPropertyName = "Model"; private readonly ITypeActivator _typeActivator; private readonly ConcurrentDictionary _activationInfo; /// /// Initializes a new instance of the class. /// public RazorPageActivator(ITypeActivator typeActivator) { _typeActivator = typeActivator; _activationInfo = new ConcurrentDictionary(); } /// public void Activate([NotNull] IRazorPage page, [NotNull] ViewContext context) { var activationInfo = _activationInfo.GetOrAdd(page.GetType(), CreateViewActivationInfo); context.ViewData = CreateViewDataDictionary(context, activationInfo); for (var i = 0; i < activationInfo.PropertyActivators.Length; i++) { var activateInfo = activationInfo.PropertyActivators[i]; activateInfo.Activate(page, context); } } private ViewDataDictionary CreateViewDataDictionary(ViewContext context, PageActivationInfo activationInfo) { // Create a ViewDataDictionary if the ViewContext.ViewData is not set or the type of // ViewContext.ViewData is an incompatibile type. if (context.ViewData == null) { // Create ViewDataDictionary(IModelMetadataProvider, ModelStateDictionary). return (ViewDataDictionary)_typeActivator.CreateInstance(context.HttpContext.RequestServices, activationInfo.ViewDataDictionaryType, context.ModelState); } else if (context.ViewData.GetType() != activationInfo.ViewDataDictionaryType) { // Create ViewDataDictionary(ViewDataDictionary). return (ViewDataDictionary)_typeActivator.CreateInstance(context.HttpContext.RequestServices, activationInfo.ViewDataDictionaryType, context.ViewData); } return context.ViewData; } private PageActivationInfo CreateViewActivationInfo(Type type) { // Look for a property named "Model". If it is non-null, we'll assume this is // the equivalent of TModel Model property on RazorPage var modelProperty = type.GetRuntimeProperty(ModelPropertyName); if (modelProperty == null) { var message = Resources.FormatViewCannotBeActivated(type.FullName, GetType().FullName); throw new InvalidOperationException(message); } var modelType = modelProperty.PropertyType; var viewDataType = typeof(ViewDataDictionary<>).MakeGenericType(modelType); return new PageActivationInfo { ViewDataDictionaryType = viewDataType, PropertyActivators = PropertyActivator.GetPropertiesToActivate(type, typeof(ActivateAttribute), CreateActivateInfo) }; } private PropertyActivator CreateActivateInfo(PropertyInfo property) { Func valueAccessor; if (typeof(ViewDataDictionary).IsAssignableFrom(property.PropertyType)) { valueAccessor = context => context.ViewData; } else { valueAccessor = context => { var serviceProvider = context.HttpContext.RequestServices; var value = serviceProvider.GetRequiredService(property.PropertyType); var canHasViewContext = value as ICanHasViewContext; if (canHasViewContext != null) { canHasViewContext.Contextualize(context); } return value; }; } return new PropertyActivator(property, valueAccessor); } private class PageActivationInfo { public PropertyActivator[] PropertyActivators { get; set; } public Type ViewDataDictionaryType { get; set; } public Action ViewDataDictionarySetter { get; set; } } } }