首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >不使用泛型向IQueryable添加where子句

不使用泛型向IQueryable添加where子句
EN

Stack Overflow用户
提问于 2022-10-20 09:57:10
回答 1查看 38关注 0票数 0

我正在尝试编写一段代码,用于从数据库中选择一个值。作为一个测试,我已经创建了这个测试版本的方法,“工作”,但不是很好。

代码语言:javascript
复制
private object? FindObjectByProperty(IQueryable query, PropertyInfo primaryKeyProperty, object lookupValue)
{
    object? result = null;
    foreach (var value in query)
    {
        if (!primaryKeyProperty.GetValue(value)!.Equals(lookupValue))
            continue;

        result = value;
        break;
    }

    return result;
}

代码将遍历查询并找到第一个与相等项匹配的条目。但是,如果查询包含数百万行,这很可能是当IQueryable实际上是一个DbSet时(在这个用例中是这样的)。只是我不知道什么是T,因为它可能是我的EF核心数据上下文中的任何一个DbSets。

我最后想要的是一些看起来像这样的东西.

代码语言:javascript
复制
private object? FindObjectByProperty(IQueryable query, PropertyInfo primaryKeyProperty, object lookupValue)
{
    return query.Where( x => x.*primaryKeyProperty* == lookupValue).FirstOrDefault();
}

上面的代码是伪代码,我遇到的问题是查询没有可用的.Where。另外,需要将primaryKeyProperty转换为实际属性。

其思想是,在执行此代码时,最终执行查询将生成一个sql语句,该语句选择单个项并返回它。

有人能帮忙解决这个问题吗?

更新:我正在研究这个问题的解决方案,到目前为止,这是我想出来的

代码语言:javascript
复制
//using System.Linq.Dynamic.Core;

private object? FindObjectByProperty(IQueryable query, PropertyInfo primaryKeyProperty, object lookupValue)
{
    var parameter = Expression.Parameter(query.ElementType);
    var e1 = Expression.Equal(Expression.Property(parameter, primaryKeyProperty.Name), Expression.Constant(lookupValue));
    var lambda = Expression.Lambda<Func<object, bool>>(e1, parameter);
    return query.Where(lambda).FirstOrDefault();
}

这是失败的,因为func使用对象,而它确实需要真正的类型。想弄清楚这点。越来越近了。

更新2:这是我需要的答案

代码语言:javascript
复制
private object? FindObjectByProperty(IQueryable query, PropertyInfo primaryKeyProperty, object lookupValue)
{
    var parameter = Expression.Parameter(query.ElementType);
    var propExpr = Expression.Property(parameter, primaryKeyProperty);
    var lambdaBody = Expression.Equal(propExpr, Expression.Constant(lookupValue, primaryKeyProperty.PropertyType));
    var filterEFn = Expression.Lambda(lambdaBody, parameter);
    return query.Where(filterEFn).FirstOrDefault();
}

区别在于,Expression.Lambda不再试图使用泛型来定义func。这是我需要做的唯一改变,以使代码功能符合我的要求。

在我使用的测试用例中,用于查找值的test如下所示.

代码语言:javascript
复制
SELECT TOP(1) [g].[Id], [g].[Deleted], [g].[Guid], [g].[Name], [g].[ParentId]
FROM [Glossaries].[Glossaries] AS [g]
WHERE [g].[Id] = CAST(3 AS bigint)

表Glossaries.Glossaries由输入查询提供。列名Id由primaryKeyProperty提供,数字3由lookupValue提供。

这非常适合我的需要,因为我只需要选择一行而不是其他,这样我就可以在需要时有效地加载property属性,而不是以前。

此外,此代码将被重用到许多不同的表中。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-10-20 14:35:44

为了将Where添加到无法访问实际IQueryable<T>IQueryable中,需要后退一步(或向上?)在编译时调用Where,在运行时构建Where调用,以及谓词lambda:

代码语言:javascript
复制
public static class DBExt {
    public static IQueryable WherePropertyIs<T2>(this IQueryable src, PropertyInfo propInfo, T2 propValue) {
        // return src.Where(s => s.{propInfo} == propValue)
        // (T s)
        var sParam = Expression.Parameter(src.ElementType, "s");
        // s.propInfo
        var propExpr = Expression.Property(sParam, propInfo);
        // s.{propInfo} == propValue
        var lambdaBody = Expression.Equal(propExpr, Expression.Constant(propValue));
        // (T s) => s.{PropInfo} == propValue
        var filterEFn = Expression.Lambda(lambdaBody, sParam);

        var origQuery = src.Expression;
        // IQueryable<Tx>.Where<Tx, Expression<Func<Tx, bool>>>()
        var whereGenericMI = typeof(Queryable).GetMethods("Where", 2).Where(mi => mi.GetParameters()[1].ParameterType.GenericTypeArguments[0].GenericTypeArguments.Length == 2).First();
        // IQueryable<T>.Where<T, Expression<Func<T, bool>>>()
        var whereMI = whereGenericMI.MakeGenericMethod(src.ElementType);
        // src.Where(s => s.{propertyInfo} == propValue)
        var newQuery = Expression.Call(whereMI, origQuery, filterEFn);

        return src.Provider.CreateQuery(newQuery);
    }
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/74137941

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档