如何动态替换电子邮件模板中的占位符

时间:2018-05-31 14:27:37

标签: c# sql entity-framework dynamic property-placeholder

有没有一种用动态数据替换占位符的好方法? 我已经尝试加载模板,然后使用来自元对象的数据替换所有{{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;
}

示例不使用db(工作))

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();
}

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

4 个答案:

答案 0 :(得分:2)

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

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

请查看我们的源代码:

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);

答案 1 :(得分:0)

你想这样做:

sb.Replace(placeholder.Key, meta.GetType().GetField(placeholder.Value).GetValue(meta).ToString())

而不是meta.reciever,您的数据库只会存储receiver

这样,数据库中指定的占位符将替换为元对象中的相应值。缺点是您只能使用此方法从元对象中提取值。但是,从我所看到的情况来看,这对你来说似乎不是一个问题,所以它可能并不重要。

更多说明:您尝试过的问题

//sb.Replace(placeholder.Key, placeholder.Value.GetType().GetField(placeholder.Value).GetValue(placeholder.Value));

首先,您尝试获取整个字符串meta.reciever的类型而不仅仅是meta部分,但是此外,似乎没有从类型的字符串(例如Type.GetType("meta"))。另外,当你使用GetValue时,没有从字符串到你需要的对象的转换(不是肯定的那样)。

答案 2 :(得分:0)

如果要动态替换模板中的所有占位符而不手动逐个替换它们。所以我觉得Regex对这些东西更好。

此函数将获取您要插入的模板以及要与模板绑定的一个对象。此功能将自动替换您的占位符 {{RECEIVER_NAME}}在您的对象中包含值。 您将需要一个包含要绑定的所有属性的类。在本例中,类是MainInvoiceBind。

    public static string Format(string obj,MainInvoiceBind invoice)
    {
        try
        {
            return Regex.Replace(obj, @"{{(?<exp>[^}]+)}}", match =>
            {
                try
                {
                    var p = Expression.Parameter(typeof(MainInvoiceBind), "");
                    var e = System.Linq.Dynamic.DynamicExpression.ParseLambda(new[] { p }, null, match.Groups["exp"].Value);
                    return (e.Compile().DynamicInvoke(invoice) ?? "").ToString();
                }
                catch
                {
                    return "Nill";
                }

            });
        }
        catch
        {
            return string.Empty;
        }
    }

我在一个项目中实现了这项技术,我希望从那里指定模板动态生成电子邮件。它对我有益。希望它能解决你的问题。

答案 3 :(得分:0)

我将 habib 的解决方案更新为较新的System.Linq.Dynamic.Core NuGet软件包,并做了一些小的改进。

此功能将自动使用您对象中的数据替换您的占位符,例如{{RECEIVER_NAME}}。您甚至可以使用某些运算符,因为它使用的是 Linq

public static string Placeholder(string input, object obj)
{
    try {
        var p = new[] { Expression.Parameter(obj.GetType(), "") };

        return Regex.Replace(input, @"{{(?<exp>[^}]+)}}", match => {
            try {
                return DynamicExpressionParser.ParseLambda(p, null, match.Groups["exp"].Value)
                  .Compile().DynamicInvoke(obj)?.ToString();
            }
            catch {
                return "(undefined)";
            }
        });
    }
    catch {
        return "(error)";
    }
}

您还可以使多个对象可访问并命名。