Не могу помочь себе в создании чего-то «быстрого», которое, по моему скромному мнению, является чем-то твердым для вас, чтобы расширить его.
Давайте начнем с некоторых данных, которые обычно извлекаются из базы данных.в пути.Таким образом, уведомления в следующей части будут извлекаться из базы данных.
var notifications = new[] {
"{user#1} tagged you in a comment in {client#1}'s account.",
"Client {client#1} is waiting for approval.",
"{user#2} assigned workitem {workitem#5} to {user#1}."
};
Единственное, что нас здесь интересует, это то, что заполнители будут заменены значениями в соответствии с тем, что находится внутри заполнителей.Поэтому нам нужно что-то, что может заменить заполнители значениями.Давайте определим что-то для этого, что может сделать это.
public interface IPlaceHolderProcessor
{
string SubstitutePlaceHolders(string text);
}
Это позволяет мне написать следующее для обработки уведомлений.
IPlaceHolderProcessor processor = CreatePlaceHolderProcessor();
List<string> messages = notifications.Select(x => processor.SubstitutePlaceHolders(x)).ToList();
Так что все ... ну, нам нужнонаписать реализацию для интерфейса.Хорошо, давайте напишем реализацию, которая является расширяемой ... и тестируемой.
public class PlaceHolderProcessor : IPlaceHolderProcessor
{
IPlaceHolderValueProvider _valueProvider;
IPlaceHolderParser _parser;
public PlaceHolderProcessor(IPlaceHolderValueProvider valueProvider, IPlaceHolderParser parser)
{
_valueProvider = valueProvider;
_parser = parser;
}
public string SubstitutePlaceHolders(string message)
{
var sb = new StringBuilder();
var idx = 0;
var placeHolders = _parser.FindPlaceHolders(message);
_valueProvider.PreFillValues(placeHolders);
foreach (var placeholder in placeHolders)
{
sb.Append(message.Substring(idx, placeholder.Start - idx));
idx = placeholder.End;
sb.Append(_valueProvider.GetValue(placeholder));
}
sb.Append(message.Substring(idx));
return sb.ToString();
}
}
public interface IPlaceHolderValueProvider
{
string GetValue(PlaceHolder placeholder);
void PreFillValues(IEnumerable<PlaceHolder> placeholders);
}
public interface IPlaceHolderParser
{
IEnumerable<PlaceHolder> FindPlaceHolders(string text);
}
Эта реализация реализует только поиск замены.Как работает сканирование текста или как получаются значения, это не то, о чем заботится эта реализация.Он только знает, что синтаксический анализатор возвратит заполнители и что поставщик значений предоставит значения для замены заполнителей.Единственная задача процессоров - создать строку, в которой заполнители заменяются правильными значениями.
Итак, давайте начнем с создания простого синтаксического анализатора ...
public class DefaultPlaceHolderParser : IPlaceHolderParser
{
public IEnumerable<PlaceHolder> FindPlaceHolders(string text)
{
foreach (Match match in Regex.Matches(text, @"(?<=\{)([^\}]+)(?=\})"))
{
yield return new PlaceHolder { Start = match.Index - 1, End = match.Index + match.Length + 1, Name = match.Value };
}
}
}
Заполнители теперь возвращаютсяэто реализация IPlaceHolderParser.Следующим шагом будет замена их значениями.Поэтому нам нужна реализация IPlaceHolderValueProvider.Давайте определим реализацию для этого.
public abstract class PlaceHolderValueProviderBase : IPlaceHolderValueProvider
{
public abstract string GetValue(PlaceHolder placeholder);
public virtual void PreFillValues(IEnumerable<PlaceHolder> placeholders) { }
}
public class DefaultPlaceHolderValueProvider : PlaceHolderValueProviderBase
{
Dictionary<string, string> _cache;
public DefaultPlaceHolderValueProvider()
{
_cache = new Dictionary<string, string>();
}
public override string GetValue(PlaceHolder placeholder)
{
if (!_cache.ContainsKey(placeholder.Name))
{
_cache[placeholder.Name] = InternalGetValue(placeholder.Name);
}
return _cache[placeholder.Name];
}
public override void PreFillValues(IEnumerable<PlaceHolder> placeholders)
{
// Use an optimized way of retrieving placeholder values and fill the _cache
}
private string InternalGetValue(string placeHolder)
{
var values = placeHolder.Split('#');
var entity = values[0];
var id = int.Parse(values[1]);
// Here you would hit the database to get a single placeholder value.
return $"[{entity}{id:000}]";
}
}
Эта реализация не попадет в базу данных, она, как мы надеемся, предоставляет достаточно указателей, чтобы увидеть, что здесь происходит.Вы можете использовать это в качестве базовой линии для своей собственной реализации.
Кстати ... я кое-что забыл.Метод CreatePlaceHolderProcessor, который является простым фабричным методом для создания подходящей реализации IPlaceHolderProcessor.В моем случае я использовал следующее.
public IPlaceHolderProcessor CreatePlaceHolderProcessor()
{
var valueProvider = new DefaultPlaceHolderValueProvider();
var processor = new PlaceHolderProcessor(valueProvider, new DefaultPlaceHolderParser());
return processor;
}
Это обеспечивает работоспособное и расширяемое (SOLID) решение.Если у вас есть вопросы / замечания относительно этой настройки, не стесняйтесь сообщать мне.
Надеюсь, это поможет вам и другим.