【发布时间】:2010-09-19 10:47:28
【问题描述】:
我有一组用于我的应用程序发送的电子邮件的模板。模板中嵌入了与我的业务对象的属性相对应的代码。
有没有比调用更优雅的方式
string.Replace("{!MyProperty!}", item.MyProperty.ToString())
无数次?也许是 XMLTransform、正则表达式或其他一些魔法?我正在使用 C# 3.5。
【问题讨论】:
我有一组用于我的应用程序发送的电子邮件的模板。模板中嵌入了与我的业务对象的属性相对应的代码。
有没有比调用更优雅的方式
string.Replace("{!MyProperty!}", item.MyProperty.ToString())
无数次?也许是 XMLTransform、正则表达式或其他一些魔法?我正在使用 C# 3.5。
【问题讨论】:
首先,当我这样做时,我使用 StringBuilder.Replace(),因为我发现它的性能更适合使用 3 个或更多替换。
当然还有其他方法,但我发现通常不值得尝试其他项目。
我猜你也许可以使用反射来自动替换,这可能是唯一“更好”的方法。
【讨论】:
public static string Translate(string pattern, object context)
{
return Regex.Replace(pattern, @"\{!(\w+)!}", match => {
string tag = match.Groups[1].Value;
if (context != null)
{
PropertyInfo prop = context.GetType().GetProperty(tag);
if (prop != null)
{
object value = prop.GetValue(context);
if (value != null)
{
return value.ToString();
}
}
}
return "";
});
}
Translate("Hello {!User!}. Welcome to {!GroupName!}!", new {
User = "John",
GroupName = "The Community"
}); // -> "Hello John. Welcome to The Community!"
【讨论】:
有一个内置的 WebControl,System.Web.UI.WebControls.MailDefinition,它执行string replacements(除其他外)。可惜他们将它与 app.config 中的 Smtp 设置和 Web 控件紧密耦合,然后将其密封以防止继承者使用。
但是,它确实可以处理邮件模板引擎中最可能需要的一些内容——来自文件的正文、html 电子邮件、嵌入对象等。Reflector 显示实际替换是通过 foreach 循环和正则表达式处理的.Replace - 这对我来说也是一个合理的选择。
快速浏览一下就会发现,如果您可以使用 app.config 中的发件人地址(您可以在之后在返回的 MailMessage 上更改它),那么您只需要嵌入资源的所有者控件或 BodyFileName。
如果您使用的是 ASP.NET 或者可以忍受这些限制 - 我会选择 MailDefinition。否则,只需对字典和 Regex.Replace 进行 foreach。由于身体的重复分配,它有点占用内存 - 但它们的寿命很短,应该不会造成太大问题。
var replacements = new Dictionary<string, object>() {
{ "Property1", obj.Property1 },
{ "Property2", obj.Property2 },
{ "Property3", obj.Property3 },
{ "Property4", obj.Property4 },
}
foreach (KeyValuePair<string, object> kvp in replacement) {
body = Regex.Replace(body, kvp.Key, kvp.Value.ToString());
}
如果你真的有很多属性,那么先用 Regex.Match 读取你的正文,然后再反射到属性。
【讨论】:
您可以使用正则表达式来执行此操作,但您的正则表达式替换也因每个属性而异。我会坚持使用string.Replace。
使用反射检索属性并循环替换:
foreach (string property in properties)
{
string.Replace("{!"+property+"!}",ReflectionHelper.GetStringValue(item,property));
}
只需实现您的ReflectionHelper.GetStringValue 方法并使用反射来检索您的项目对象类型的所有属性。
【讨论】:
在查看了之前包含的示例之后,我想我应该看看真正的代码。 @mark-brackett 你比你知道的更接近。
//The guts of MailDefinition.CreateMailMessage
//from https://github.com/Microsoft/referencesource/blob/master/System.Web/UI/WebControls/MailDefinition.cs
if (replacements != null && !String.IsNullOrEmpty(body)) {
foreach (object key in replacements.Keys) {
string fromString = key as string;
string toString = replacements[key] as string;
if ((fromString == null) || (toString == null)) {
throw new ArgumentException(SR.GetString(SR.MailDefinition_InvalidReplacements));
}
// DevDiv 151177
// According to http://msdn2.microsoft.com/en-us/library/ewy2t5e0.aspx, some special
// constructs (starting with "$") are recognized in the replacement patterns. References of
// these constructs will be replaced with predefined strings in the final output. To use the
// character "$" as is in the replacement patterns, we need to replace all references of single "$"
// with "$$", because "$$" in replacement patterns are replaced with a single "$" in the
// final output.
toString = toString.Replace("$", "$$");
body = Regex.Replace(body, fromString, toString, RegexOptions.IgnoreCase);
}
}
【讨论】: