如何将此Func转换为表达式
本文关键字:转换 表达式 Func | 更新日期: 2023-09-27 17:57:41
我在玩表达式树,并试图更好地理解它们是如何工作的。我写了一些正在使用的示例代码,希望有人能帮我。
所以我有一个有点混乱的问题:
/// <summary>
/// Retrieves the total number of messages for the user.
/// </summary>
/// <param name="username">The name of the user.</param>
/// <param name="sent">True if retrieving the number of messages sent.</param>
/// <returns>The total number of messages.</returns>
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
是实体框架数据上下文。这个查询运行得很好,但读起来并不容易。我决定将内联IF语句分解为Func
,如下所示:
public int GetMessageCountBy_Username(string username, bool sent)
{
Func<Message, string> userSelector = x => sent ? x.Sender : x.Recipient;
var query = _dataContext.Messages
.Where(x => userSelector(x).ToLower() == username.ToLower())
.Count();
return query;
}
这看起来会很好,但有一个问题。因为查询是针对IQueryable<T>
的,所以该LINQ表达式被转换为SQL,以便在数据源处执行。这很好,但正因为如此,它不知道该如何处理对userSelector(x)
的调用,并抛出异常。它无法将此委托转换为表达式。
所以现在我明白了为什么它会失败,我想尝试让它发挥作用。这是我需要的更多的工作,但我这么做纯粹是出于兴趣。如何将此Func
转换为可以转换为SQL的表达式?
我试着这样做:
Expression<Func<Message, string>> userSelectorExpression = x => sent ? x.Sender : x.Recipient;
Func<Message, string> userSelector = userSelectorExpression.Compile();
然而,有了这个,我得到了同样的错误。我想我不懂表达。我想我对上面的代码所做的就是写一个表达式,然后再次将其转换为可执行代码,然后得到相同的错误。但是,如果我尝试在LINQ查询中使用userSelectorExpression
,它就不能像方法一样被调用。
编辑
对于那些对例外感兴趣的人来说,这里是:
LINQ to Entities中不支持LINQ表达式节点类型"Invoke"。
我认为这意味着它不能";调用";CCD_ 7代表。因为,如上所述,它需要将其转换为表达式树。
当使用实际方法时,您会得到一条稍微详细一些的错误消息:
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());
玩了一会儿之后,我得到了我想要的。
在这种情况下,这并没有为我节省大量的代码,但它确实使基本查询更容易查看。对于未来更复杂的查询,这将非常棒!这个查询逻辑永远不会重复,但仍然可以根据需要重复使用多次
首先,我的存储库中有两种方法。其中一个统计消息的总数(我在问题中使用的例子),另一个实际按页码获取消息集合。以下是它们的结构:
获得消息总数的一个:
/// <summary>
/// Retrieves the total number of messages for the user.
/// </summary>
/// <param name="username">The name of the user.</param>
/// <param name="sent">True if retrieving the number of messages sent.</param>
/// <returns>The total number of messages.</returns>
public int GetMessageCountBy_Username(string username, bool sent)
{
var query = _dataContext.Messages
.Count(UserSelector(username, sent));
return query;
}
获取消息并对其进行分页的程序:
/// <summary>
/// Retrieves a list of messages from the data context for a user.
/// </summary>
/// <param name="username">The name of the user.</param>
/// <param name="page">The page number.</param>
/// <param name="itemsPerPage">The number of items to display per page.</param>
/// <returns>An enumerable list of messages.</returns>
public IEnumerable<Message> 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)
才是最重要的。下面是这种方法的样子:
/// <summary>
/// Builds an expression to be reused in a LINQ query.
/// </summary>
/// <param name="username">The name of the user.</param>
/// <param name="sent">True if retrieving sent messages.</param>
/// <returns>An expression to be used in a LINQ query.</returns>
private Expression<Func<Message, bool>> UserSelector(string username, bool sent)
{
return x => ((sent ? x.FromUser : x.ToUser).Username.ToLower() == username.ToLower()) && (sent ? !x.SenderDeleted : !x.RecipientDeleted);
}
因此,该方法构建了一个要求值的表达式,并将其正确地转换为等效的SQL。如果用户名与发件人或收件人的用户名匹配,并且根据提供的序列化到表达式中的布尔值sent
,删除的发件人或收件人为false,则表达式中的函数计算结果为true。
这是上面的一个版本,更接近我问题中的例子。由于我的表情怪异,它不太可读,但至少我理解它现在的工作原理:
public int GetMessageCountBy_Username(string username, bool sent)
{
Expression<Func<Message, bool>> 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,即使我不喜欢你的答案,我仍然爱你)
也许这可以帮助您抽象条件(谓词):http://www.albahari.com/nutshell/predicatebuilder.aspx