我需要向用户发送电子邮件通知,我需要允许管理员为邮件正文提供模板(也可能是标题)。
我喜欢像string.Format
这样的东西允许我给出命名的替换字符串,所以模板看起来像这样:
Dear {User},
Your job finished at {FinishTime} and your file is available for download at {FileURL}.
Regards,
--
{Signature}
对我来说,最简单的方法是什么?
答案 0 :(得分:30)
以下是可以使用新版C#的人的版本:
// add $ at start to mark string as template
var template = $"Your job finished at {FinishTime} and your file is available for download at {FileURL}."
在一行中 - 现在这是一个完全支持的语言功能(字符串插值)。
答案 1 :(得分:19)
使用模板引擎。 StringTemplate就是其中之一,而且还有很多。
答案 2 :(得分:14)
您可以使用“string.Format”方法:
var user = GetUser();
var finishTime = GetFinishTime();
var fileUrl = GetFileUrl();
var signature = GetSignature();
string msg =
@"Dear {0},
Your job finished at {1} and your file is available for download at {2}.
Regards,
--
{3}";
msg = string.Format(msg, user, finishTime, fileUrl, signature);
它允许您在将来更改内容并且对本地化很友好。
答案 3 :(得分:13)
SmartFormat是一个非常简单的库,可以满足您的所有要求。它专注于撰写“自然语言”文本,非常适合从列表生成数据或应用条件逻辑。
语法与String.Format
极为相似,非常简单易学易用。以下是文档中语法的示例:
Smart.Format("{Name}'s friends: {Friends:{Name}|, |, and}", user)
// Result: "Scott's friends: Michael, Jim, Pam, and Dwight"
该库具有很好的错误处理选项(忽略错误,输出错误,抛出错误)。显然,这对你的例子来说是完美的。
该库是开源的,易于扩展,因此您也可以使用其他功能对其进行增强。
答案 4 :(得分:10)
你可以使用string.Replace(...),最终在for-each中使用所有关键字。如果只有几个关键字,您可以将它们放在这样的一行:
string myString = template.Replace("FirstName", "John").Replace("LastName", "Smith").Replace("FinishTime", DateTime.Now.ToShortDateString());
或者您可以使用Regex.Replace(...),如果您需要更强大的功能和更多选项。
阅读此article on codeproject以查看最快的字符串替换选项。
答案 5 :(得分:6)
基于Benjamin Gruenbaum的回答,在C#版本6中,您可以使用$添加@并且几乎可以使用您的代码,例如:
var text = $@"Dear {User},
Your job finished at {FinishTime} and your file is available for download at {FileURL}.
Regards,
--
{Signature}
";
答案 6 :(得分:5)
实际上,您可以使用XSLT。 您创建一个简单的XML模板:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:template match="TETT">
<p>
Dear <xsl:variable name="USERNAME" select="XML_PATH" />,
Your job finished at <xsl:variable name="FINISH_TIME" select="XML_PATH" /> and your file is available for download at <xsl:variable name="FILE_URL" select="XML_PATH" />.
Regards,
--
<xsl:variable name="SIGNATURE" select="XML_PATH" />
</p>
</xsl:template>
然后创建一个XmlDocument来执行转换: XmlDocument xmlDoc = new XmlDocument();
XmlNode xmlNode = xmlDoc .CreateNode(XmlNodeType.Element, "EMAIL", null);
XmlElement xmlElement= xmlDoc.CreateElement("USERNAME");
xmlElement.InnerXml = username;
xmlNode .AppendChild(xmlElement); ///repeat the same thing for all the required fields
xmlDoc.AppendChild(xmlNode);
之后,应用转换:
XPathNavigator xPathNavigator = xmlDocument.DocumentElement.CreateNavigator();
StringBuilder sb = new StringBuilder();
StringWriter sw = new StringWriter(sb);
XmlTextWriter xmlWriter = new XmlTextWriter(sw);
your_xslt_transformation.Transform(xPathNavigator, null, xmlWriter);
return sb.ToString();
答案 7 :(得分:4)
实现自己的自定义格式化程序可能是一个好主意。
这是你如何做到的。首先,创建一个类型,定义要注入消息的内容。注意:我只是用模板的用户部分来说明这一点......
class JobDetails
{
public string User
{
get;
set;
}
}
接下来,实现一个简单的自定义格式化程序......
class ExampleFormatter : IFormatProvider, ICustomFormatter
{
public object GetFormat(Type formatType)
{
return this;
}
public string Format(string format, object arg, IFormatProvider formatProvider)
{
// make this more robust
JobDetails job = (JobDetails)arg;
switch (format)
{
case "User":
{
return job.User;
}
default:
{
// this should be replaced with logic to cover the other formats you need
return String.Empty;
}
}
}
}
最后,像这样使用它......
string template = "Dear {0:User}. Your job finished...";
JobDetails job = new JobDetails()
{
User = "Martin Peck"
};
string message = string.Format(new ExampleFormatter(), template, job);
...将生成文本“亲爱的Martin Peck。你的工作已经完成......”。
答案 8 :(得分:4)
有一个很好的背景,有一个实现,在Phil Haack的博客上:http://haacked.com/archive/2009/01/14/named-formats-redux.aspx
答案 9 :(得分:4)
一个非常简单的基于正则表达式的解决方案。支持\n
- 样式单字符转义序列和{Name}
- 样式命名变量。
class Template
{
/// <summary>Map of replacements for characters prefixed with a backward slash</summary>
private static readonly Dictionary<char, string> EscapeChars
= new Dictionary<char, string>
{
['r'] = "\r",
['n'] = "\n",
['\\'] = "\\",
['{'] = "{",
};
/// <summary>Pre-compiled regular expression used during the rendering process</summary>
private static readonly Regex RenderExpr = new Regex(@"\\.|{([a-z0-9_.\-]+)}",
RegexOptions.IgnoreCase | RegexOptions.Compiled);
/// <summary>Template string associated with the instance</summary>
public string TemplateString { get; }
/// <summary>Create a new instance with the specified template string</summary>
/// <param name="TemplateString">Template string associated with the instance</param>
public Template(string TemplateString)
{
if (TemplateString == null) {
throw new ArgumentNullException(nameof(TemplateString));
}
this.TemplateString = TemplateString;
}
/// <summary>Render the template using the supplied variable values</summary>
/// <param name="Variables">Variables that can be substituted in the template string</param>
/// <returns>The rendered template string</returns>
public string Render(Dictionary<string, object> Variables)
{
return Render(this.TemplateString, Variables);
}
/// <summary>Render the supplied template string using the supplied variable values</summary>
/// <param name="TemplateString">The template string to render</param>
/// <param name="Variables">Variables that can be substituted in the template string</param>
/// <returns>The rendered template string</returns>
public static string Render(string TemplateString, Dictionary<string, object> Variables)
{
if (TemplateString == null) {
throw new ArgumentNullException(nameof(TemplateString));
}
return RenderExpr.Replace(TemplateString, Match => {
switch (Match.Value[0]) {
case '\\':
if (EscapeChars.ContainsKey(Match.Value[1])) {
return EscapeChars[Match.Value[1]];
}
break;
case '{':
if (Variables.ContainsKey(Match.Groups[1].Value)) {
return Variables[Match.Groups[1].Value].ToString();
}
break;
}
return string.Empty;
});
}
}
var tplStr1 = @"Hello {Name},\nNice to meet you!";
var tplStr2 = @"This {Type} \{contains} \\ some things \\n that shouldn't be rendered";
var variableValues = new Dictionary<string, object>
{
["Name"] = "Bob",
["Type"] = "string",
};
Console.Write(Template.Render(tplStr1, variableValues));
// Hello Bob,
// Nice to meet you!
var template = new Template(tplStr2);
Console.Write(template.Render(variableValues));
// This string {contains} \ some things \n that shouldn't be rendered
\n
,\r
,\\
和\{
转义序列并对其进行了硬编码。您可以轻松添加更多内容或让消费者对其进行定义。RegexOptions.IgnoreCase
标记。Match.Value
回调结束时返回Regex.Replace
而不是空字符串。你也可以抛出异常。{var}
语法,但这可能会干扰本机插值字符串语法。如果要在代码中的字符串文字中定义模板,建议将变量分隔符更改为例如%var%
(正则表达式\\.|%([a-z0-9_.\-]+)%
)或您选择的其他一些更适合用例的语法。答案 10 :(得分:1)
如果您需要非常强大的功能(但实际上不是最简单的方法),您可以托管ASP.NET并将其用作模板引擎。
您将拥有ASP.NET的所有功能来格式化邮件正文。
答案 11 :(得分:1)
如果您使用VB.NET编码,则可以使用XML文字。如果您使用C#进行编码,则可以使用ShartDevelop将VB.NET中的文件放在与C#代码相同的项目中。
答案 12 :(得分:1)
如果有人正在寻找替代品-实际的.NET替代品:
https://github.com/crozone/FormatWith
一个很好的简单可扩展解决方案。谢谢crozone!
因此,使用FormatWith提供的字符串扩展名是两个示例:
/// Use a dictionary that has the tokens as keys with values for the replacement
/// OR
/// Use a poco with properties that match the replacement tokens
//////////////////////////////////
public void testEmailFormatWith()
{
var emailDictionary = new Dictionary<string, object>()
{
{ "User", "Simon" },
{ "FinishTime", DateTime.Now },
{ "FileUrl", new Uri("http://example.com/dictionary") },
{ "Signature", $"Sincerely,{Environment.NewLine}Admin" }
};
var emailTemplate = @"
Dear {User},
Your job finished at {FinishTime} and your file is available for download at {FileURL}.
Regards,
--
{Signature}
";
{
var emailBody = emailTemplate.FormatWith(emailDictionary);
System.Console.WriteLine(emailBody);
}
{
var emailBody = emailTemplate.FormatWith(new MessageValues());
System.Console.WriteLine(emailBody);
}
}
//////////////////////////////////
public class MessageValues
{
public string User { get; set; } = "Simon";
public DateTime FinishTime { get; set; } = DateTime.Now;
public Uri FileURL { get; set; } = new Uri("http://example.com");
public string Signature { get; set; } = $"Sincerely,{Environment.NewLine}Admin";
}
它还允许格式化替换内联格式。例如,尝试将{FinishTime}
更改为{FinishTime:HH:mm:ss}
。