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

Commit

Permalink
Adding support for Razor precompilation
Browse files Browse the repository at this point in the history
Fixes #3917
  • Loading branch information
pranavkm committed Aug 18, 2016
1 parent b7a0393 commit 736d28b
Show file tree
Hide file tree
Showing 20 changed files with 502 additions and 179 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,27 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
/// <summary>
/// An <see cref="ApplicationPart"/> backed by an <see cref="Assembly"/>.
/// </summary>
public class AssemblyPart : ApplicationPart, IApplicationPartTypeProvider, ICompilationReferencesProvider
public class AssemblyPart :
ApplicationPart,
IApplicationPartTypeProvider,
ICompilationReferencesProvider,
IViewsProvider
{
/// <summary>
/// Gets the suffix for the view assembly.
/// </summary>
public static readonly string PrecompiledViewsAssemblySuffix = ".PrecompiledViews";

/// <summary>
/// Gets the namespace for the <see cref="ViewInfoContainer"/> type in the view assembly.
/// </summary>
public static readonly string ViewInfoContainerNamespace = "AspNetCore";

/// <summary>
/// Gets the type name for the view collection type in the view assembly.
/// </summary>
public static readonly string ViewInfoContainerTypeName = "__PrecompiledViewCollection";

/// <summary>
/// Initalizes a new <see cref="AssemblyPart"/> instance.
/// </summary>
Expand Down Expand Up @@ -41,6 +60,27 @@ public AssemblyPart(Assembly assembly)
/// <inheritdoc />
public IEnumerable<TypeInfo> Types => Assembly.DefinedTypes;

/// <inheritdoc />
public IEnumerable<ViewInfo> Views
{
get
{
var precompiledAssemblyName = new AssemblyName(Assembly.FullName);
precompiledAssemblyName.Name = precompiledAssemblyName.Name + PrecompiledViewsAssemblySuffix;

var typeName = $"{ViewInfoContainerNamespace}.{ViewInfoContainerTypeName},{precompiledAssemblyName}";
var viewInfoContainerTypeName = Type.GetType(typeName);

if (viewInfoContainerTypeName == null)
{
return null;
}

var precompiledViews = (ViewInfoContainer)Activator.CreateInstance(viewInfoContainerTypeName);
return precompiledViews.ViewInfos;
}
}

/// <inheritdoc />
public IEnumerable<string> GetReferencePaths()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// 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.Collections.Generic;

namespace Microsoft.AspNetCore.Mvc.ApplicationParts
{
/// <summary>
/// Exposes a sequence of views associated with an <see cref="ApplicationPart"/> .
/// </summary>
public interface IViewsProvider
{
/// <summary>
/// Gets the sequence of <see cref="ViewInfo"/>.
/// </summary>
IEnumerable<ViewInfo> Views { get; }
}
}
34 changes: 34 additions & 0 deletions src/Microsoft.AspNetCore.Mvc.Core/ApplicationParts/ViewInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// 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;

namespace Microsoft.AspNetCore.Mvc.ApplicationParts
{
/// <summary>
/// Provides information for precompiled views.
/// </summary>
public class ViewInfo
{
/// <summary>
/// Creates a new instance of <see cref="ViewInfo" />.
/// </summary>
/// <param name="path">The path of the view.</param>
/// <param name="type">The view <see cref="System.Type"/>.</param>
public ViewInfo(string path, Type type)
{
Path = path;
Type = type;
}

/// <summary>
/// The path of the view.
/// </summary>
public string Path { get; }

/// <summary>
/// The view <see cref="System.Type"/>.
/// </summary>
public Type Type { get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// 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.Collections.Generic;

namespace Microsoft.AspNetCore.Mvc.ApplicationParts
{
/// <summary>
/// A container for <see cref="ViewInfo"/> instances.
/// </summary>
public class ViewInfoContainer
{
/// <summary>
/// Initializes a new instance of <see cref="ViewInfos"/>.
/// </summary>
/// <param name="views">The sequence of <see cref="ViewInfo"/>.</param>
public ViewInfoContainer(IReadOnlyList<ViewInfo> views)
{
ViewInfos = views;
}

/// <summary>
/// The <see cref="IReadOnlyList{T}"/> of <see cref="ViewInfo"/>.
/// </summary>
public IReadOnlyList<ViewInfo> ViewInfos { get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// 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 Microsoft.Extensions.DependencyInjection;

namespace Microsoft.AspNetCore.Mvc
{
/// <summary>
/// Configures the <see cref="IMvcBuilder"/>. Implement this interface to enable design-time configuration
/// (for instance during pre-compilation of views) of <see cref="IMvcBuilder"/>.
/// </summary>
public interface IDesignTimeMvcBuilderConfiguration
{
/// <summary>
/// Configures the <see cref="IMvcBuilder"/>.
/// </summary>
/// <param name="builder">The <see cref="IMvcBuilder"/>.</param>
void ConfigureMvc(IMvcBuilder builder);
}
}
14 changes: 14 additions & 0 deletions src/Microsoft.AspNetCore.Mvc.Razor/Compilation/ViewsFeature.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// 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;

namespace Microsoft.AspNetCore.Mvc.Razor.Compilation
{
public class ViewsFeature
{
public IDictionary<string, Type> Views { get; } =
new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase);
}
}
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.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc.ApplicationParts;

namespace Microsoft.AspNetCore.Mvc.Razor.Compilation
{
/// <summary>
/// An <see cref="IApplicationFeatureProvider{TFeature}"/> for <see cref="ViewsFeature"/>.
/// </summary>
public class ViewsFeatureProvider : IApplicationFeatureProvider<ViewsFeature>
{
/// <inheritdoc />
public void PopulateFeature(IEnumerable<ApplicationPart> parts, ViewsFeature feature)
{
foreach (var provider in parts.OfType<IViewsProvider>())
{
var precompiledViews = provider.Views;
if (precompiledViews != null)
{
foreach (var viewInfo in precompiledViews)
{
feature.Views[viewInfo.Path] = viewInfo.Type;
}
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ private static void AddRazorViewEngineFeatureProviders(IMvcCoreBuilder builder)
{
builder.PartManager.FeatureProviders.Add(new MetadataReferenceFeatureProvider());
}

if (!builder.PartManager.FeatureProviders.OfType<ViewsFeatureProvider>().Any())
{
builder.PartManager.FeatureProviders.Add(new ViewsFeatureProvider());
}
}

/// <summary>
Expand Down Expand Up @@ -127,6 +132,8 @@ public static IMvcCoreBuilder InitializeTagHelper<TTagHelper>(
// Internal for testing.
internal static void AddRazorViewEngineServices(IServiceCollection services)
{
services.TryAddSingleton<CSharpCompiler>();
services.TryAddSingleton<RazorReferenceManager>();
// This caches compilation related details that are valid across the lifetime of the application.
services.TryAddSingleton<ICompilationService, DefaultRoslynCompilationService>();

Expand Down Expand Up @@ -165,7 +172,7 @@ internal static void AddRazorViewEngineServices(IServiceCollection services)
// creating the singleton RazorViewEngine instance.
services.TryAddTransient<IRazorPageFactoryProvider, DefaultRazorPageFactoryProvider>();
services.TryAddTransient<IRazorCompilationService, RazorCompilationService>();
services.TryAddTransient<IMvcRazorHost,MvcRazorHost>();
services.TryAddTransient<IMvcRazorHost, MvcRazorHost>();

// This caches Razor page activation details that are valid for the lifetime of the application.
services.TryAddSingleton<IRazorPageActivator, RazorPageActivator>();
Expand Down
51 changes: 51 additions & 0 deletions src/Microsoft.AspNetCore.Mvc.Razor/Internal/CSharpCompiler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// 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 Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.Text;
using Microsoft.Extensions.Options;

namespace Microsoft.AspNetCore.Mvc.Razor.Internal
{
public class CSharpCompiler
{
private readonly CSharpCompilationOptions _compilationOptions;
private readonly CSharpParseOptions _parseOptions;
private readonly RazorReferenceManager _referenceManager;
private readonly DebugInformationFormat _pdbFormat =
#if NET451
SymbolsUtility.SupportsFullPdbGeneration() ?
DebugInformationFormat.Pdb :
DebugInformationFormat.PortablePdb;
#else
DebugInformationFormat.PortablePdb;
#endif

public CSharpCompiler(RazorReferenceManager manager, IOptions<RazorViewEngineOptions> optionsAccessor)
{
_referenceManager = manager;
_compilationOptions = optionsAccessor.Value.CompilationOptions;
_parseOptions = optionsAccessor.Value.ParseOptions;
EmitOptions = new EmitOptions(debugInformationFormat: _pdbFormat);
}

public EmitOptions EmitOptions { get; }

public SyntaxTree CreateSyntaxTree(SourceText sourceText)
{
return CSharpSyntaxTree.ParseText(
sourceText,
options: _parseOptions);
}

public CSharpCompilation CreateCompilation(string assemblyName)
{
return CSharpCompilation.Create(
assemblyName,
options: _compilationOptions,
references: _referenceManager.CompilationReferences);
}
}
}
14 changes: 7 additions & 7 deletions src/Microsoft.AspNetCore.Mvc.Razor/Internal/CompilerCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,22 @@ public CompilerCache(IFileProvider fileProvider)

/// <summary>
/// Initializes a new instance of <see cref="CompilerCache"/> populated with precompiled views
/// specified by <paramref name="precompiledViews"/>.
/// specified by <paramref name="views"/>.
/// </summary>
/// <param name="fileProvider"><see cref="IFileProvider"/> used to locate Razor views.</param>
/// <param name="precompiledViews">A mapping of application relative paths of view to the precompiled view
/// <see cref="Type"/>s.</param>
/// <param name="views">A mapping of application relative paths of view to <see cref="Type"/>s that
/// have already been compiled.</param>
public CompilerCache(
IFileProvider fileProvider,
IDictionary<string, Type> precompiledViews)
IDictionary<string, Type> views)
: this(fileProvider)
{
if (precompiledViews == null)
if (views == null)
{
throw new ArgumentNullException(nameof(precompiledViews));
throw new ArgumentNullException(nameof(views));
}

foreach (var item in precompiledViews)
foreach (var item in views)
{
var cacheEntry = new CompilerCacheResult(item.Key, new CompilationResult(item.Value));
_cache.Set(GetNormalizedPath(item.Key), Task.FromResult(cacheEntry));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// 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 Microsoft.Extensions.Options;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.AspNetCore.Mvc.Razor.Compilation;

namespace Microsoft.AspNetCore.Mvc.Razor.Internal
{
Expand All @@ -13,10 +14,15 @@ public class DefaultCompilerCacheProvider : ICompilerCacheProvider
/// <summary>
/// Initializes a new instance of <see cref="DefaultCompilerCacheProvider"/>.
/// </summary>
/// <param name="applicationPartManager">The <see cref="ApplicationPartManager" /></param>
/// <param name="fileProviderAccessor">The <see cref="IRazorViewEngineFileProviderAccessor"/>.</param>
public DefaultCompilerCacheProvider(IRazorViewEngineFileProviderAccessor fileProviderAccessor)
public DefaultCompilerCacheProvider(
ApplicationPartManager applicationPartManager,
IRazorViewEngineFileProviderAccessor fileProviderAccessor)
{
Cache = new CompilerCache(fileProviderAccessor.FileProvider);
var feature = new ViewsFeature();
applicationPartManager.PopulateFeature(feature);
Cache = new CompilerCache(fileProviderAccessor.FileProvider, feature.Views);
}

/// <inheritdoc />
Expand Down
Loading

0 comments on commit 736d28b

Please sign in to comment.