【问题标题】:How to replace placeholders within email template dynamically如何动态替换电子邮件模板中的占位符
【发布时间】:2018-11-10 14:33:55
【问题描述】:

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

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

【问题讨论】:

  • 我想你想要像 angularjs 那样的字符串插值?
  • “代码不工作”不是一个好的描述。如果您在 placeholder.Value 属性中具有正确的值,则代码应该可以工作。如果您不这样做,那么不是您发布的代码不正确,而是您尚未发布的 .GetAllAsync() 方法。请发帖minimal reproducible example
  • 您好 Lasse,感谢您的指正!问题是我尝试了不同类型的 GetAllAsync 返回 IEnumerable、IDictionary 等等,但我遇到了同样的问题。代码有效,但不是我想要的。

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


【解决方案1】:

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

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

请查看我们的源代码:

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

【讨论】:

    【解决方案2】:

    你想这样做:

    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 时,没有从字符串到您需要的对象的转换(不是肯定的样子)。

    【讨论】:

    • 嗨尼克,感谢您的解决方案,但它对我不起作用。我收到错误 CS1503: Argument 1: cannot convert from 'object' to 'string' 当我添加 .ToString() "placeholder.Value.ToString()" 我得到 "Object reference not set to an instance of an object"。
    • 我的错,绝对应该是@​​987654328@。但是,当您的元对象不具有您正在寻找的值时,就会发生此错误。例如,如果placeholder.Valuereceiver,而meta.receiver 不是有效字段,则会抛出此错误。您可以将此语句包装在 try-catch 块中以避免这些崩溃,但终点是您必须确保传入的值是有效的。您是否按照我的建议将数据库从 meta.receiver 更改为 receiver
    【解决方案3】:

    因为您想动态替换模板中的所有占位符,而无需手动一一替换。所以我认为 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;
            }
        }
    

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

    【讨论】:

      【解决方案4】:

      我将 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)";
          }
      }
      

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

      【讨论】:

        猜你喜欢
        • 2014-03-01
        • 1970-01-01
        • 2020-06-01
        • 2018-12-05
        • 1970-01-01
        • 1970-01-01
        • 2012-05-01
        • 2012-03-31
        • 2013-10-26
        相关资源
        最近更新 更多