我该如何清理这个 lambda?

发布于 2024-11-10 08:59:41 字数 3861 浏览 3 评论 0原文

我有一个在多个 LINQ 查询中多次使用的表达式,因此我将其分离到它自己的返回该表达式的方法中。该函数的 lambda 部分看起来有点混乱。有人想尝试重构它并使其更具可读性和/或更小吗?

    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);
    }

对其所做操作的快速英文描述是,它检查布尔值 sent 并根据以下内容检查 Message.FromUserMessage.ToUser那个布尔值。

如果用户正在查看他/她的发件箱,则 sent 为 true,并且它将查看是否 x.FromUser.Username == usernamex.SenderDeleted ==假

如果用户正在查看他/她的收件箱,那么它会执行相同的逻辑,但发送是错误的,并且它会检查 x.ToUser 和 x.RecipientDeleted 。

也许这是最简单的方法,但我愿意进行一些重构。

答案编辑

我真的很喜欢Davy8的答案,但我决定更进一步,做两个表达式,而不是一个带有嵌套函数的表达式。现在我有以下方法:

    /// <summary>
    /// Expression to determine if a message belongs to a user.
    /// </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>> MessageBelongsToUser(string username, bool sent)
    {
        return x => (sent ? x.FromUser : x.ToUser).Username.Equals(username, StringComparison.OrdinalIgnoreCase);
    }

    /// <summary>
    /// Expression to determine if a message has been deleted by the user.
    /// </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>> UserDidNotDeleteMessage(string username, bool sent)
    {
        return x => sent ? !x.SenderDeleted : !x.RecipientDeleted;
    }

所以现在我的查询看起来像这样:

    /// <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>
    /// <param name="sent">True if retrieving sent messages.</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(MessageBelongsToUser(username, sent))
            .Where(UserDidNotDeleteMessage(username, sent))
            .OrderByDescending(x => x.SentDate)
            .Skip(itemsPerPage * (page - 1))
            .Take(itemsPerPage);
        return query;
    }

    /// <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(MessageBelongsToUser(username, sent))
            .Where(UserDidNotDeleteMessage(username, sent))
            .Count();
        return query;
    }

我想说这些是一些非常英语可读的查询,谢谢大家!

参考:http://www.codetunnel.com /blog/post/64/how-to-simplify-complex-linq-表达式

I have an expression that I use a few times in several LINQ queries so I separated it out into it's own method that returns the expression. The lambda portion of the function is kind of messy looking. Anyone want to take a crack at refactoring it and making it more readable and/or smaller?

    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);
    }

A quick English description of what it's doing is it is checking the boolean sent and checking either the Message.FromUser or the Message.ToUser based on that boolean.

If the user is looking at his/her outbox, sent is true and it will see if x.FromUser.Username == username and x.SenderDeleted == false.

If the user is looking at his/her inbox then it does the same logic, but sent is false and it checks x.ToUser and x.RecipientDeleted instead.

Maybe this is the easiest way but I'm open to some refactoring.

ANSWER EDIT

I really liked Davy8's answer but I decided to take a step further and do two expressions instead of one expression with a nested function. Now I have the following methods:

    /// <summary>
    /// Expression to determine if a message belongs to a user.
    /// </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>> MessageBelongsToUser(string username, bool sent)
    {
        return x => (sent ? x.FromUser : x.ToUser).Username.Equals(username, StringComparison.OrdinalIgnoreCase);
    }

    /// <summary>
    /// Expression to determine if a message has been deleted by the user.
    /// </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>> UserDidNotDeleteMessage(string username, bool sent)
    {
        return x => sent ? !x.SenderDeleted : !x.RecipientDeleted;
    }

So now my queries look like this:

    /// <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>
    /// <param name="sent">True if retrieving sent messages.</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(MessageBelongsToUser(username, sent))
            .Where(UserDidNotDeleteMessage(username, sent))
            .OrderByDescending(x => x.SentDate)
            .Skip(itemsPerPage * (page - 1))
            .Take(itemsPerPage);
        return query;
    }

    /// <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(MessageBelongsToUser(username, sent))
            .Where(UserDidNotDeleteMessage(username, sent))
            .Count();
        return query;
    }

I would say those are some very English readable queries, thanks guys!

Reference: http://www.codetunnel.com/blog/post/64/how-to-simplify-complex-linq-expressions

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

情深已缘浅 2024-11-17 08:59:41

将其拆分为一个单独的函数:

    private Expression<Func<Message, bool>> UserSelector(string username, bool sent)
    {
        return message=> InnerFunc(message, username, sent);
    }

    private static bool InnerFunc(Message message, string username, bool sent)
    {
        if(sent)
        {
            return string.Equals(message.FromUser.Username, username, StringComparison.InvariantCultureIgnoreCase) && !message.SenderDeleted;
        }
        return string.Equals(message.ToUser.Username, username, StringComparison.InvariantCultureIgnoreCase) && !message.RecipientDeleted;
    }

或者可以将其内联以维护闭包用法:(

    private Expression<Func<Message, bool>> UserSelector(string username, bool sent)
    {
        Func<Message, bool> innerFunc = message =>
        {
            if (sent)
            {
                return string.Equals(message.FromUser.Username, username, StringComparison.InvariantCultureIgnoreCase) &&
                        !message.SenderDeleted;
            }
            return string.Equals(message.ToUser.Username, username, StringComparison.InvariantCultureIgnoreCase) &&
                    !message.RecipientDeleted;
        };
        return message => innerFunc(message);
    }

编辑为将 string.EqualsStringComparison.InvariantCultureIgnoreCase 结合使用,用于具有不同的奇数边缘情况文化设置。)

Split it out into a separate function:

    private Expression<Func<Message, bool>> UserSelector(string username, bool sent)
    {
        return message=> InnerFunc(message, username, sent);
    }

    private static bool InnerFunc(Message message, string username, bool sent)
    {
        if(sent)
        {
            return string.Equals(message.FromUser.Username, username, StringComparison.InvariantCultureIgnoreCase) && !message.SenderDeleted;
        }
        return string.Equals(message.ToUser.Username, username, StringComparison.InvariantCultureIgnoreCase) && !message.RecipientDeleted;
    }

Or alternatively it can be inlined to maintain closure usage:

    private Expression<Func<Message, bool>> UserSelector(string username, bool sent)
    {
        Func<Message, bool> innerFunc = message =>
        {
            if (sent)
            {
                return string.Equals(message.FromUser.Username, username, StringComparison.InvariantCultureIgnoreCase) &&
                        !message.SenderDeleted;
            }
            return string.Equals(message.ToUser.Username, username, StringComparison.InvariantCultureIgnoreCase) &&
                    !message.RecipientDeleted;
        };
        return message => innerFunc(message);
    }

(Edited to use string.Equals with StringComparison.InvariantCultureIgnoreCase for odd edge cases with different culture settings.)

無處可尋 2024-11-17 08:59:41

你有Reshaper吗?很多时候我会写这样的东西作为 foreach 循环,看看 Resharper 是否可以重构它。不过,我认为可读性是最重要的,尽管您应该评估您提出的任何解决方案的性能。

Do you have Resharper? A lot of times I'll write something like this as a foreach loop and see if Resharper can refactor it. I think readability, though, is most important, although you should evaluate the peformance of any solution you come up with.

夜血缘 2024-11-17 08:59:41

只要您将为此问题编写的描述写为评论,它就应该可以帮助任何试图阅读/理解它的人,正确吗?

As long as you write the description you wrote for this question as a comment, it should help anyone trying to read / understand it correct?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文