diff --git a/src/Microsoft.AspNet.Mvc.Razor/Internal/RazorFileInfoCollectionGenerator.cs b/src/Microsoft.AspNet.Mvc.Razor/Internal/RazorFileInfoCollectionGenerator.cs new file mode 100644 index 0000000000..954a27ee20 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Razor/Internal/RazorFileInfoCollectionGenerator.cs @@ -0,0 +1,71 @@ +// 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.Runtime.CompilerServices; +using System.Text; +using Microsoft.AspNet.Mvc.Razor.Precompilation; +using Microsoft.Dnx.Runtime; +using Microsoft.Framework.Internal; + +namespace Microsoft.AspNet.Mvc.Razor.Internal +{ + /// + /// Utility type to code generate types. + /// + public static class RazorFileInfoCollectionGenerator + { + /// + /// Generates CSharp code for the specified . + /// + /// The . + /// + public static string GenerateCode([NotNull] RazorFileInfoCollection fileInfoCollection) + { + var builder = new StringBuilder(); + + builder.Append( +$@"namespace __ASP_ASSEMBLY +{{ + [{typeof(CompilerGeneratedAttribute).FullName}] + public class __PreGeneratedViewCollection : {typeof(RazorFileInfoCollection).FullName} + {{ + public __PreGeneratedViewCollection() + {{ + {nameof(RazorFileInfoCollection.AssemblyResourceName)} = @""{fileInfoCollection.AssemblyResourceName}""; + {nameof(RazorFileInfoCollection.SymbolsResourceName)} = @""{fileInfoCollection.SymbolsResourceName}""; + FileInfos = new System.Collections.Generic.List<{typeof(RazorFileInfo).FullName}> + {{"); + + foreach (var fileInfo in fileInfoCollection.FileInfos) + { + builder.Append( +$@" + new {typeof(RazorFileInfo).FullName} + {{ + {nameof(RazorFileInfo.FullTypeName)} = @""{fileInfo.FullTypeName}"", + {nameof(RazorFileInfo.RelativePath)} = @""{fileInfo.RelativePath}"" + }},"); + } + + builder.Append( +$@" + }}; + }} + + private static {typeof(System.Reflection.Assembly).FullName} _loadedAssembly; + + public override {typeof(System.Reflection.Assembly).FullName} LoadAssembly( + {typeof(IAssemblyLoadContext).FullName} loadContext) + {{ + if (_loadedAssembly == null) + {{ + _loadedAssembly = base.LoadAssembly(loadContext); + }} + return _loadedAssembly; + }} + }} +}}"); + return builder.ToString(); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Razor/Precompilation/RazorFileInfoCollectionGenerator.cs b/src/Microsoft.AspNet.Mvc.Razor/Precompilation/RazorFileInfoCollectionGenerator.cs deleted file mode 100644 index 8a590ba907..0000000000 --- a/src/Microsoft.AspNet.Mvc.Razor/Precompilation/RazorFileInfoCollectionGenerator.cs +++ /dev/null @@ -1,129 +0,0 @@ -// 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.Globalization; -using System.Text; -using Microsoft.AspNet.Mvc.Razor.Compilation; -using Microsoft.CodeAnalysis; -using Microsoft.Dnx.Compilation.CSharp; -using Microsoft.Dnx.Runtime; -using Microsoft.Framework.Internal; - -namespace Microsoft.AspNet.Mvc.Razor.Precompilation -{ - public class RazorFileInfoCollectionGenerator - { - private string _fileFormat; - - public RazorFileInfoCollectionGenerator([NotNull] RazorFileInfoCollection fileInfoCollection, - [NotNull] CompilationSettings compilationSettings) - { - RazorFileInfoCollection = fileInfoCollection; - CompilationSettings = compilationSettings; - } - - protected RazorFileInfoCollection RazorFileInfoCollection { get; } - - protected CompilationSettings CompilationSettings { get; } - - public virtual SyntaxTree GenerateCollection() - { - var builder = new StringBuilder(); - builder.AppendFormat(CultureInfo.InvariantCulture, - TopFormat, - RazorFileInfoCollection.AssemblyResourceName, - RazorFileInfoCollection.SymbolsResourceName); - - foreach (var fileInfo in RazorFileInfoCollection.FileInfos) - { - var perFileEntry = GenerateFile(fileInfo); - builder.Append(perFileEntry); - } - - builder.Append(Bottom); - - var sourceCode = builder.ToString(); - var syntaxTree = SyntaxTreeGenerator.Generate(sourceCode, - "__AUTO__GeneratedViewsCollection.cs", - CompilationSettings); - - return syntaxTree; - } - - protected virtual string GenerateFile([NotNull] RazorFileInfo fileInfo) - { - return string.Format(FileFormat, - fileInfo.RelativePath, - fileInfo.FullTypeName); - } - - protected virtual string TopFormat - { - get - { - return -$@"using System; -using System.Collections.Generic; -using System.Reflection; -using {typeof(RazorFileInfoCollection).Namespace}; - -namespace __ASP_ASSEMBLY -{{{{ - public class __PreGeneratedViewCollection : {nameof(RazorFileInfoCollection)} - {{{{ - public __PreGeneratedViewCollection() - {{{{ - {nameof(RazorFileInfoCollection.AssemblyResourceName)} = ""{{0}}""; - {nameof(RazorFileInfoCollection.SymbolsResourceName)} = ""{{1}}""; - var fileInfos = new List<{nameof(RazorFileInfo)}>(); - {nameof(RazorFileInfoCollection.FileInfos)} = fileInfos; - {nameof(RazorFileInfo)} info; -"; - } - } - - protected virtual string Bottom - { - get - { - return - $@" - }} - private static Assembly _loadedAssembly; - - public override Assembly LoadAssembly({typeof(IAssemblyLoadContext).FullName} loadContext) - {{ - if (_loadedAssembly == null) - {{ - _loadedAssembly = base.LoadAssembly(loadContext); - }} - return _loadedAssembly; - }} - }} -}} -"; - } - } - - protected virtual string FileFormat - { - get - { - if (_fileFormat == null) - { - _fileFormat = - $@" - info = new {nameof(RazorFileInfo)} - {{{{ - {nameof(RazorFileInfo.RelativePath)} = @""{{0}}"", - {nameof(RazorFileInfo.FullTypeName)} = @""{{1}}"" - }}}}; - fileInfos.Add(info); -"; - } - - return _fileFormat; - } - } - } -} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Razor/Precompilation/RazorPreCompiler.cs b/src/Microsoft.AspNet.Mvc.Razor/Precompilation/RazorPreCompiler.cs index 3749b43448..10bd897a8c 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/Precompilation/RazorPreCompiler.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/Precompilation/RazorPreCompiler.cs @@ -64,12 +64,11 @@ namespace Microsoft.AspNet.Mvc.Razor.Precompilation var result = CreateFileInfoCollection(); if (result != null) { - var collectionGenerator = new RazorFileInfoCollectionGenerator( - result, - CompilationSettings); - - var tree = collectionGenerator.GenerateCollection(); - CompileContext.Compilation = CompileContext.Compilation.AddSyntaxTrees(tree); + var generatedCode = RazorFileInfoCollectionGenerator.GenerateCode(result); + var syntaxTree = CSharpSyntaxTree.ParseText( + generatedCode, + SyntaxTreeGenerator.GetParseOptions(CompilationSettings)); + CompileContext.Compilation = CompileContext.Compilation.AddSyntaxTrees(syntaxTree); } } diff --git a/test/Microsoft.AspNet.Mvc.Razor.Test/Internal/RazorFileInfoCollectionGeneratorTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Test/Internal/RazorFileInfoCollectionGeneratorTest.cs new file mode 100644 index 0000000000..1730d74efb --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Razor.Test/Internal/RazorFileInfoCollectionGeneratorTest.cs @@ -0,0 +1,135 @@ +// 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 Microsoft.AspNet.Mvc.Razor.Compilation; +using Microsoft.AspNet.Mvc.Razor.Precompilation; +using Microsoft.CodeAnalysis.CSharp; +using Xunit; + +namespace Microsoft.AspNet.Mvc.Razor.Internal +{ + public class RazorFileInfoCollectionGeneratorTest + { + public static TheoryData GenerateCollection_ProducesExpectedCodeData + { + get + { + var expected1 = +@"namespace __ASP_ASSEMBLY +{ + [System.Runtime.CompilerServices.CompilerGeneratedAttribute] + public class __PreGeneratedViewCollection : Microsoft.AspNet.Mvc.Razor.Precompilation.RazorFileInfoCollection + { + public __PreGeneratedViewCollection() + { + AssemblyResourceName = @""EmptyAssembly""; + SymbolsResourceName = @""""; + FileInfos = new System.Collections.Generic.List + { + }; + } + + private static System.Reflection.Assembly _loadedAssembly; + + public override System.Reflection.Assembly LoadAssembly( + Microsoft.Dnx.Runtime.IAssemblyLoadContext loadContext) + { + if (_loadedAssembly == null) + { + _loadedAssembly = base.LoadAssembly(loadContext); + } + return _loadedAssembly; + } + } +}"; + var expected2 = +@"namespace __ASP_ASSEMBLY +{ + [System.Runtime.CompilerServices.CompilerGeneratedAttribute] + public class __PreGeneratedViewCollection : Microsoft.AspNet.Mvc.Razor.Precompilation.RazorFileInfoCollection + { + public __PreGeneratedViewCollection() + { + AssemblyResourceName = @""TestAssembly""; + SymbolsResourceName = @""SymbolsResource""; + FileInfos = new System.Collections.Generic.List + { + new Microsoft.AspNet.Mvc.Razor.Precompilation.RazorFileInfo + { + FullTypeName = @""SomeType.Name"", + RelativePath = @""Views/Home/Index.cshtml"" + }, + new Microsoft.AspNet.Mvc.Razor.Precompilation.RazorFileInfo + { + FullTypeName = @""Different.Name"", + RelativePath = @""Views/Home/Different.cshtml"" + }, + }; + } + + private static System.Reflection.Assembly _loadedAssembly; + + public override System.Reflection.Assembly LoadAssembly( + Microsoft.Dnx.Runtime.IAssemblyLoadContext loadContext) + { + if (_loadedAssembly == null) + { + _loadedAssembly = base.LoadAssembly(loadContext); + } + return _loadedAssembly; + } + } +}"; + + return new TheoryData + { + { new EmptyCollection(), expected1 }, + { new TestCollection(), expected2 }, + }; + } + } + + [Theory] + [MemberData(nameof(GenerateCollection_ProducesExpectedCodeData))] + public void GenerateCollection_ProducesExpectedCode(RazorFileInfoCollection collection, string expected) + { + // Act + var actual = RazorFileInfoCollectionGenerator.GenerateCode(collection); + + // Assert + Assert.Equal(expected, actual); + } + + private class EmptyCollection : RazorFileInfoCollection + { + public EmptyCollection() + { + AssemblyResourceName = "EmptyAssembly"; + FileInfos = new List(); + } + } + + private class TestCollection : RazorFileInfoCollection + { + public TestCollection() + { + AssemblyResourceName = "TestAssembly"; + SymbolsResourceName = "SymbolsResource"; + FileInfos = new List + { + new RazorFileInfo + { + FullTypeName = "SomeType.Name", + RelativePath = @"Views/Home/Index.cshtml" + }, + new RazorFileInfo + { + FullTypeName = "Different.Name", + RelativePath = @"Views/Home/Different.cshtml" + }, + }; + } + } + } +}