Asp.net core 如何在不破坏实体的情况下从不同类型的表中获取记录

Asp.net core 如何在不破坏实体的情况下从不同类型的表中获取记录,asp.net-core,entity-framework-core,asp.net-core-webapi,Asp.net Core,Entity Framework Core,Asp.net Core Webapi,假设我有不同格式的通知,例如 {UserName}tag在{ClientName}的帐户中为您添加注释 客户端{ClientName}正在等待批准 等等。我希望在数据库中保留带有占位符的消息,并在列出它们时使用右表中相应的名称替换占位符。 因此,对于上述示例,我的表将包含以下列: Message: string Type: enum [OnePlaceholder, TwoPlaceholders] UserId: int ClientId: int 当我从这些表中提取数据来显示它时,我应该检

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

{UserName}tag在{ClientName}的帐户中为您添加注释

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

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

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();
但这显然不是一个好的解决方案。它还打破了坚实的原则,因为若有新类型的通知消息,我应该首先在表中添加新列,然后在获取所有通知时添加else if检查。
所以问题是你能给我什么建议呢?

我忍不住要编造一些“快速”的东西,依我之见,这些东西对你来说是可以扩展的

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

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();
这提供了一个可行且可扩展的(固体)解决方案。如果您对此设置有任何疑问/意见,请随时给我留言


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

消息列中的任何内容都已包含您实际值的占位符。为什么不写一些东西来扫描消息中的占位符,然后用适当的值替换它们呢。您可以为此使用常规表达式。因为它是相同的,所以无论我是使用列类型识别消息中有多少占位符,还是使用正则表达式扫描消息,都无关紧要。。想象一下,如果我有最多30个占位符的消息,我应该有30个if占位符吗?此外,每种格式/类型的占位符顺序可能不同。让我澄清一下。删除Type、UserId和ClientId列,任何新通知都可能引入包含id的新列,这是您不想要的。更改文本中的占位符,以指定要在占位符中使用的表和id,以及目标表的属性。这可能看起来像“用户{person#1234}登录”。使用占位符中的信息编写代码以实际值替换占位符。@dbugy这听起来是个不错的解决方案。谢谢你:)你太棒了!非常感谢您提供了非常好的解决方案:pIn-DefaultValueProvider我应该如何访问数据库?因为我必须将这些类注册为服务,以便能够将DbContext注入构造函数。您可以将DbContext注入DefaultPlaceholder ValueProvider,并在服务方法中构建值提供程序。
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 };
        }
    }
}
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}]";
    }
}
public IPlaceHolderProcessor CreatePlaceHolderProcessor()
{
    var valueProvider = new DefaultPlaceHolderValueProvider();
    var processor = new PlaceHolderProcessor(valueProvider, new DefaultPlaceHolderParser());
    return processor;
}