RazorEngine字符串布局和部分?

我像这样使用razor引擎:

public class EmailService : IService { private readonly ITemplateService templateService; public EmailService(ITemplateService templateService) { if (templateService == null) { throw new ArgumentNullException("templateService"); } this.templateService = templateService; } public string GetEmailTemplate(string templateName) { if (templateName == null) { throw new ArgumentNullException("templateName"); } Assembly assembly = Assembly.GetAssembly(typeof(EmailTemplate)); Stream stream = assembly.GetManifestResourceStream(typeof(EmailTemplate), "{0}.cshtml".FormatWith(templateName)); string template = stream.ReadFully(); return template; } public string GetEmailBody(string templateName, object model = null) { if (templateName == null) { throw new ArgumentNullException("templateName"); } string template = GetEmailTemplate(templateName); string emailBody = templateService.Parse(template, model, null, null); return emailBody; } } 

我使用的模板服务是注入的,虽然它只是一个默认实现:

  internal ITemplateService InstanceDefaultTemplateService() { ITemplateServiceConfiguration configuration = new TemplateServiceConfiguration(); ITemplateService service = new TemplateService(configuration); return service; } 

特别是在这种情况下,我将从这些模板构建电子邮件。 我希望能够使用@section s作为email’a主题,以及电子邮件正文的不同部分,同时使用布局,我指定整个电子邮件结构共有的样式(看起来像MailChimp之一)可能)。

问题是双重的:

  • 如何在RazorEngine指定布局?
  • 如何从字符串(或流)指定这些布局? 正如您所看到的,我使用嵌入式资源来存储razor电子邮件模板。

更新

也许我不清楚,但我指的是RazorEngine库。

经过一些挖掘支持布局后,我们只需要使用_Layout而不是Layout来声明它们

至于嵌入式资源问题,我实现了以下ITemplateResolver

 using System; using System.IO; using System.Reflection; using Bruttissimo.Common; using RazorEngine.Templating; namespace Website.Extensions.RazorEngine { ///  /// Resolves templates embedded as resources in a target assembly. ///  public class EmbeddedTemplateResolver : ITemplateResolver { private readonly Assembly assembly; private readonly Type type; private readonly string templateNamespace; ///  /// Specify an assembly and the template namespace manually. ///  /// The assembly where the templates are embedded. ///  public EmbeddedTemplateResolver(Assembly assembly, string templateNamespace) { if (assembly == null) { throw new ArgumentNullException("assembly"); } if (templateNamespace == null) { throw new ArgumentNullException("templateNamespace"); } this.assembly = assembly; this.templateNamespace = templateNamespace; } ///  /// Uses a type reference to resolve the assembly and namespace where the template resources are embedded. ///  /// The type whose namespace is used to scope the manifest resource name. public EmbeddedTemplateResolver(Type type) { if (type == null) { throw new ArgumentNullException("type"); } this.assembly = Assembly.GetAssembly(type); this.type = type; } public string Resolve(string name) { if (name == null) { throw new ArgumentNullException("name"); } Stream stream; if (templateNamespace == null) { stream = assembly.GetManifestResourceStream(type, "{0}.cshtml".FormatWith(name)); } else { stream = assembly.GetManifestResourceStream("{0}.{1}.cshtml".FormatWith(templateNamespace, name)); } if (stream == null) { throw new ArgumentException("EmbeddedResourceNotFound"); } string template = stream.ReadFully(); return template; } } } 

然后你就像这样连线:

  internal static ITemplateService InstanceTemplateService() { TemplateServiceConfiguration configuration = new TemplateServiceConfiguration { Resolver = new EmbeddedTemplateResolver(typeof(EmailTemplate)) }; ITemplateService service = new TemplateService(configuration); return service; } 

您传递的类型仅用于引用嵌入资源的程序集和命名空间。

 namespace Website.Domain.Logic.Email.Template { ///  /// The purpose of this class is to expose the namespace of razor engine templates in order to /// avoid having to hard-code it when retrieving the templates embedded as resources. ///  public sealed class EmailTemplate { } } 

最后一件事,为了让我们的解析器解析模板,我们必须像这样解决它们:

 ITemplate template = templateService.Resolve(templateName, model); string body = template.Run(); return body; 

.Run只是一个简单的扩展方法,因为我找不到ViewBag任何用途。

 public static class ITemplateExtensions { public static string Run(this ITemplate template) { ExecuteContext context = new ExecuteContext(); string result = template.Run(context); return result; } } 

UPDATE

这是缺少的扩展

  public static string FormatWith(this string text, params object[] args) { return string.Format(text, args); } public static string ReadFully(this Stream stream) { using (StreamReader reader = new StreamReader(stream)) { return reader.ReadToEnd(); } } 

我需要提供自己的布局作为字符串或文件名。 这是我如何解决这个问题(基于这篇博文 )

 public static class RazorEngineConfigurator { public static void Configure() { var templateConfig = new TemplateServiceConfiguration { Resolver = new DelegateTemplateResolver(name => { //no caching cause RazorEngine handles that itself var emailsTemplatesFolder = HttpContext.Current.Server.MapPath(Properties.Settings.Default.EmailTemplatesLocation); var templatePath = Path.Combine(emailsTemplatesFolder, name); using (var reader = new StreamReader(templatePath)) // let it throw if doesn't exist { return reader.ReadToEnd(); } }) }; RazorEngine.Razor.SetTemplateService(new TemplateService(templateConfig)); } } 

然后我在Global.asax.cs中调用RazorEngineConfigurator.Configure()并准备就绪。

我的模板的路径位于Properties.Settings.Default.EmailTemplatesLocation中

在我看来,我有这个:

 @{ Layout = "_layout.html";} 

_layout.html位于emailsTemplatesFolder中

这是一个非常标准的HTML,中间有一个@RenderBody()调用。

据我所知,RazorEngine使用模板名称(在本例中为“_layout.html”)作为其缓存的键,因此每个模板只调用一次配置器中的委托。

我认为它将解析器用于它还不知道的每个模板名称。

看起来其他人为你解决了这个问题。

https://github.com/aqueduct/Appia/blob/master/src/Aqueduct.Appia.Razor/RazorViewEngine.cs

您想要的代码位于第二个ExecuteView方法中。 虽然他们正在创建自己的视图引擎,但您可以创建自己的自定义模板解决方案并使用类似的东西。 基本上,他们正在寻找模板的布局属性,如果它存在进行搜索并替换布局中的内容。

这是RazorEngine自定义模板的链接:

http://razorengine.codeplex.com/wikipage?title=Building%20Custom%20Base%20Templates&referringTitle=Documentation

这是我找到你的解决方案的地方:

.NET Razor引擎 – 实现布局