Как динамически заменить заполнители в шаблоне электронной почты - PullRequest
0 голосов
/ 31 мая 2018

Есть ли хороший способ заменить заполнители динамическими данными?Я попытался загрузить шаблон, а затем заменил все {{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

метаэто имя параметра, отправляемого в метод BuildTemplate.

Поэтому, когда я перебираю все метки-заполнители (из БД), я хочу привести значение из БД в мета-объект.Вместо получения 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;
}

sampleне использует 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();
}

Я думаю, что это может быть лучшим решением для этой проблемы.Пожалуйста, дайте мне знать!

Ответы [ 3 ]

0 голосов
/ 31 мая 2018

Мы решили аналогичную проблему в нашей разработке.

Мы создали расширение для форматирования любого объекта.

Пожалуйста, просмотрите наш исходный код:

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);
0 голосов
/ 31 мая 2018

Поскольку вы хотите динамически заменять все заполнители в вашем шаблоне, не заменяя их по одному вручную.Поэтому я думаю, что 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;
        }
    }

Я реализую эту технику в проекте, где я должен динамически генерировать электронную почту из указанных шаблонов.Это работает хорошо для меня.Надеюсь, это решит вашу проблему.

0 голосов
/ 31 мая 2018

Вы хотите сделать это так:

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, нет преобразования строки в нужный вам объект (не совсем то, как это будет выглядеть).

...