如何将此Func转换为表达式?

我正在玩表达树,并试图更好地了解它们的工作原理。 我写了一些我正在使用的示例代码,希望有人可以帮助我。

所以我有一个有点凌乱的查询:

///  /// Retrieves the total number of messages for the user. ///  /// The name of the user. /// True if retrieving the number of messages sent. /// The total number of messages. public int GetMessageCountBy_Username(string username, bool sent) { var query = _dataContext.Messages .Where(x => (sent ? x.Sender.ToLower() : x.Recipient.ToLower()) == username.ToLower()) .Count(); return query; } 

_dataContext是entity framework数据上下文。 这个查询工作得很漂亮,但它不容易阅读。 我决定将内联IF语句分解为Func如下所示:

 public int GetMessageCountBy_Username(string username, bool sent) { Func userSelector = x => sent ? x.Sender : x.Recipient; var query = _dataContext.Messages .Where(x => userSelector(x).ToLower() == username.ToLower()) .Count(); return query; } 

这似乎很有效,但有一个问题。 因为查询是针对IQueryable这个LINQ表达式被转换为SQL以在数据源上执行。 这很好,但由于这个原因,它不知道如何处理对userSelector(x)的调用并抛出exception。 它无法将此委托转换为表达式。

所以现在我明白为什么它失败了我想尝试让它发挥作用。 对于我需要的东西来说,它的工作要多得多,但我只是出于纯粹的兴趣而这样做。 我如何将此Func转换为可以转换为SQL的表达式?

我试着这样做:

 Expression<Func> userSelectorExpression = x => sent ? x.Sender : x.Recipient; Func userSelector = userSelectorExpression.Compile(); 

然而,有了这个,我得到了同样的错误。 我想我无法理解表达方式。 我认为我正在使用上面的代码编写一个表达式,然后再将其转换为可执行代码,然后得到相同的错误。 但是,如果我尝试在LINQ查询中使用userSelectorExpression ,则无法像方法一样调用它。

我此刻相当困惑。 任何澄清将不胜感激。 谢谢!

编辑

对于那些对例外感兴趣的人,这里是:

LINQ to Entities中不支持LINQ表达式节点类型“Invoke”。

我认为这意味着它无法“调用” userSelector委托。 因为,如上所述,它需要将其转换为表达式树。

使用实际方法时,您会收到一条稍微详细的错误消息:

LINQ to Entities无法识别方法’System.String userSelector(Message,Boolean)’方法,并且此方法无法转换为存储表达式。

无需复杂化:

 return sent ? _dataContext.Messages.Count(x => x.Sender.ToLower() == username.ToLower()) : _dataContext.Messages.Count(x => x.Recipient.ToLower() == username.ToLower()); 

也许这可以用来帮助你抽象出条件(谓词): http : //www.albahari.com/nutshell/predicatebuilder.aspx

好好玩了一下后,我得到了我想要的东西。

在这种情况下,这并没有为我节省大量代码,但它确实使基本查询更容易查看。 对于将来更复杂的查询,这将是非常棒的! 这个查询逻辑永远不会重复,但仍然可以根据需要重复使用多次。

首先,我的存储库中有两个方法。 一个计算消息总数(我在问题中用作示例的消息)和一个实际按页码获取消息集合的消息总数。 以下是它们的结构:

得到消息总数的那个:

  ///  /// Retrieves the total number of messages for the user. ///  /// The name of the user. /// True if retrieving the number of messages sent. /// The total number of messages. public int GetMessageCountBy_Username(string username, bool sent) { var query = _dataContext.Messages .Count(UserSelector(username, sent)); return query; } 

获取消息并将其分页的那个:

  ///  /// Retrieves a list of messages from the data context for a user. ///  /// The name of the user. /// The page number. /// The number of items to display per page. /// An enumerable list of messages. public IEnumerable GetMessagesBy_Username(string username, int page, int itemsPerPage, bool sent) { var query = _dataContext.Messages .Where(UserSelector(username, sent)) .OrderByDescending(x => x.SentDate) .Skip(itemsPerPage * (page - 1)) .Take(itemsPerPage); return query; } 

显然,对UserSelector(string, bool)的调用在这里是最重要的。 这是该方法的样子:

  ///  /// Builds an expression to be reused in a LINQ query. ///  /// The name of the user. /// True if retrieving sent messages. /// An expression to be used in a LINQ query. private Expression> UserSelector(string username, bool sent) { return x => ((sent ? x.FromUser : x.ToUser).Username.ToLower() == username.ToLower()) && (sent ? !x.SenderDeleted : !x.RecipientDeleted); } 

因此,此方法构建一个要计算的表达式,并将其正确地转换为它的SQL等价物。 如果用户名与发件人或收件人的用户名匹配,则表达式中的函数的计算结果为true,对于发件人或收件人,删除的内容为false,具体取决于所提供的序列化为表达式的布尔值。

这是上面的一个版本,更接近我的问题中的示例。 它不具有可读性,因为我的表达是怪诞的,但至少我理解它现在是如何工作的:

  public int GetMessageCountBy_Username(string username, bool sent) { Expression> userSelector = x => ((sent ? x.FromUser : x.ToUser).Username.ToLower() == username.ToLower()) && (sent ? !x.SenderDeleted : !x.RecipientDeleted); var query = _dataContext.Messages .Count(userSelector); return query; } 

这实际上非常酷。 花了很多时间弄明白,但这似乎真的很强大。 我现在对LINQ,lambdas和表达式如何工作有了新的认识:)

感谢所有为此问题做出贡献的人! (包括你的artplastika,即使我不喜欢你的答案,我仍然爱你)