Using Razor outside of MVC in .NET Core

后端 未结 6 2125
南旧
南旧 2020-11-27 11:05

I would like to use Razor as a templating engine in a .NET console application that I\'m writing in .NET Core.

The standalone Razor engines I\'ve come across (RazorE

6条回答
  •  醉酒成梦
    2020-11-27 11:20

    There's a working example for .NET Core 1.0 at aspnet/Entropy/samples/Mvc.RenderViewToString. Since this might change or go away, I'll detail the approach I'm using in my own applications here.

    Tl;dr - Razor works really well outside of MVC! This approach can handle more complex rendering scenarios like partial views and injecting objects into views as well, although I'll just demonstrate a simple example below.


    The core service looks like this:

    RazorViewToStringRenderer.cs

    using System;
    using System.IO;
    using Microsoft.AspNetCore.Http;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.Abstractions;
    using Microsoft.AspNetCore.Mvc.ModelBinding;
    using Microsoft.AspNetCore.Mvc.Razor;
    using Microsoft.AspNetCore.Mvc.Rendering;
    using Microsoft.AspNetCore.Mvc.ViewFeatures;
    using Microsoft.AspNetCore.Routing;
    
    namespace RenderRazorToString
    {
        public class RazorViewToStringRenderer
        {
            private readonly IRazorViewEngine _viewEngine;
            private readonly ITempDataProvider _tempDataProvider;
            private readonly IServiceProvider _serviceProvider;
    
            public RazorViewToStringRenderer(
                IRazorViewEngine viewEngine,
                ITempDataProvider tempDataProvider,
                IServiceProvider serviceProvider)
            {
                _viewEngine = viewEngine;
                _tempDataProvider = tempDataProvider;
                _serviceProvider = serviceProvider;
            }
    
            public async Task RenderViewToString(string name, TModel model)
            {
                var actionContext = GetActionContext();
    
                var viewEngineResult = _viewEngine.FindView(actionContext, name, false);
    
                if (!viewEngineResult.Success)
                {
                    throw new InvalidOperationException(string.Format("Couldn't find view '{0}'", name));
                }
    
                var view = viewEngineResult.View;
    
                using (var output = new StringWriter())
                {
                    var viewContext = new ViewContext(
                        actionContext,
                        view,
                        new ViewDataDictionary(
                            metadataProvider: new EmptyModelMetadataProvider(),
                            modelState: new ModelStateDictionary())
                        {
                            Model = model
                        },
                        new TempDataDictionary(
                            actionContext.HttpContext,
                            _tempDataProvider),
                        output,
                        new HtmlHelperOptions());
    
                    await view.RenderAsync(viewContext);
    
                    return output.ToString();
                }
            }
    
            private ActionContext GetActionContext()
            {
                var httpContext = new DefaultHttpContext
                {
                    RequestServices = _serviceProvider
                };
    
                return new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
            }
        }
    }
    

    A simple test console app just needs to initialize the service (and some supporting services), and call it:

    Program.cs

    using System;
    using System.Diagnostics;
    using System.IO;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.AspNetCore.Hosting.Internal;
    using Microsoft.AspNetCore.Mvc.Razor;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.FileProviders;
    using Microsoft.Extensions.ObjectPool;
    using Microsoft.Extensions.PlatformAbstractions;
    
    namespace RenderRazorToString
    {
        public class Program
        {
            public static void Main()
            {
                // Initialize the necessary services
                var services = new ServiceCollection();
                ConfigureDefaultServices(services);
                var provider = services.BuildServiceProvider();
    
                var renderer = provider.GetRequiredService();
    
                // Build a model and render a view
                var model = new EmailViewModel
                {
                    UserName = "User",
                    SenderName = "Sender"
                };
                var emailContent = renderer.RenderViewToString("EmailTemplate", model).GetAwaiter().GetResult();
    
                Console.WriteLine(emailContent);
                Console.ReadLine();
            }
    
            private static void ConfigureDefaultServices(IServiceCollection services)
            {
                var applicationEnvironment = PlatformServices.Default.Application;
                services.AddSingleton(applicationEnvironment);
    
                var appDirectory = Directory.GetCurrentDirectory();
    
                var environment = new HostingEnvironment
                {
                    WebRootFileProvider = new PhysicalFileProvider(appDirectory),
                    ApplicationName = "RenderRazorToString"
                };
                services.AddSingleton(environment);
    
                services.Configure(options =>
                {
                    options.FileProviders.Clear();
                    options.FileProviders.Add(new PhysicalFileProvider(appDirectory));
                });
    
                services.AddSingleton();
    
                var diagnosticSource = new DiagnosticListener("Microsoft.AspNetCore");
                services.AddSingleton(diagnosticSource);
    
                services.AddLogging();
                services.AddMvc();
                services.AddSingleton();
            }
        }
    }
    

    This assumes that you have a view model class:

    EmailViewModel.cs

    namespace RenderRazorToString
    {
        public class EmailViewModel
        {
            public string UserName { get; set; }
    
            public string SenderName { get; set; }
        }
    }
    

    And layout and view files:

    Views/_Layout.cshtml

    
    
    
    
        
    @RenderBody()
    Thanks,
    @Model.SenderName

    Views/EmailTemplate.cshtml

    @model RenderRazorToString.EmailViewModel
    @{ 
        Layout = "_EmailLayout";
    }
    
    Hello @Model.UserName,
    
    

    This is a generic email about something.

提交回复
热议问题