Linq - 如何将反射与where和contains结合使用

3
我想创建一个动态方法,通过在where子句中使用contains来选择数据。我找到了很多关于表达式的信息,但我找不到可行的组合。
我发布了我的方法,并且通过使用反射或类似方法搜索解决方案,我卡在了这一行:

.Where(p => p.Description != null && p.Description.ToLower().Contains(lowerFilter));

如何通过最佳实践解决这个问题?
选择方法:
public List<TEntity> GetItems(string sortBy, string sort, string? filter)
{
    IQueryable<TEntity> items = dbContext.Set<TEntity>();

    if (filter != null && filter.Length > 0)
    {
        string lowerFilter = filter.ToLower(CultureInfo.CurrentCulture);

        items = items
            .Where(p => p.Description != null && p.Description.ToLower().Contains(lowerFilter));
    }

    if (!string.IsNullOrEmpty(sortBy))
    {
        if (!string.IsNullOrEmpty(sort))
        {
            items = items.AsQueryable().OrderBy($"{sortBy} {sort}");
        }
    }

    List<TEntity> itemsList = items.ToList();

    return itemsList;
}

对不起,但我有点困惑。你的GetItems方法是一个通用方法。你确定每个TEntity都有Description列吗?或者这就是你在尝试解决的问题吗? - undefined
我正在寻找一种替代的方法,因为当前的方法不适用于泛型。可以通过动态指定字符串来命名列的名称。 - undefined
我只是想在这里分享我的想法。即使你通过反射、EF约定或其他方法找到了实现这一点的方式,我认为你的GetItems方法会变得混乱,因为要么你的代码会有多个if-else语句,要么where子句会变得更重。不确定你在这里的使用情况,但在朝这个方向努力之前,请重新审视一下。 - undefined
2个回答

1

你可以使用动态表达式生成器来实现这个功能:

这个通用的扩展方法可以帮助你为所有IQueryable实现Contains

public static IQueryable<T> ContainsByField<T>(this IQueryable<T> q, string field, string value)
{
    var eParam = Expression.Parameter(typeof(T), "e");
    var method = field.GetType().GetMethod("Contains");
    var call = Expression.Call(Expression.Property(eParam, field), method, Expression.Constant(value.ToLower()));
    var lambdaExpression = Expression.Lambda<Func<T, bool>>(
        Expression.AndAlso(
            Expression.NotEqual(Expression.Property(eParam, field), Expression.Constant(null)),
            call
        ),
        eParam
    );

    return q.Where(lambdaExpression);
}

然后你只需要这样简单地调用:

items = items.ContainsByField("Description", lowerFilter);

这正是我要找的。我只需要将带有GetMethod的那一行改为typeof(string).GetMethod("Contains", new[] { typeof(string) }),因为我在那里遇到了"找到多个匹配项"的错误。 在我的测试中,我只遇到了奇怪的行为。它在与真实的MSSQL数据库进行集成测试时正常工作。但是,在针对InMemoryDatabase进行单元测试时,值"Test1"找不到。 - undefined
如果我的回答对你有帮助,请将其标记为已接受的答案。我测试了这段代码,它只适用于这个特定的目的,我认为你遇到的问题与此问题无关。因为我的代码中没有Test1等内容,所以很难从你的描述中理解问题所在。 - undefined
我已经找到了不同行为的原因。在你的表达式中,.Description.ToLower().Contains(...) 缺少了 .ToLower()。因此,它只能在大小写不敏感的数据库上工作。也许你可以调整一下你的代码?我一直无法使其正常工作。 - undefined

0
你的linq查询应该可以正常工作。
.Where(p => p.Description != null && p.Description.ToLower().Contains(lowerFilter));

就最佳实践而言,目前我能看到的唯一问题是不必要的嵌套if语句。你可以简化为:
 if (!string.IsNullOrEmpty(sortBy) && !string.IsNullOrEmpty(sort))
    {
        items = items.AsQueryable().OrderBy($"{sortBy} {sort}");
    }

1
很遗憾,由于我使用的是一种未知类型和描述,所以无法实现。我正在寻找一种用反射或类似方法替换这行代码的解决方案。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接