如何解决如何避免在LINQ表达式中进行动态调用
我想问你如何避免与LINQ表达式中的动态调用相关的错误。
现有错误:
不支持方法'System.Object DynamicInvoke(System.Object [])' 转换为SQL
代码示例(LINQPad):
编辑:
void Main()
{
var entity = new[]
{
new Customers { CustomerID = "ALFKI",CompanyName = "Alfreds Futterkiste",ContactName = "Maria Anders"},new Customers { CustomerID = "ANATR",CompanyName = "Ana Trujillo Emparedados y helados",ContactName = "Ana Trujillo"},new Customers { CustomerID = "2",CompanyName = "Louis Vuiton2",ContactName = "Dom2"}};
var result = Exists(entity,x => x.CustomerID + " " + x.CompanyName);
}
IDictionary<string,bool> Exists(IEnumerable<Customers> data,Func<Customers,object> predicate)
{
Expression<Func<Customers,object>> expression = x => predicate(x);
var ids = data.AsQueryable().Select(x=>predicate(x));
var existing = Customers.Where(x => ids.Contains(expression)).ToList(); //Error line.
//I do not want to materialize query after Customers for example Customers.ToList()[...]
data.ToList().ForEach(x => existing.Any(y=> predicate(y).ToString() == predicate(x).ToString()));
var dictionary = data.ToDictionary(x => x.CustomerID.ToString(),x => existing.Any(y => predicate(y).ToString() == predicate(x).ToString()));
return dictionary;
}
编辑: 现有的var应该返回:
但返回null。
解决方法
这是避免重复转换为字符串的方法。您真正想要的是获取数组并将其转换为可以应用于客户对象的表达式,然后使用Expression.OrElse
聚合这些表达式,以创建一个单个查询表达式,然后将其应用于数据库。
这并不简单,但这是如何做到的。
您将像这样在最后调用该方法:
var result = Exists(Customers.AsQueryable(),entity,(q) => c => (q.CustomerID == c.CustomerID && q.CompanyName == c.CompanyName));
相对于比较中的所有内容,这具有多个优势。首先,数据库可以使用索引优化查询。对于另一个,您可以传递更复杂的表达式,如果需要的话,可以通过简单的字符串比较来进行传递,例如c.CustomerID > q.CustomerID
。
我已将CustomerQuery
类与CustomerClass
分开,因为它们是不同的(并固定了您的多元性)。
完成工作的实际方法非常简单。之前的所有方法都用于重写具有不同参数的表达式以创建要创建的OrElse
表达式。这些方法通常在您想操纵表达式并了解基础ExpressionVisitor
类以及参数替换如何工作的任何时候都是有用的。请注意,如何使用Func
将CustomerQuery
映射到Expression
并应用于Customer
数据库。
/// <summary>
/// An ExpressionVisitor for parameter substitution
/// </summary>
internal class ExpressionParameterSubstitute : ExpressionVisitor
{
private readonly ParameterExpression from;
private readonly Expression to;
/// <summary>
/// Creates a new instance of the <see cref="ExpressionParameterSubstitute"/> visitor
/// </summary>
public ExpressionParameterSubstitute(ParameterExpression from,Expression to)
{
this.from = from;
this.to = to;
}
/// <summary>
/// Visit a Lambda Expression
/// </summary>
protected override Expression VisitLambda<T>(Expression<T> node)
{
if (node.Parameters.All(p => p != this.from))
return node;
// We need to replace the `from` parameter,but in its place we need the `to` parameter(s)
// e.g. F<DateTime,Bool> subst F<Source,DateTime> => F<Source,bool>
// e.g. F<DateTime,Bool> subst F<Source1,Source2,DateTime> => F<Source1,bool>
if (to is LambdaExpression toLambda)
{
var substituteParameters = toLambda?.Parameters ?? Enumerable.Empty<ParameterExpression>();
ReadOnlyCollection<ParameterExpression> substitutedParameters
= new ReadOnlyCollection<ParameterExpression>(node.Parameters
.SelectMany(p => p == this.from ? substituteParameters : Enumerable.Repeat(p,1))
.ToList());
var updatedBody = this.Visit(node.Body); // which will convert parameters to 'to'
return Expression.Lambda(updatedBody,substitutedParameters);
}
else
{
// to is not a lambda expression so simple substitution can work
ReadOnlyCollection<ParameterExpression> substitutedParameters
= new ReadOnlyCollection<ParameterExpression>(node.Parameters
.Where(p => p != this.from)
.ToList());
var updatedBody = this.Visit(node.Body); // which will convert parameters to 'to'
if (substitutedParameters.Any())
return Expression.Lambda(updatedBody,substitutedParameters);
else
return updatedBody;
}
}
/// <summary>
/// Visit a ParameterExpression
/// </summary>
protected override Expression VisitParameter(ParameterExpression node)
{
var toLambda = to as LambdaExpression;
if (node == from) return toLambda?.Body ?? to;
return base.VisitParameter(node);
}
}
public static Expression<Func<T,bool>> OrElse<T>(
Expression<Func<T,bool>> expr1,Expression<Func<T,bool>> expr2)
{
var parameter = Expression.Parameter(typeof (T));
var leftVisitor = new ExpressionParameterSubstitute(expr1.Parameters[0],parameter);
var left = leftVisitor.Visit(expr1.Body);
var rightVisitor = new ExpressionParameterSubstitute(expr2.Parameters[0],parameter);
var right = rightVisitor.Visit(expr2.Body);
return Expression.Lambda<Func<T,bool>>(
Expression.OrElse(left,right),parameter);
}
public static IDictionary<string,bool> Exists(IQueryable<Customer> customers,IEnumerable<CustomerQuery> data,Func<CustomerQuery,Expression<Func<Customer,bool>>> predicate)
{
Expression<Func<Customer,bool>> expression = x => false;
foreach (var item in data)
{
var exprForOne = predicate.Invoke(item);
expression = OrElse(expression,exprForOne);
}
var split = customers.GroupBy(expression).SelectMany(g => g.Select(c => new {c,g.Key})).ToDictionary(x => x.c.CustomerID,x => x.Key);
return split;
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。