剃刀引擎字符串布局和部分
本文关键字:布局 引擎 字符串 剃刀 | 更新日期: 2023-09-27 18:32:29
我像这样使用剃须刀引擎:
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
用于电子邮件的主题和电子邮件正文的不同部分,同时使用一种布局,其中我指定了整个电子邮件结构共有的样式(可能看起来像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
{
/// <summary>
/// Resolves templates embedded as resources in a target assembly.
/// </summary>
public class EmbeddedTemplateResolver : ITemplateResolver
{
private readonly Assembly assembly;
private readonly Type type;
private readonly string templateNamespace;
/// <summary>
/// Specify an assembly and the template namespace manually.
/// </summary>
/// <param name="assembly">The assembly where the templates are embedded.</param>
/// <param name="templateNamespace"></param>
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;
}
/// <summary>
/// Uses a type reference to resolve the assembly and namespace where the template resources are embedded.
/// </summary>
/// <param name="type">The type whose namespace is used to scope the manifest resource name.</param>
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
{
/// <summary>
/// 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.
/// </summary>
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;
}
}
更新
以下是缺少的扩展名
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 中调用 RazorEngineConfigurator.Configure(.cs 它就准备好了。
我的模板的路径位于属性.设置.默认.电子邮件模板位置
在我看来,我有这个:
@{ Layout = "_layout.html";}
_layout.html在电子邮件模板文件夹中
这是一个非常标准的 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 引擎 - 实现布局