【发布时间】:2017-03-01 09:17:16
【问题描述】:
我在 IQueryable lambda linq 中使用字典抛出了
Unable to create a constant value of type 'System.Collections.Generic.KeyValuePair`2
代码:
Dictionary<int, int> keyValues = new Dictionary<int, int>();
IQueryable<Account> = context.Account
.Where(W => keyValues
.Where(W1 => W1.Key == S.AccountID)
.Where(W1 => W1.Value == S.Balance)
.Count() > 0);
详情:
我有这样的字典里面的数据
AccountID 余额
11000
2 2000
3 3000
我希望用户具有 (ID = 1 AND Balance = 1000) OR (ID = 2 AND BALANCE = 2000) OR (ID = 3 AND BALANCE = 3000)
那么我该如何为它编写 lambda 表达式呢?
已编辑
谢谢@caesay,你的回答对我帮助很大。
我希望你再帮我一个忙。
根据您的回答,我创建了如下所示的表达式:
private static Expression<Func<Accounting, bool>> GenerateExpression(Dictionary<int, int> lstAccountsBalance)
{
try
{
var objAccounting = Expression.Parameter(typeof(Accounting));
Expression expr = null;
const bool NOT_ALLOWED = false;
if (lstAccountsBalance != null && lstAccountsBalance.Count > 0)
{
var clauses = new List<Expression>();
foreach (var kvp in lstAccountsBalance)
{
clauses.Add(Expression.AndAlso(
Expression.Equal(Expression.Constant(kvp.Key), Expression.Property(objAccounting, nameof(Accounting.ID))),
Expression.Equal(Expression.Constant(kvp.Value), Expression.Property(objAccounting, nameof(Accounting.Balance)))
));
}
expr = clauses.First();
foreach (var e in clauses.Skip(1))
{
expr = Expression.OrElse(e, expr);
}
var notAllowedExpr = Expression.AndAlso(
Expression.Equal(Expression.Constant(NOT_ALLOWED), Expression.Property(objAccounting, nameof(Accounting.ALLOWED))),
Expression.Equal(Expression.Constant(true), Expression.Constant(true))
);
expr = Expression.And(notAllowedExpr, expr);
}
var allowedExpr = Expression.AndAlso(
Expression.Equal(Expression.Constant(!NOT_ALLOWED), Expression.Property(objAccounting, nameof(Accounting.ALLOWED))),
Expression.Equal(Expression.Property(objAccounting, nameof(Accounting.ID)), Expression.Property(objAccounting, nameof(Accounting.ID)))
);
if (expr != null)
{
expr = Expression.OrElse(allowedExpr, expr);
}
else
{
expr = allowedExpr;
}
return Expression.Lambda<Func<Accounting, bool>>(expr, objAccounting);
}
catch (Exception ex)
{
throw objEx;
}
}
之后我编译了这样的表达式:
Expression<Func<Accounting, bool>> ExpressionFunctions = GenerateExpression(lstAccountsBalance);
var compiledExpression = ExpressionFunctions.Compile();
我是这样使用的:
.Select(S => new
{
Accounting = S.Accounts
.Join(context.AccountInfo,
objAccounts => objAccounts.ID,
objAccountInfo => objAccountInfo.ID,
(objAccounts, objAccountInfo) => new Accounting
{
ID = objAccounts.ID,
Balance = objAccountInfo.Balance,
})
.Where(W => W.ID == user.ID)
.AsQueryable()
.Where(W => compiledExpression(W))
.Select(S1 => new Accounting()
{
ID = S1.ID,
Balance = S1.Balance
})
.ToList(),
}
它会抛出异常并显示消息:
System.NotSupportedException:LINQ 表达式节点类型“Invoke” 在 LINQ to Entities 中不受支持。
不编译
没有编译它就像魅力一样。它提供了我想要的输出。
Expression<Func<Accounting, bool>> ExpressionFunctions = GenerateExpression(lstAccountsBalance);
用途:
.Select(S => new
{
Accounting = S.Accounts
.Join(context.AccountInfo,
objAccounts => objAccounts.ID,
objAccountInfo => objAccountInfo.ID,
(objAccounts, objAccountInfo) => new Accounting
{
ID = objAccounts.ID,
Balance = objAccountInfo.Balance,
})
.Where(W => W.ID == user.ID)
.AsQueryable()
.Where(ExpressionFunctions)
.Select(S1 => new Accounting()
{
ID = S1.ID,
Balance = S1.Balance
})
谢谢你..
【问题讨论】:
-
那么最后一个样本有什么问题?它不正是你想要的吗?最后一个例子有什么问题?
-
我遇到了性能问题。在我的数据库中,我有 5,00,000 条记录,从表中取出 1,000 条记录需要 10 到 12 秒。我想提高查询的性能 我想将时间减少到 3 到 4 秒。
-
这个表达式被编译成 sql,并在你的数据库服务器上执行。在这里调用
Compile正在将表达式树转换为C# 代码——它不能再编译为SQL,即使它不会加快任何速度。这里的瓶颈是你的数据库服务器,相比之下,从这个 linq 表达式生成 sql 已经非常快了。 -
@caesay 谢谢你变化很大。我真的很感激你的工作。而 Skip and Take 到最后一条记录,例如从 4,99,000 获取数据并占用 1,000,而不是 49 秒。它确实变化缓慢。有什么建议可以提高这种性能吗?谢谢:)
-
所以第一步是find out what SQL that EF is generating,然后在 SQL Management Studio 中运行它并分析它以查看查询的哪一部分花费的时间最长。也许您在数据库中缺少一个可以加快速度的索引。试试这个,如果你没有任何运气,请打开一个与性能相关的新问题。
标签: c# entity-framework linq lambda entity-framework-6