我正在尝试编写一段代码,用于从数据库中选择一个值。作为一个测试,我已经创建了这个测试版本的方法,“工作”,但不是很好。
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。
我最后想要的是一些看起来像这样的东西.
private object? FindObjectByProperty(IQueryable query, PropertyInfo primaryKeyProperty, object lookupValue)
{
return query.Where( x => x.*primaryKeyProperty* == lookupValue).FirstOrDefault();
}上面的代码是伪代码,我遇到的问题是查询没有可用的.Where。另外,需要将primaryKeyProperty转换为实际属性。
其思想是,在执行此代码时,最终执行查询将生成一个sql语句,该语句选择单个项并返回它。
有人能帮忙解决这个问题吗?
更新:我正在研究这个问题的解决方案,到目前为止,这是我想出来的
//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:这是我需要的答案
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如下所示.
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属性,而不是以前。
此外,此代码将被重用到许多不同的表中。
发布于 2022-10-20 14:35:44
为了将Where添加到无法访问实际IQueryable<T>的IQueryable中,需要后退一步(或向上?)在编译时调用Where,在运行时构建Where调用,以及谓词lambda:
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);
}
}https://stackoverflow.com/questions/74137941
复制相似问题