LinqKit 无法将类型 'System.Linq.Expressions.InstanceMethodCallExpression2' 的对象转换为类型 'System.Linq.Expressions.LambdaExpression'
LinqKit Unable to cast object of type 'System.Linq.Expressions.InstanceMethodCallExpression2' to type 'System.Linq.Expressions.LambdaExpression'
我对 Expressions
有疑问。我有这个 lambda LINQ 查询 Entity Framework Core LINQ Provider:
IQueryable<ProcessValueBase> valuesSubquery;
switch (req.Period)
{
case TimePeriodType.Current:
valuesSubquery = dbContext.ProcessValues;
break;
case TimePeriodType.Day:
case TimePeriodType.Week:
case TimePeriodType.Month:
valuesSubquery = dbContext.NormalizedLogValues;
break;
default:
valuesSubquery = dbContext.ProcessValues;
break;
}
var res = dbContext.Rooms.Select(r => new
{
RoomId = r.Id,
ZoneId = r.ZoneId,
IdealSetpoint = r.Group.Setpoints.First(sp => sp.ClimaticZoneId == dbContext.ClimaticZonesLogs.OrderByDescending(cz => cz.Timestamp).First().ClimaticZoneId).Setpoint,
Devices = r.Devices
.Select(rd => rd.Device)
.Select(d => new
{
Id = d.Id,
Name = d.Name,
//Setpoint = GetQuery(rd.Device.Id).Average(t=>t.Value)
Setpoint = valuesSubquery.Where(GetQuery(req.Period, d)).Average(t => t.Value)
})
}
).ToList();
然后我有一个动态处理谓词的函数:
private Expression<Func<ProcessValueBase,bool>> GetQuery(string period, DeviceModel device)
{
var predicate = PredicateBuilder.New<ProcessValueBase>();
predicate = predicate.And(v => v.TagSettings.DeviceId == device.Id);
predicate = predicate.And(v => v.TagSettings.TagTypeId == GetSetpointTagTypeId(device.DeviceTypeId));
predicate = predicate.And(v => v.ClimaticZoneId == dbContext.ClimaticZonesLogs.OrderByDescending(cz => cz.Timestamp).First().ClimaticZoneId);
var utcTime = DateTime.Now.ToUniversalTime();
switch (period)
{
case TimePeriodType.Day:
var startOfDay = utcTime.StartOfDay();
predicate = predicate.And(v => v.Timestamp >= startOfDay && v.Timestamp < startOfDay.AddDays(1));
break;
case TimePeriodType.Week:
var startOfWeek = utcTime.FirstDayOfWeek();
predicate = predicate.And(v => v.Timestamp >= startOfWeek && v.Timestamp < startOfWeek.AddDays(7));
break;
case TimePeriodType.Month:
var startOfMonth = utcTime.FirstDayOfMonth();
predicate = predicate.And(v => v.Timestamp >= startOfMonth && v.Timestamp < startOfMonth.AddMonths(1));
break;
default:
break;
}
return predicate;
}
我得到的错误如下:
消息是:
Unable to cast object of type 'System.Linq.Expressions.InstanceMethodCallExpression2' to type 'System.Linq.Expressions.LambdaExpression'.
谁能指出我做错了什么?
提前致谢,
朱利安
编辑:
作为对 Svyatoslav Danyliv 回答的回答。它给我的例外是:
The LINQ expression 'DbSet<NormalizedLogValueModel>()
.Where(n => (v, device) => v.TagSettings.DeviceId == device.Id && v.TagSettings.TagTypeId == SetpointSideViewHandler.GetSetpointTagTypeId(device.DeviceTypeId) && v.ClimaticZoneId == DbSet<ClimaticZoneLogModel>()
.OrderByDescending(c0 => c0.Timestamp)
.Select(c0 => c0.ClimaticZoneId)
.First() && v.Timestamp >= __startOfDay_0 && v.Timestamp < __AddDays_1
.Invoke(
arg1: n,
arg2: EntityShaperExpression:
EntityType: DeviceModel
ValueBufferExpression:
ProjectionBindingExpression: Inner
IsNullable: True
))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
当GetQuery(req.Period).Compile()(p,d)
:
The LINQ expression 'DbSet<NormalizedLogValueModel>()
.Where(n => Invoke(__Compile_0, n, [RelationalEntityShaperExpression])
)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
好吧,答案很复杂,但根据我的经验,这只是一种方法:
用法:
var res = dbContext.Rooms
.Select(r => new
{
RoomId = r.Id,
ZoneId = r.ZoneId,
IdealSetpoint = r.Group.Setpoints.First(sp => sp.ClimaticZoneId == dbContext.ClimaticZonesLogs.OrderByDescending(cz => cz.Timestamp).First().ClimaticZoneId).Setpoint,
Devices = r.Devices
.Select(rd => rd.Device)
.Select(d => new
{
Id = d.Id,
Name = d.Name,
Setpoint = valuesSubquery.Where(p => GetQuery(req.Period).Invoke(dbContext, p, d)).Average(t => t.Value)
})
}
).ToList();
新功能(可能是编译错误,未测试):
private static Expression<Func<DBIContext, ProcessValueBase, DeviceModel, bool>> GetQuery(string period)
{
Expression<Func<DBIContext, ProcessValueBase, DeviceModel, bool>> predicateTemplate = (dbContext, v, device) =>
v.TagSettings.DeviceId == device.Id &&
v.TagSettings.TagTypeId == GetSetpointTagTypeId(device.DeviceTypeId) &&
v.ClimaticZoneId == dbContext.ClimaticZonesLogs.OrderByDescending(cz => cz.Timestamp).First().ClimaticZoneId;
var predicate = PredicateBuilder.New<ProcessValueBase>();
var utcTime = DateTime.Now.ToUniversalTime();
switch (period)
{
case TimePeriodType.Day:
var startOfDay = utcTime.StartOfDay();
predicate = predicate.And(v => v.Timestamp >= startOfDay && v.Timestamp < startOfDay.AddDays(1));
break;
case TimePeriodType.Week:
var startOfWeek = utcTime.FirstDayOfWeek();
predicate = predicate.And(v => v.Timestamp >= startOfWeek && v.Timestamp < startOfWeek.AddDays(7));
break;
case TimePeriodType.Month:
var startOfMonth = utcTime.FirstDayOfMonth();
predicate = predicate.And(v => v.Timestamp >= startOfMonth && v.Timestamp < startOfMonth.AddMonths(1));
break;
default:
break;
}
Expression<Func<ProcessValueBase, bool>> lambda = predicate;
var newBody = predicateTemplate.Body;
newBody = Expression.AndAlso(newBody, ExpressionReplacer.GetBody(lambda, predicateTemplate.Parameters[1]));
return Expression.Lambda<Func<ProcessValueBase, DeviceModel, bool>>(newBody, predicateTemplate.Parameters);
}
[Expandable(nameof(GetSetpointTagTypeIdImpl))]
private static long GetSetpointTagTypeId(long deviceTypeId)
{
throw new NotImplementedException();
}
private static Expression<Func<long, long>> GetSetpointTagTypeIdImpl()
{
return deviceTypeId => deviceTypeId == 180_000 ? 180_002
: deviceTypeId == 190_000 ? 190_002 : 0;
}
和帮手class:
public class ExpressionReplacer : ExpressionVisitor
{
readonly IDictionary<Expression, Expression> _replaceMap;
public ExpressionReplacer(IDictionary<Expression, Expression> replaceMap)
{
_replaceMap = replaceMap ?? throw new ArgumentNullException(nameof(replaceMap));
}
public override Expression Visit(Expression exp)
{
if (exp != null && _replaceMap.TryGetValue(exp, out var replacement))
return replacement;
return base.Visit(exp);
}
public static Expression Replace(Expression expr, Expression toReplace, Expression toExpr)
{
return new ExpressionReplacer(new Dictionary<Expression, Expression> {{toReplace, toExpr}}).Visit(expr);
}
public static Expression Replace(Expression expr, IDictionary<Expression, Expression> replaceMap)
{
return new ExpressionReplacer(replaceMap).Visit(expr);
}
public static Expression GetBody(LambdaExpression lambda, params Expression[] toReplace)
{
if (lambda.Parameters.Count != toReplace.Length)
throw new InvalidOperationException();
return new ExpressionReplacer(Enumerable.Range(0, lambda.Parameters.Count)
.ToDictionary(i => (Expression) lambda.Parameters[i], i => toReplace[i])).Visit(lambda.Body);
}
}
我对 Expressions
有疑问。我有这个 lambda LINQ 查询 Entity Framework Core LINQ Provider:
IQueryable<ProcessValueBase> valuesSubquery;
switch (req.Period)
{
case TimePeriodType.Current:
valuesSubquery = dbContext.ProcessValues;
break;
case TimePeriodType.Day:
case TimePeriodType.Week:
case TimePeriodType.Month:
valuesSubquery = dbContext.NormalizedLogValues;
break;
default:
valuesSubquery = dbContext.ProcessValues;
break;
}
var res = dbContext.Rooms.Select(r => new
{
RoomId = r.Id,
ZoneId = r.ZoneId,
IdealSetpoint = r.Group.Setpoints.First(sp => sp.ClimaticZoneId == dbContext.ClimaticZonesLogs.OrderByDescending(cz => cz.Timestamp).First().ClimaticZoneId).Setpoint,
Devices = r.Devices
.Select(rd => rd.Device)
.Select(d => new
{
Id = d.Id,
Name = d.Name,
//Setpoint = GetQuery(rd.Device.Id).Average(t=>t.Value)
Setpoint = valuesSubquery.Where(GetQuery(req.Period, d)).Average(t => t.Value)
})
}
).ToList();
然后我有一个动态处理谓词的函数:
private Expression<Func<ProcessValueBase,bool>> GetQuery(string period, DeviceModel device)
{
var predicate = PredicateBuilder.New<ProcessValueBase>();
predicate = predicate.And(v => v.TagSettings.DeviceId == device.Id);
predicate = predicate.And(v => v.TagSettings.TagTypeId == GetSetpointTagTypeId(device.DeviceTypeId));
predicate = predicate.And(v => v.ClimaticZoneId == dbContext.ClimaticZonesLogs.OrderByDescending(cz => cz.Timestamp).First().ClimaticZoneId);
var utcTime = DateTime.Now.ToUniversalTime();
switch (period)
{
case TimePeriodType.Day:
var startOfDay = utcTime.StartOfDay();
predicate = predicate.And(v => v.Timestamp >= startOfDay && v.Timestamp < startOfDay.AddDays(1));
break;
case TimePeriodType.Week:
var startOfWeek = utcTime.FirstDayOfWeek();
predicate = predicate.And(v => v.Timestamp >= startOfWeek && v.Timestamp < startOfWeek.AddDays(7));
break;
case TimePeriodType.Month:
var startOfMonth = utcTime.FirstDayOfMonth();
predicate = predicate.And(v => v.Timestamp >= startOfMonth && v.Timestamp < startOfMonth.AddMonths(1));
break;
default:
break;
}
return predicate;
}
我得到的错误如下:
消息是:
Unable to cast object of type 'System.Linq.Expressions.InstanceMethodCallExpression2' to type 'System.Linq.Expressions.LambdaExpression'.
谁能指出我做错了什么?
提前致谢,
朱利安
编辑: 作为对 Svyatoslav Danyliv 回答的回答。它给我的例外是:
The LINQ expression 'DbSet<NormalizedLogValueModel>()
.Where(n => (v, device) => v.TagSettings.DeviceId == device.Id && v.TagSettings.TagTypeId == SetpointSideViewHandler.GetSetpointTagTypeId(device.DeviceTypeId) && v.ClimaticZoneId == DbSet<ClimaticZoneLogModel>()
.OrderByDescending(c0 => c0.Timestamp)
.Select(c0 => c0.ClimaticZoneId)
.First() && v.Timestamp >= __startOfDay_0 && v.Timestamp < __AddDays_1
.Invoke(
arg1: n,
arg2: EntityShaperExpression:
EntityType: DeviceModel
ValueBufferExpression:
ProjectionBindingExpression: Inner
IsNullable: True
))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
当GetQuery(req.Period).Compile()(p,d)
:
The LINQ expression 'DbSet<NormalizedLogValueModel>()
.Where(n => Invoke(__Compile_0, n, [RelationalEntityShaperExpression])
)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
好吧,答案很复杂,但根据我的经验,这只是一种方法:
用法:
var res = dbContext.Rooms
.Select(r => new
{
RoomId = r.Id,
ZoneId = r.ZoneId,
IdealSetpoint = r.Group.Setpoints.First(sp => sp.ClimaticZoneId == dbContext.ClimaticZonesLogs.OrderByDescending(cz => cz.Timestamp).First().ClimaticZoneId).Setpoint,
Devices = r.Devices
.Select(rd => rd.Device)
.Select(d => new
{
Id = d.Id,
Name = d.Name,
Setpoint = valuesSubquery.Where(p => GetQuery(req.Period).Invoke(dbContext, p, d)).Average(t => t.Value)
})
}
).ToList();
新功能(可能是编译错误,未测试):
private static Expression<Func<DBIContext, ProcessValueBase, DeviceModel, bool>> GetQuery(string period)
{
Expression<Func<DBIContext, ProcessValueBase, DeviceModel, bool>> predicateTemplate = (dbContext, v, device) =>
v.TagSettings.DeviceId == device.Id &&
v.TagSettings.TagTypeId == GetSetpointTagTypeId(device.DeviceTypeId) &&
v.ClimaticZoneId == dbContext.ClimaticZonesLogs.OrderByDescending(cz => cz.Timestamp).First().ClimaticZoneId;
var predicate = PredicateBuilder.New<ProcessValueBase>();
var utcTime = DateTime.Now.ToUniversalTime();
switch (period)
{
case TimePeriodType.Day:
var startOfDay = utcTime.StartOfDay();
predicate = predicate.And(v => v.Timestamp >= startOfDay && v.Timestamp < startOfDay.AddDays(1));
break;
case TimePeriodType.Week:
var startOfWeek = utcTime.FirstDayOfWeek();
predicate = predicate.And(v => v.Timestamp >= startOfWeek && v.Timestamp < startOfWeek.AddDays(7));
break;
case TimePeriodType.Month:
var startOfMonth = utcTime.FirstDayOfMonth();
predicate = predicate.And(v => v.Timestamp >= startOfMonth && v.Timestamp < startOfMonth.AddMonths(1));
break;
default:
break;
}
Expression<Func<ProcessValueBase, bool>> lambda = predicate;
var newBody = predicateTemplate.Body;
newBody = Expression.AndAlso(newBody, ExpressionReplacer.GetBody(lambda, predicateTemplate.Parameters[1]));
return Expression.Lambda<Func<ProcessValueBase, DeviceModel, bool>>(newBody, predicateTemplate.Parameters);
}
[Expandable(nameof(GetSetpointTagTypeIdImpl))]
private static long GetSetpointTagTypeId(long deviceTypeId)
{
throw new NotImplementedException();
}
private static Expression<Func<long, long>> GetSetpointTagTypeIdImpl()
{
return deviceTypeId => deviceTypeId == 180_000 ? 180_002
: deviceTypeId == 190_000 ? 190_002 : 0;
}
和帮手class:
public class ExpressionReplacer : ExpressionVisitor
{
readonly IDictionary<Expression, Expression> _replaceMap;
public ExpressionReplacer(IDictionary<Expression, Expression> replaceMap)
{
_replaceMap = replaceMap ?? throw new ArgumentNullException(nameof(replaceMap));
}
public override Expression Visit(Expression exp)
{
if (exp != null && _replaceMap.TryGetValue(exp, out var replacement))
return replacement;
return base.Visit(exp);
}
public static Expression Replace(Expression expr, Expression toReplace, Expression toExpr)
{
return new ExpressionReplacer(new Dictionary<Expression, Expression> {{toReplace, toExpr}}).Visit(expr);
}
public static Expression Replace(Expression expr, IDictionary<Expression, Expression> replaceMap)
{
return new ExpressionReplacer(replaceMap).Visit(expr);
}
public static Expression GetBody(LambdaExpression lambda, params Expression[] toReplace)
{
if (lambda.Parameters.Count != toReplace.Length)
throw new InvalidOperationException();
return new ExpressionReplacer(Enumerable.Range(0, lambda.Parameters.Count)
.ToDictionary(i => (Expression) lambda.Parameters[i], i => toReplace[i])).Visit(lambda.Body);
}
}