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());
        }
Beispiel #3
0
        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());
        }