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

Commit 736d28b

Browse files
committed
Adding support for Razor precompilation
Fixes #3917
1 parent b7a0393 commit 736d28b

20 files changed

+502
-179
lines changed

src/Microsoft.AspNetCore.Mvc.Core/ApplicationParts/AssemblyPart.cs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,27 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
1212
/// <summary>
1313
/// An <see cref="ApplicationPart"/> backed by an <see cref="Assembly"/>.
1414
/// </summary>
15-
public class AssemblyPart : ApplicationPart, IApplicationPartTypeProvider, ICompilationReferencesProvider
15+
public class AssemblyPart :
16+
ApplicationPart,
17+
IApplicationPartTypeProvider,
18+
ICompilationReferencesProvider,
19+
IViewsProvider
1620
{
21+
/// <summary>
22+
/// Gets the suffix for the view assembly.
23+
/// </summary>
24+
public static readonly string PrecompiledViewsAssemblySuffix = ".PrecompiledViews";
25+
26+
/// <summary>
27+
/// Gets the namespace for the <see cref="ViewInfoContainer"/> type in the view assembly.
28+
/// </summary>
29+
public static readonly string ViewInfoContainerNamespace = "AspNetCore";
30+
31+
/// <summary>
32+
/// Gets the type name for the view collection type in the view assembly.
33+
/// </summary>
34+
public static readonly string ViewInfoContainerTypeName = "__PrecompiledViewCollection";
35+
1736
/// <summary>
1837
/// Initalizes a new <see cref="AssemblyPart"/> instance.
1938
/// </summary>
@@ -41,6 +60,27 @@ public AssemblyPart(Assembly assembly)
4160
/// <inheritdoc />
4261
public IEnumerable<TypeInfo> Types => Assembly.DefinedTypes;
4362

63+
/// <inheritdoc />
64+
public IEnumerable<ViewInfo> Views
65+
{
66+
get
67+
{
68+
var precompiledAssemblyName = new AssemblyName(Assembly.FullName);
69+
precompiledAssemblyName.Name = precompiledAssemblyName.Name + PrecompiledViewsAssemblySuffix;
70+
71+
var typeName = $"{ViewInfoContainerNamespace}.{ViewInfoContainerTypeName},{precompiledAssemblyName}";
72+
var viewInfoContainerTypeName = Type.GetType(typeName);
73+
74+
if (viewInfoContainerTypeName == null)
75+
{
76+
return null;
77+
}
78+
79+
var precompiledViews = (ViewInfoContainer)Activator.CreateInstance(viewInfoContainerTypeName);
80+
return precompiledViews.ViewInfos;
81+
}
82+
}
83+
4484
/// <inheritdoc />
4585
public IEnumerable<string> GetReferencePaths()
4686
{
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System.Collections.Generic;
5+
6+
namespace Microsoft.AspNetCore.Mvc.ApplicationParts
7+
{
8+
/// <summary>
9+
/// Exposes a sequence of views associated with an <see cref="ApplicationPart"/> .
10+
/// </summary>
11+
public interface IViewsProvider
12+
{
13+
/// <summary>
14+
/// Gets the sequence of <see cref="ViewInfo"/>.
15+
/// </summary>
16+
IEnumerable<ViewInfo> Views { get; }
17+
}
18+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
6+
namespace Microsoft.AspNetCore.Mvc.ApplicationParts
7+
{
8+
/// <summary>
9+
/// Provides information for precompiled views.
10+
/// </summary>
11+
public class ViewInfo
12+
{
13+
/// <summary>
14+
/// Creates a new instance of <see cref="ViewInfo" />.
15+
/// </summary>
16+
/// <param name="path">The path of the view.</param>
17+
/// <param name="type">The view <see cref="System.Type"/>.</param>
18+
public ViewInfo(string path, Type type)
19+
{
20+
Path = path;
21+
Type = type;
22+
}
23+
24+
/// <summary>
25+
/// The path of the view.
26+
/// </summary>
27+
public string Path { get; }
28+
29+
/// <summary>
30+
/// The view <see cref="System.Type"/>.
31+
/// </summary>
32+
public Type Type { get; }
33+
}
34+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System.Collections.Generic;
5+
6+
namespace Microsoft.AspNetCore.Mvc.ApplicationParts
7+
{
8+
/// <summary>
9+
/// A container for <see cref="ViewInfo"/> instances.
10+
/// </summary>
11+
public class ViewInfoContainer
12+
{
13+
/// <summary>
14+
/// Initializes a new instance of <see cref="ViewInfos"/>.
15+
/// </summary>
16+
/// <param name="views">The sequence of <see cref="ViewInfo"/>.</param>
17+
public ViewInfoContainer(IReadOnlyList<ViewInfo> views)
18+
{
19+
ViewInfos = views;
20+
}
21+
22+
/// <summary>
23+
/// The <see cref="IReadOnlyList{T}"/> of <see cref="ViewInfo"/>.
24+
/// </summary>
25+
public IReadOnlyList<ViewInfo> ViewInfos { get; }
26+
}
27+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using Microsoft.Extensions.DependencyInjection;
5+
6+
namespace Microsoft.AspNetCore.Mvc
7+
{
8+
/// <summary>
9+
/// Configures the <see cref="IMvcBuilder"/>. Implement this interface to enable design-time configuration
10+
/// (for instance during pre-compilation of views) of <see cref="IMvcBuilder"/>.
11+
/// </summary>
12+
public interface IDesignTimeMvcBuilderConfiguration
13+
{
14+
/// <summary>
15+
/// Configures the <see cref="IMvcBuilder"/>.
16+
/// </summary>
17+
/// <param name="builder">The <see cref="IMvcBuilder"/>.</param>
18+
void ConfigureMvc(IMvcBuilder builder);
19+
}
20+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
7+
namespace Microsoft.AspNetCore.Mvc.Razor.Compilation
8+
{
9+
public class ViewsFeature
10+
{
11+
public IDictionary<string, Type> Views { get; } =
12+
new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase);
13+
}
14+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using Microsoft.AspNetCore.Mvc.ApplicationParts;
7+
8+
namespace Microsoft.AspNetCore.Mvc.Razor.Compilation
9+
{
10+
/// <summary>
11+
/// An <see cref="IApplicationFeatureProvider{TFeature}"/> for <see cref="ViewsFeature"/>.
12+
/// </summary>
13+
public class ViewsFeatureProvider : IApplicationFeatureProvider<ViewsFeature>
14+
{
15+
/// <inheritdoc />
16+
public void PopulateFeature(IEnumerable<ApplicationPart> parts, ViewsFeature feature)
17+
{
18+
foreach (var provider in parts.OfType<IViewsProvider>())
19+
{
20+
var precompiledViews = provider.Views;
21+
if (precompiledViews != null)
22+
{
23+
foreach (var viewInfo in precompiledViews)
24+
{
25+
feature.Views[viewInfo.Path] = viewInfo.Type;
26+
}
27+
}
28+
}
29+
}
30+
}
31+
}

src/Microsoft.AspNetCore.Mvc.Razor/DependencyInjection/MvcRazorMvcCoreBuilderExtensions.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@ private static void AddRazorViewEngineFeatureProviders(IMvcCoreBuilder builder)
7272
{
7373
builder.PartManager.FeatureProviders.Add(new MetadataReferenceFeatureProvider());
7474
}
75+
76+
if (!builder.PartManager.FeatureProviders.OfType<ViewsFeatureProvider>().Any())
77+
{
78+
builder.PartManager.FeatureProviders.Add(new ViewsFeatureProvider());
79+
}
7580
}
7681

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

@@ -165,7 +172,7 @@ internal static void AddRazorViewEngineServices(IServiceCollection services)
165172
// creating the singleton RazorViewEngine instance.
166173
services.TryAddTransient<IRazorPageFactoryProvider, DefaultRazorPageFactoryProvider>();
167174
services.TryAddTransient<IRazorCompilationService, RazorCompilationService>();
168-
services.TryAddTransient<IMvcRazorHost,MvcRazorHost>();
175+
services.TryAddTransient<IMvcRazorHost, MvcRazorHost>();
169176

170177
// This caches Razor page activation details that are valid for the lifetime of the application.
171178
services.TryAddSingleton<IRazorPageActivator, RazorPageActivator>();
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using Microsoft.CodeAnalysis;
5+
using Microsoft.CodeAnalysis.CSharp;
6+
using Microsoft.CodeAnalysis.Emit;
7+
using Microsoft.CodeAnalysis.Text;
8+
using Microsoft.Extensions.Options;
9+
10+
namespace Microsoft.AspNetCore.Mvc.Razor.Internal
11+
{
12+
public class CSharpCompiler
13+
{
14+
private readonly CSharpCompilationOptions _compilationOptions;
15+
private readonly CSharpParseOptions _parseOptions;
16+
private readonly RazorReferenceManager _referenceManager;
17+
private readonly DebugInformationFormat _pdbFormat =
18+
#if NET451
19+
SymbolsUtility.SupportsFullPdbGeneration() ?
20+
DebugInformationFormat.Pdb :
21+
DebugInformationFormat.PortablePdb;
22+
#else
23+
DebugInformationFormat.PortablePdb;
24+
#endif
25+
26+
public CSharpCompiler(RazorReferenceManager manager, IOptions<RazorViewEngineOptions> optionsAccessor)
27+
{
28+
_referenceManager = manager;
29+
_compilationOptions = optionsAccessor.Value.CompilationOptions;
30+
_parseOptions = optionsAccessor.Value.ParseOptions;
31+
EmitOptions = new EmitOptions(debugInformationFormat: _pdbFormat);
32+
}
33+
34+
public EmitOptions EmitOptions { get; }
35+
36+
public SyntaxTree CreateSyntaxTree(SourceText sourceText)
37+
{
38+
return CSharpSyntaxTree.ParseText(
39+
sourceText,
40+
options: _parseOptions);
41+
}
42+
43+
public CSharpCompilation CreateCompilation(string assemblyName)
44+
{
45+
return CSharpCompilation.Create(
46+
assemblyName,
47+
options: _compilationOptions,
48+
references: _referenceManager.CompilationReferences);
49+
}
50+
}
51+
}

src/Microsoft.AspNetCore.Mvc.Razor/Internal/CompilerCache.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,22 +42,22 @@ public CompilerCache(IFileProvider fileProvider)
4242

4343
/// <summary>
4444
/// Initializes a new instance of <see cref="CompilerCache"/> populated with precompiled views
45-
/// specified by <paramref name="precompiledViews"/>.
45+
/// specified by <paramref name="views"/>.
4646
/// </summary>
4747
/// <param name="fileProvider"><see cref="IFileProvider"/> used to locate Razor views.</param>
48-
/// <param name="precompiledViews">A mapping of application relative paths of view to the precompiled view
49-
/// <see cref="Type"/>s.</param>
48+
/// <param name="views">A mapping of application relative paths of view to <see cref="Type"/>s that
49+
/// have already been compiled.</param>
5050
public CompilerCache(
5151
IFileProvider fileProvider,
52-
IDictionary<string, Type> precompiledViews)
52+
IDictionary<string, Type> views)
5353
: this(fileProvider)
5454
{
55-
if (precompiledViews == null)
55+
if (views == null)
5656
{
57-
throw new ArgumentNullException(nameof(precompiledViews));
57+
throw new ArgumentNullException(nameof(views));
5858
}
5959

60-
foreach (var item in precompiledViews)
60+
foreach (var item in views)
6161
{
6262
var cacheEntry = new CompilerCacheResult(item.Key, new CompilationResult(item.Value));
6363
_cache.Set(GetNormalizedPath(item.Key), Task.FromResult(cacheEntry));

src/Microsoft.AspNetCore.Mvc.Razor/Internal/DefaultCompilerCacheProvider.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4-
using Microsoft.Extensions.Options;
4+
using Microsoft.AspNetCore.Mvc.ApplicationParts;
5+
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
56

67
namespace Microsoft.AspNetCore.Mvc.Razor.Internal
78
{
@@ -13,10 +14,15 @@ public class DefaultCompilerCacheProvider : ICompilerCacheProvider
1314
/// <summary>
1415
/// Initializes a new instance of <see cref="DefaultCompilerCacheProvider"/>.
1516
/// </summary>
17+
/// <param name="applicationPartManager">The <see cref="ApplicationPartManager" /></param>
1618
/// <param name="fileProviderAccessor">The <see cref="IRazorViewEngineFileProviderAccessor"/>.</param>
17-
public DefaultCompilerCacheProvider(IRazorViewEngineFileProviderAccessor fileProviderAccessor)
19+
public DefaultCompilerCacheProvider(
20+
ApplicationPartManager applicationPartManager,
21+
IRazorViewEngineFileProviderAccessor fileProviderAccessor)
1822
{
19-
Cache = new CompilerCache(fileProviderAccessor.FileProvider);
23+
var feature = new ViewsFeature();
24+
applicationPartManager.PopulateFeature(feature);
25+
Cache = new CompilerCache(fileProviderAccessor.FileProvider, feature.Views);
2026
}
2127

2228
/// <inheritdoc />

0 commit comments

Comments
 (0)