首页 > 解决方案 > 如何动态替换电子邮件模板中的占位符

问题描述

有没有用动态数据替换占位符的好方法?我尝试加载一个模板,然后用元对象中的数据替换所有 {{PLACEHOLDER}} 标记,这是有效的。但是如果我需要添加更多的占位符,我必须在代码中完成,并进行新的部署,所以如果可能的话,我想通过数据库来完成,如下所示:

Table Placeholders
ID, Key (nvarchar(50),  Value (nvarchar(59))
1   {{RECEIVER_NAME}}   meta.receiver
2   {{RESOURCE_NAME}}   meta.resource
3 ..
4 .. and so on

meta 是发送到 BuildTemplate 方法的参数的名称。

因此,当我遍历所有占位符(来自 db)时,我想将值从 db 转换为元对象。我需要参数内的值,而不是获取“meta.receiver”。

GetAllAsync ex.1

public async Task<Dictionary<string, object>> GetAllAsync()
{
     return await _context.EmailTemplatePlaceholders.ToDictionaryAsync(x => x.PlaceholderKey, x => x.PlaceholderValue as object);
}

GetAllAsync ex.2

public async Task<IEnumerable<EmailTemplatePlaceholder>> GetAllAsync()
{
     var result = await _context.EmailTemplatePlaceholders.ToListAsync();
     return result;
}

样本不使用数据库(工作))

private async Task<string> BuildTemplate(string template, dynamic meta)
{
    var sb = new StringBuilder(template);

    sb.Replace("{{RECEIVER_NAME}}", meta.receiver?.ToString());
    sb.Replace("{{RESOURCE_NAME}}", meta.resource?.ToString());    

    return sb.ToString();
}

我希望它如何工作

private async Task<string> BuildTemplate(string template, dynamic meta)
{
    var sb = new StringBuilder(template);

    var placeholders = await _placeholders.GetAllAsync();

    foreach (var placeholder in placeholders)
    {           
        // when using reflection I still get a string like "meta.receiver" instead of meta.receiver, like the object.
        // in other words, the sb.Replace methods gives the same result.
        //sb.Replace(placeholder.Key, placeholder.Value.GetType().GetField(placeholder.Value).GetValue(placeholder.Value));
        sb.Replace(placeholder.Key, placeholder.Value);
    }  

    return sb.ToString();
}

我认为这可能是解决这个问题的更好方法。请告诉我!

标签: c#sqlentity-frameworkdynamicproperty-placeholder

解决方案


我们在开发过程中已经解决了类似的问题。

我们创建了扩展来格式化任何对象。

请查看我们的源代码:

public static string FormatWith(this string format, object source, bool escape = false)
{
    return FormatWith(format, null, source, escape);
}

public static string FormatWith(this string format, IFormatProvider provider, object source, bool escape = false)
{
    if (format == null)
        throw new ArgumentNullException("format");

    List<object> values = new List<object>();
    var rewrittenFormat = Regex.Replace(format,
        @"(?<start>\{)+(?<property>[\w\.\[\]]+)(?<format>:[^}]+)?(?<end>\})+",
        delegate(Match m)
        {
            var startGroup = m.Groups["start"];
            var propertyGroup = m.Groups["property"];
            var formatGroup = m.Groups["format"];
            var endGroup = m.Groups["end"];

            var value = propertyGroup.Value == "0"
                ? source
                : Eval(source, propertyGroup.Value);

            if (escape && value != null)
            {
                value = XmlEscape(JsonEscape(value.ToString()));
            }

            values.Add(value);

            var openings = startGroup.Captures.Count;
            var closings = endGroup.Captures.Count;

            return openings > closings || openings%2 == 0
                ? m.Value
                : new string('{', openings) + (values.Count - 1) + formatGroup.Value
                  + new string('}', closings);
        },
        RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);

    return string.Format(provider, rewrittenFormat, values.ToArray());
}

private static object Eval(object source, string expression)
{
    try
    {
        return DataBinder.Eval(source, expression);
    }
    catch (HttpException e)
    {
        throw new FormatException(null, e);
    }
}

用法很简单:

var body = "[{Name}] {Description} (<a href='{Link}'>See More</a>)";
var model = new { Name="name", Link="localhost", Description="" };
var result = body.FormatWith(model);

推荐阅读