public async Task RenderAsync(RenderRequest request) { CompilationParameters parameters = new CompilationParameters { BasePageType = request.BaseType, Namespaces = new NamespaceCollection(request.Context.Namespaces) }; RazorCompiler compiler = _compilers.GetOrAdd(parameters, _ => new RazorCompiler(parameters, request.Context)); await compiler.RenderPageAsync(request); }
/// <summary> /// Creates a Razor compiler using an existing set of services (which must already have Razor services registered). /// </summary> /// <param name="parameters">The compilation parameters.</param> /// <param name="serviceProvider">The service provider to use.</param> public RazorCompiler(CompilationParameters parameters, IServiceProvider serviceProvider) { _ = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); // Do a check to make sure required services are registered RazorProjectEngine razorProjectEngine = serviceProvider.GetService <RazorProjectEngine>(); if (razorProjectEngine == null) { // Razor services haven't been registered so create a new services container for this compiler ServiceCollection serviceCollection = new ServiceCollection(); serviceCollection.AddSingleton(serviceProvider.GetRequiredService <ILoggerFactory>()); serviceCollection.AddRazor( serviceProvider.GetRequiredService <IReadOnlyFileSystem>(), serviceProvider.GetService <ClassCatalog>()); serviceProvider = serviceCollection.BuildServiceProvider(); razorProjectEngine = serviceProvider.GetRequiredService <RazorProjectEngine>(); } _serviceScopeFactory = serviceProvider.GetRequiredService <IServiceScopeFactory>(); // Calculate the base page type Type basePageType = parameters.BasePageType ?? typeof(StatiqRazorPage <>); string baseClassName = basePageType.FullName; int tickIndex = baseClassName.IndexOf('`'); if (tickIndex > 0) { baseClassName = baseClassName.Substring(0, tickIndex); } string baseType = basePageType.IsGenericTypeDefinition ? $"{baseClassName}<TModel>" : baseClassName; // We need to register a new document classifier phase because builder.SetBaseType() (which uses builder.ConfigureClass()) // use the DefaultRazorDocumentClassifierPhase which stops applying document classifier passes after DocumentIntermediateNode.DocumentKind is set // (which gets set by the Razor document classifier passes registered in RazorExtensions.Register()) // Also need to add it just after the DocumentClassifierPhase, otherwise it'll miss the C# lowering phase List <IRazorEnginePhase> phases = razorProjectEngine.Engine.Phases.ToList(); phases.Insert( phases.IndexOf(phases.OfType <IRazorDocumentClassifierPhase>().Last()) + 1, new StatiqDocumentPhase(baseType, parameters.Namespaces) { Engine = razorProjectEngine.Engine }); FieldInfo phasesField = razorProjectEngine.Engine.GetType().GetField("<Phases>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic); phasesField.SetValue(razorProjectEngine.Engine, phases.ToArray()); }
public RazorCompiler(CompilationParameters parameters, IExecutionContext context) { _namespaces = parameters.Namespaces; // Calculate the base page type Type basePageType = parameters.BasePageType ?? typeof(StatiqRazorPage <>); string baseClassName = basePageType.FullName; int tickIndex = baseClassName.IndexOf('`'); if (tickIndex > 0) { baseClassName = baseClassName.Substring(0, tickIndex); } _baseType = basePageType.IsGenericTypeDefinition ? $"{baseClassName}<TModel>" : baseClassName; // Create the service collection that MVC needs and add default MVC services ServiceCollection serviceCollection = new ServiceCollection(); // Register some of our own types serviceCollection .AddSingleton(parameters.FileSystem) .AddSingleton <FileSystemFileProvider>() .AddSingleton(context.GetRequiredService <ILoggerFactory>()) .AddSingleton <DiagnosticSource, SilentDiagnosticSource>() .AddSingleton <IHostingEnvironment, HostingEnvironment>() .AddSingleton <ObjectPoolProvider, DefaultObjectPoolProvider>() .AddSingleton <IRazorViewEngineFileProviderAccessor, DefaultRazorViewEngineFileProviderAccessor>() .AddSingleton <IViewCompilerProvider, StatiqRazorViewCompilerProvider>() .AddSingleton <StatiqRazorProjectFileSystem>() .AddSingleton <RazorProjectFileSystem, StatiqRazorProjectFileSystem>() .AddSingleton(x => RazorProjectEngine.Create( RazorConfiguration.Default, x.GetRequiredService <RazorProjectFileSystem>(), b => { // See MvcRazorMvcCoreBuilderExtensions.AddRazorViewEngineServices(IServiceCollection) RazorExtensions.Register(b); b.Features.Add(x.GetRequiredService <LazyMetadataReferenceFeature>()); // Lazily calls the MetadataReferenceFeatureProvider b.Features.Add(new CompilationTagHelperFeature()); b.Features.Add(new DefaultTagHelperDescriptorProvider()); b.Features.Add(new ViewComponentTagHelperDescriptorProvider()); // We need to register a new document classifier phase because builder.SetBaseType() (which uses builder.ConfigureClass()) // use the DefaultRazorDocumentClassifierPhase which stops applying document classifier passes after DocumentIntermediateNode.DocumentKind is set // (which gets set by the Razor document classifier passes registered in RazorExtensions.Register()) // Also need to add it just after the DocumentClassifierPhase, otherwise it'll miss the C# lowering phase b.Phases.Insert( b.Phases.IndexOf(b.Phases.OfType <IRazorDocumentClassifierPhase>().Last()) + 1, new StatiqDocumentPhase(_baseType, _namespaces)); })); // Register the view location expander serviceCollection.Configure <RazorViewEngineOptions>(x => x.ViewLocationExpanders.Add(new ViewLocationExpander())); // Add the default services _after_ adding our own // (most default registration use .TryAdd...() so they skip already registered types) IMvcCoreBuilder builder = serviceCollection .AddMvcCore() .AddRazorViewEngine(); // Get and register MetadataReferences builder.PartManager.FeatureProviders.Add( new MetadataReferenceFeatureProvider(parameters.DynamicAssemblies)); IServiceProvider serviceProvider = serviceCollection.BuildServiceProvider(); _serviceScopeFactory = serviceProvider.GetRequiredService <IServiceScopeFactory>(); }
public RazorCompiler(CompilationParameters parameters, IExecutionContext context) { // Create the service collection that MVC needs and add default MVC services ServiceCollection serviceCollection = new ServiceCollection(); DiagnosticListener listener = new DiagnosticListener("Razor"); // Register some of our own types serviceCollection .AddSingleton(parameters.FileSystem) .AddSingleton <Microsoft.Extensions.FileProviders.IFileProvider, FileSystemFileProvider>() .AddSingleton(context.GetRequiredService <ILoggerFactory>()) .AddSingleton <DiagnosticSource, SilentDiagnosticSource>() .AddSingleton <DiagnosticListener>(listener) .AddSingleton <IWebHostEnvironment, HostEnvironment>() .AddSingleton <ObjectPoolProvider, DefaultObjectPoolProvider>() // .AddSingleton<IViewCompilerProvider, StatiqRazorViewCompilerProvider>() .AddSingleton <StatiqRazorProjectFileSystem>() .AddSingleton <RazorProjectFileSystem, StatiqRazorProjectFileSystem>(); // Register the view location expander serviceCollection.Configure <RazorViewEngineOptions>(x => x.ViewLocationExpanders.Add(new ViewLocationExpander())); // Add the default services _after_ adding our own // (most default registration use .TryAdd...() so they skip already registered types) IMvcCoreBuilder builder = serviceCollection .AddMvcCore() .AddRazorViewEngine() .AddRazorRuntimeCompilation(); // Add all loaded assemblies CompilationReferencesProvider referencesProvider = new CompilationReferencesProvider(); referencesProvider.Assemblies.AddRange(AppDomain.CurrentDomain.GetAssemblies()); // And a couple needed assemblies that might not be loaded in the AppDomain yet referencesProvider.Assemblies.Add(typeof(IHtmlContent).Assembly); referencesProvider.Assemblies.Add(Assembly.Load(new AssemblyName("Microsoft.CSharp"))); // Add the reference provider as an ApplicationPart builder.ConfigureApplicationPartManager(x => x.ApplicationParts.Add(referencesProvider)); // Build the service provider and local scope IServiceProvider serviceProvider = serviceCollection.BuildServiceProvider(); _serviceScopeFactory = serviceProvider.GetRequiredService <IServiceScopeFactory>(); // Calculate the base page type Type basePageType = parameters.BasePageType ?? typeof(StatiqRazorPage <>); string baseClassName = basePageType.FullName; int tickIndex = baseClassName.IndexOf('`'); if (tickIndex > 0) { baseClassName = baseClassName.Substring(0, tickIndex); } string baseType = basePageType.IsGenericTypeDefinition ? $"{baseClassName}<TModel>" : baseClassName; // We need to register a new document classifier phase because builder.SetBaseType() (which uses builder.ConfigureClass()) // use the DefaultRazorDocumentClassifierPhase which stops applying document classifier passes after DocumentIntermediateNode.DocumentKind is set // (which gets set by the Razor document classifier passes registered in RazorExtensions.Register()) // Also need to add it just after the DocumentClassifierPhase, otherwise it'll miss the C# lowering phase RazorProjectEngine razorProjectEngine = serviceProvider.GetRequiredService <RazorProjectEngine>(); List <IRazorEnginePhase> phases = razorProjectEngine.Engine.Phases.ToList(); phases.Insert( phases.IndexOf(phases.OfType <IRazorDocumentClassifierPhase>().Last()) + 1, new StatiqDocumentPhase(baseType, parameters.Namespaces) { Engine = razorProjectEngine.Engine }); FieldInfo phasesField = razorProjectEngine.Engine.GetType().GetField("<Phases>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic); phasesField.SetValue(razorProjectEngine.Engine, phases.ToArray()); }