如何在不破坏SOLID的情况下从不同类型的表中获取记录

问题描述 投票:1回答:1

假设我有不同格式的通知,例如

{UserName}在{ClientName}的帐户中对您进行评论。

客户端{ClientName}正在等待批准。

等等。我希望使用占位符将消息保存在数据库中,并在列出它们时使用dynimacly来替换来自右表的相应名称的占位符。因此,对于上面的示例,我的表将包含以下列:

Message: string
Type: enum [OnePlaceholder, TwoPlaceholders]
UserId: int
ClientId: int

当我从那些表中获取数据以显示它时,我应该检查每条记录的类型,并根据它来格式化消息。

var list = myTable.Select(m => {
    if(n.Type == OnePlaceholder)
        return new { Message = string.Format(n.Text, n.Client.Name) }
    else if(n.Type == TwoPlaceholder) 
        return new { Message = string.Format(n.Text, n.User.Name, n.Client.Name) }
}.ToList();

但这显然不是一个好的解决方案。它还打破了SOLID原则,因为如果有新类型的通知消息,我应该首先在表中添加新列,然后在获取所有通知时添加else if if。所以问题是你可以建议我什么?

asp.net-core entity-framework-core asp.net-core-webapi
1个回答
1
投票

无法帮助自己制作一些“快速”的东西,在我的拙见中,这是一种坚持让你延伸的东西。

让我们从一些数据开始,这些数据通常会以某种方式从数据库中检索出来。因此,将从数据库中检索下一部分中的通知。

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)的解决方案。如果您对此设置有疑问/评论,请随时给我发消息。

希望这对你和其他人有所帮助。

© www.soinside.com 2019 - 2024. All rights reserved.