【问题标题】:SQL LINQ Evaluate Comma-Separated String Contains ValueSQL LINQ 评估逗号分隔的字符串是否包含值
【发布时间】:2020-09-24 15:39:50
【问题描述】:

我正在使用 EFCore Linq,但遇到了问题。我有一个存储逗号分隔字符串的表,我需要在 where 子句中使用它来仅过滤那些包含特定值的字符串。这是我的 Linq:

var volunteers = context.Volunteers
               .Where(x => x.StatusId == 1
                     && x.RoleIds.Split(',', StringSplitOptions.None).Contains("1")
                ).ToList();

该列是 RoleIds。我收到的错误是 Linq 查询无法翻译。我需要在这里做什么?谢谢!

【问题讨论】:

  • 问题是可能没有 SQL 函数可以拆分字符串和/或检查字符串数组是否包含字符串和/或 EFCore 没有实现这些 SQL 函数和C# 函数。你可以试试x.RoleIds.StartsWith("1,") || x.RoleIds.Contains(",1,") || x.RoleIds.EndsWith(",1") || x.RoleIds == "1"),这应该是一样的,检查第一项是1,内部项目是1,最后一项是1,还是只有一个项目1。
  • 谢谢,我这样做了,它有效:(x.RoleIds + ",").Contains("1,")
  • 请注意,您的缩写形式也会找到 11、21 等。
  • 感谢 ckuri 的提醒。实际数字要大得多且独一无二。应该可以。但是好点!
  • 另请注意,如果角色 ID 是字符串中的最后一个,"1," 将不匹配,因此需要所有四个测试。

标签: c# linq entity-framework-core


【解决方案1】:

如果您可以使用 LINQKit(或滚动您自己的重点版本),您可以创建扩展来为您处理测试:

public static class IQueryableExt { // using LINQKit
    // string fieldExpr(T row) - function returning multiple value string field to test
    // delimiter - string separator between values in test field
    // value - string value to find in values of test field
    // dbq.Where(r => fieldExpr(r).Split(delimiter).Contains(value))
    public static IQueryable<T> WhereSplitContains<T>(this IQueryable<T> dbq, Expression<Func<T, string>> fieldExpr, string delimiter, string value) {
        var pred = PredicateBuilder.New<T>(r => fieldExpr.Invoke(r) == value);
        pred = pred.Or(r => fieldExpr.Invoke(r).StartsWith(value + delimiter));
        pred = pred.Or(r => fieldExpr.Invoke(r).EndsWith(delimiter + value));
        pred = pred.Or(r => fieldExpr.Invoke(r).Contains(delimiter + value + delimiter));

        return dbq.Where((Expression<Func<T, bool>>)pred.Expand());
    }

    // values - string values, one of which to find in values of test field
    // string fieldExpr(T row) - function returning multiple value string field to test
    // delimiter - string separator between values in test field
    // dbq.Where(r => values.Any(value => fieldExpr(r).Split(delimiter).Contains(value)))
    public static IQueryable<T> WhereAnySplitContains<T>(this IQueryable<T> dbq, IEnumerable<string> values, Expression<Func<T, string>> fieldExpr, string delimiter) {
        var pred = PredicateBuilder.New<T>();
        foreach (var value in values) {
            pred = pred.Or(r => fieldExpr.Invoke(r) == value);
            pred = pred.Or(r => fieldExpr.Invoke(r).StartsWith(value + delimiter));
            pred = pred.Or(r => fieldExpr.Invoke(r).EndsWith(delimiter + value));
            pred = pred.Or(r => fieldExpr.Invoke(r).Contains(delimiter + value + delimiter));
        }

        return dbq.Where((Expression<Func<T, bool>>)pred.Expand());
    }
    public static IQueryable<T> WhereSplitContainsAny<T>(this IQueryable<T> dbq, Expression<Func<T, string>> fieldExpr, string delimiter, IEnumerable<string> values) =>
        dbq.WhereAnySplitContains(values, fieldExpr, delimiter);
    public static IQueryable<T> WhereSplitContainsAny<T>(this IQueryable<T> dbq, Expression<Func<T, string>> fieldExpr, string delimiter, params string[] values) =>
        dbq.WhereAnySplitContains(values, fieldExpr, delimiter);

    // values - string values, all of which to find in values of test field
    // string fieldExpr(T row) - function returning multiple value string field to test
    // delimiter - string separator between values in test field
    // dbq.Where(r => values.All(value => fieldExpr(r).Split(delimiter).Contains(value)))
    public static IQueryable<T> WhereAllSplitContains<T>(this IQueryable<T> dbq, IEnumerable<string> values, Expression<Func<T, string>> fieldExpr, string delimiter) {
        var pred = PredicateBuilder.New<T>();
        foreach (var value in values) {
            var subPred = PredicateBuilder.New<T>(r => fieldExpr.Invoke(r) == value);
            subPred = subPred.Or(r => fieldExpr.Invoke(r).StartsWith(value + delimiter));
            subPred = subPred.Or(r => fieldExpr.Invoke(r).EndsWith(delimiter + value));
            subPred = subPred.Or(r => fieldExpr.Invoke(r).Contains(delimiter + value + delimiter));
            pred = pred.And(subPred);
        }

        return dbq.Where((Expression<Func<T, bool>>)pred.Expand());
    }
    public static IQueryable<T> WhereSplitContainsAll<T>(this IQueryable<T> dbq, Expression<Func<T, string>> fieldExpr, string delimiter, IEnumerable<string> values) =>
        dbq.WhereAllSplitContains(values, fieldExpr, delimiter);
    public static IQueryable<T> WhereSplitContainsAll<T>(this IQueryable<T> dbq, Expression<Func<T, string>> fieldExpr, string delimiter, params string[] values) =>
        dbq.WhereAllSplitContains(values, fieldExpr, delimiter);
}

使用这些扩展,您的查询将是:

var volunteers = context.Volunteers.Where(x => x.StatusId == 1)
                                   .WhereSplitContains(r => r.RoleIds, ",", "1")
                                   .ToList();

如果您有多个值,则可以使用其他变体:

var volunteers = context.Volunteers.Where(x => x.StatusId == 1)
                                   .WhereSplitContainsAny(r => r.RoleIds, ",", "1", "2")
                                   .ToList();

【讨论】:

  • 效果很好 NetMage,非常感谢!只是另一个想法 - 我将如何修改扩展名,以便它接收字符串值列表而不是单个值“1”?
  • @Franky 您想要对值列表进行 OR 或 AND 处理吗?
  • @Franky 我为值列表添加了变体。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-26
  • 2013-05-31
  • 1970-01-01
相关资源
最近更新 更多