First stab Roslyn based compilation service for razor pages

- Flow the host service provider to MvcServices
- Use assembly neutral interfaces to access host services
- Added RoslynCompilationService to Microsoft.AspNet.Mvc.Razor
- Modified self host sample as helios needs to be updated to flow more services
This commit is contained in:
David Fowler 2014-03-02 01:14:01 -08:00
parent 5c07c6a07b
commit 346f02e37c
7 changed files with 155 additions and 7 deletions

View File

@ -9,11 +9,17 @@ namespace MvcSample
public class Program
{
const string baseUrl = "http://localhost:9001/";
private readonly IServiceProvider _serviceProvider;
public static void Main()
public Program(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public void Main()
{
#if NET45
using (WebApp.Start<Startup>(new StartOptions(baseUrl)))
using (WebApp.Start(baseUrl, app => new Startup(_serviceProvider).Configuration(app)))
{
Console.WriteLine("Listening at {0}", baseUrl);
Process.Start(baseUrl);

View File

@ -16,6 +16,13 @@ namespace MvcSample
{
public class Startup
{
private IServiceProvider _serviceProvider;
public Startup(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public void Configuration(IAppBuilder app)
{
app.UseErrorPage();
@ -28,7 +35,7 @@ namespace MvcSample
{
string appRoot = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
var mvcServices = new MvcServices(appRoot);
var mvcServices = new MvcServices(appRoot, _serviceProvider);
var router = builder.UseRouter();

View File

@ -0,0 +1,79 @@
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.Net.Runtime.Services;
namespace Microsoft.AspNet.Mvc.Razor.Compilation
{
public class RoslynCompilationService : ICompilationService
{
private readonly IMetadataReferenceProvider _provider;
private readonly IApplicationEnvironment _environment;
public RoslynCompilationService(IServiceProvider serviceProvider)
{
// TODO: Get these services via ctor injection when we get container chaining implemented
_provider = (IMetadataReferenceProvider)serviceProvider.GetService(typeof(IMetadataReferenceProvider));
_environment = (IApplicationEnvironment)serviceProvider.GetService(typeof(IApplicationEnvironment));
}
public Task<CompilationResult> Compile(string content)
{
var syntaxTrees = new[] { CSharpSyntaxTree.ParseText(content) };
var targetFramework = _environment.TargetFramework;
// Get references from the application itself and from
// this assembly for the base types etc
var referenceNames = new[] {
_environment.ApplicationName,
typeof(RoslynCompilationService).GetTypeInfo().Assembly.GetName().Name
};
var references = referenceNames.SelectMany(name => _provider.GetReferences(name, targetFramework))
.Cast<MetadataReference>();
var assemblyName = Path.GetRandomFileName();
var compilation = CSharpCompilation.Create(assemblyName,
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary),
syntaxTrees: syntaxTrees,
references: references);
var ms = new MemoryStream();
var result = compilation.Emit(ms);
if (!result.Success)
{
var messages = result.Diagnostics.Where(IsError).Select(d => GetCompilationMessage(d));
return Task.FromResult(CompilationResult.Failed(content, messages));
}
// TODO: Flow loader to this code so we're not using Load() directly
var type = Assembly.Load(ms.ToArray())
.GetExportedTypes()
.First();
return Task.FromResult(CompilationResult.Successful(String.Empty, type));
}
private CompilationMessage GetCompilationMessage(Diagnostic diagnostic)
{
#if NET45
var formatter = DiagnosticFormatter.Instance;
#else
var formatter = new DiagnosticFormatter();
#endif
return new CompilationMessage(formatter.Format(diagnostic));
}
private bool IsError(Diagnostic diagnostic)
{
return diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error;
}
}
}

View File

@ -0,0 +1,13 @@
using System.Runtime.Versioning;
namespace Microsoft.Net.Runtime.Services
{
[AssemblyNeutral]
public interface IApplicationEnvironment
{
string ApplicationName { get; }
string Version { get; }
string ApplicationBasePath { get; }
FrameworkName TargetFramework { get; }
}
}

View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Runtime.Versioning;
namespace Microsoft.Net.Runtime.Services
{
[AssemblyNeutral]
public interface IMetadataReferenceProvider
{
// REVIEW: This is object because we don't have a reference to roslyn in this assembly
IEnumerable<object> GetReferences(string name, FrameworkName targetFramework);
}
[AssemblyNeutral]
public class AssemblyNeutralAttribute : Attribute { }
}

View File

@ -8,10 +8,21 @@
"Microsoft.AspNet.Mvc" : "",
"Microsoft.AspNet.Mvc.ModelBinding" : "",
"Microsoft.AspNet.Mvc.Rendering" : "",
"Microsoft.AspNet.Mvc.Razor.Host" : ""
"Microsoft.AspNet.Mvc.Razor.Host" : "",
"Microsoft.CodeAnalysis" : "0.6.31123.2",
"Microsoft.CodeAnalysis.CSharp" : "0.6.31123.2",
"System.Reflection.Metadata.Ecma335": "0.6.31123.2",
"System.Collections.Immutable" : "1.1.15.0"
},
"configurations": {
"net45": {},
"net45" : {
"dependencies": {
"System.Xml.Linq": "",
"System.Xml": "",
"System.Runtime" : "",
"System.Collections" : ""
}
},
"k10" : {}
}
}

View File

@ -1,8 +1,10 @@
using Microsoft.AspNet.DependencyInjection;
using System;
using Microsoft.AspNet.DependencyInjection;
using Microsoft.AspNet.DependencyInjection.NestedProviders;
using Microsoft.AspNet.FileSystems;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Razor;
using Microsoft.AspNet.Mvc.Razor.Compilation;
namespace Microsoft.AspNet.Mvc.Startup
{
@ -11,6 +13,11 @@ namespace Microsoft.AspNet.Mvc.Startup
public ServiceProvider Services { get; private set; }
public MvcServices(string appRoot)
: this(appRoot, null)
{
}
public MvcServices(string appRoot, IServiceProvider hostServiceProvider)
{
Services = new ServiceProvider();
@ -29,7 +36,16 @@ namespace Microsoft.AspNet.Mvc.Startup
AddInstance<IMvcRazorHost>(new MvcRazorHost(typeof(RazorView).FullName));
#if NET45
Add<ICompilationService, CscBasedCompilationService>();
// TODO: Container chaining to flow services from the host to this container
if (hostServiceProvider == null)
{
Add<ICompilationService, CscBasedCompilationService>();
}
else
{
// TODO: Make this work like normal when we get container chaining
AddInstance<ICompilationService>(new RoslynCompilationService(hostServiceProvider));
}
#endif
Add<IRazorCompilationService, RazorCompilationService>();
Add<IVirtualPathViewFactory, VirtualPathViewFactory>();