我有一个包含成员的小组课程,这些成员可以属于不同的班级类型。
与此相关的所有其他功能都有效,我只缺少数据库调用,对此我已经没有想法了。
我的代码如下:
response = group.MapEntity<GroupDataResponse>(); //Irrelevant for this question (kept due to comment about it).
var resourceType = group.GetResourceType();
var functionType = typeof(Func<,>).MakeGenericType(typeof(IGroupMember), typeof(bool));
var expressionType = typeof(Expression<>).MakeGenericType(functionType);
var contextSetMethod = _context.GetType().GetMethod("Set", genericParameterCount: 1, Array.Empty<Type>())!.MakeGenericMethod(resourceType);
var dbSetMethod = typeof(Enumerable).GetMethods().FirstOrDefault(x => x.Name == "SingleOrDefault")!.MakeGenericMethod(expressionType);
var dynamicContext = contextSetMethod.Invoke(_context, null);
foreach (var member in group.Members)
{
var func = BuildExpression(member.MemberId);
var result = dbSetMethod.Invoke(dynamicContext, new object?[] { func });
if (result is null || result.GetType() != resourceType)
throw new Exception($"Something went wrong!!!!! {result}");
members.Add((IGroupMember)result);
}
response.Members = members;
异常发生在
dbSetMethod.Invoke(...)
例外:
System.ArgumentException:“System.Linq.Expressions.Expression1”1[System.Func'2[Trasolu.Domain.Common.IGroupMember,System.Boolean]]”类型的对象无法转换为“System.Collections.Generic”类型.IEnumerable'1[System.Linq.Expressions.Expression'1[System.Func`2[Trasolu.Domain.Common.IGroupMember,System.Boolean]]]'。
我想要的是对数据库进行动态调用,循环中的
func
是一个 lambda 表达式,我想在 dbSetMethod
上运行以确保我只获取具有给定 id 的实体.
这是我尝试运行的函数。
private Func<IGroupMember, bool> BuildExpression(int id)
{
Expression<Func<IGroupMember, int>> queryFunction = x => x.Id;
var paramExpr = Expression.Parameter(typeof(IGroupMember));
var varExpr = Expression.Constant(id, typeof(int));
var equalExpr = Expression.Equal(
left: Expression.Invoke(queryFunction, paramExpr),
right: varExpr);
var lambda = Expression.Lambda<Func<IGroupMember, bool>>(equalExpr, paramExpr);
return lambda.Compile();
}
EF Core 不支持解析任意编译树。它只能解析正常的表达式树。
您需要首先以正常方式创建表达式。最简单的方法是使用泛型。
private Expression<Func<T, bool>> BuildExpression<T>(int id) where T : IGroupMember
{
Expression<Func<IGroupMember, bool>> queryFunction = x => x.Id == id;
return queryFunction;
}
然后你可以使用另一个函数来调用它。如果您愿意,您也可以将这两个功能组合成一个函数。
private static object GetMember<T>(DbContext dbContext, int id) where T : IGroupMember
{
return dbContext.Set<T>().SingleOrDefault(BuildExpression(id));
}
最后动态调用该函数。您现在只需要构造一个动态调用,而不是整个链。
var functionType = typeof(Func<,,>).MakeGenericMethod(typeof(DbContext), typeof(int), resourceType);
var func = this.GetType()
.GetMethod(nameof(GetMember), BindingFlags.Static | BindingFlags.NonPublic)
.MakeGenericMethod(resourceType)
.CreateDelegate<Func<DbContext, int, object>>(this);
foreach (var member in group.Members)
{
var result = (IGroupMember)func.Invoke(_context, member.MemberId);
members.Add((IGroupMember)result);
}
response.Members = members;