在Method参数中包含OrderBy委托

gri*_*egs 8 c# lambda

我有一个方法;

    public List<Task> GetTasksByAssignedTo(Guid contactId)
    {
        List<Task> tasks = dc.Tasks.Where(x => x.ContactId == contactId).ToList();
        return tasks;
    }
Run Code Online (Sandbox Code Playgroud)

它返回一个项目列表.说我现在想要指定我想要返回列表的排序顺序.

所以我可能按名称,截止日期,完成等等排序等.

我怎么能在方法中包含它作为参数?我不想使用switch语句,而是如果可能的话我想使用lambda.

所以;

List<Task> list = GetTasksByAssignedTo("guid", ??????);
Run Code Online (Sandbox Code Playgroud)

或者这是错误的方法.

Eni*_*ity 11

我认为你的方法是使用LINQ的错误方法.

LINQ使用延迟执行模型是有原因的.它可以让你链接在一起得到执行,只有当你告诉它来计算结果的一系列操作-往往.ToList(),.ToArray(),.First()-但你也可以通过与过滤迫使计算OrderBy使用的条款Func<T, ?>作为它的参数.

现在你要返回一个List<Task>意味着你已经强制执行 - 当你准备好使用结果时这是正确的事情 - 但如果你继续做进一步的操作你可能会加载更多的记录进入记忆而不是你需要.

你当然可以这样做:

public List<Task> GetTasksByAssignedTo<P>(Guid contactId, Func<Task, P> orderBy)
{
    return dc.Tasks
        .Where(x => x.ContactId == contactId)
        .OrderBy(orderBy) // this forces evaluation - sort happens in memory
        .ToList();
}
Run Code Online (Sandbox Code Playgroud)

要在数据库中执行,您需要更改它:

public List<Task> GetTasksByAssignedTo<P>(
    Guid contactId,
    Expression<Func<Task, P>> orderBy)
{
    return dc.Tasks
        .Where(x => x.ContactId == contactId)
        .OrderBy(orderBy)
        .ToList(); // Now execution happens here
}
Run Code Online (Sandbox Code Playgroud)

但问题是,如果你这样做:

var query =
    from t1 in GetTasksByAssignedTo(contactId, t => t.Name)
    join t2 in GetTasksByAssignedTo(contactId, t => t.Name)
        on t1.Name equals t2.Name
    select new { t1, t2 };
Run Code Online (Sandbox Code Playgroud)

因为您GetTasksByAssignedTo将记录带入内存,所以您正在进行内存中的连接.(是的,查询有点人为,但原则很扎实.)

在数据库中执行它通常要好得多.

以下是修复方法:

public IQueryable<Task> GetTasksByAssignedTo<P>(
    Guid contactId,
    Expression<Func<Task, P>> orderBy)
{
    return dc.Tasks
        .Where(x => x.ContactId == contactId)
        .OrderBy(orderBy);
}
Run Code Online (Sandbox Code Playgroud)

现在上述查询将在您执行之前不会执行,query.ToList()并且所有操作都将在数据库中执行.

但我有一个更大的问题.

你隐藏了很多信息GetTasksByAssignedTo.使用代码的人不知道他们在阅读代码时实际上是在获取列表,而他们实际上并不知道实际的实现是否做得对.我认为,对于这些类型的查询,通常最好将其保留为简单的LINQ.

比较这些:

var tasks1 = GetTasksByAssignedTo(contactId);
var tasks2 = GetTasksByAssignedTo(contactId, t => t.Name);
var tasks3 = GetTasksByAssignedToDescending(contactId, t => t.Name);

var tasks4 = (
        from t in dc.Tasks
        where t.ContactId == contactId
        orderby t.Name descending
        select t
    ).ToList();
Run Code Online (Sandbox Code Playgroud)

第一个查询,tasks1不是太糟糕,但它不会告诉你返回类型是什么;

第二个查询,tasks2对某些t和属性执行某些操作Name,但不会告诉您什么.

第三个查询,tasks3给你一个提示它正在排序降序,但不告诉你它是否是由神秘Name属性或其他东西.

第四个查询,tasks4告诉你需要知道的一切 - 它是过滤任务ContactId,反向排序结果Name,最后返回一个列表.

现在,看一下这个查询:

var query2 =
    from t1 in dc.Tasks
    where t1.ContactId == contactId
    join t2 in dc.Tasks on t1.Name equals t2.Name
    where t2.ContactId != contactId
    orderby t2.Name descending
    select t2;
Run Code Online (Sandbox Code Playgroud)

我可以很容易地阅读它,看看它在做什么.试想一下这个帮助方法的名称是什么!或者需要什么样的辅助方法的疯狂嵌套.

底线是LINQ是用于查询的API.

如果您迫切想要创建辅助方法,那么使用扩展方法.

public static class TaskEx
{
    public static IQueryable<Task> WhereAssignedTo(this IQueryable<Task> tasks,
        Guid contactId)
    {
        return tasks.Where(t => t.ContactId == contactId);
    }

    public static IQueryable<Task> OrderByName(this IQueryable<Task> tasks)
    {
        return tasks.OrderBy(t => t.Name);
    }
}
Run Code Online (Sandbox Code Playgroud)

这允许你写这个:

var tasks = dc.Tasks
    .WhereAssignedTo(contactId)
    .OrderByName()
    .ToList();
Run Code Online (Sandbox Code Playgroud)

这是清晰,简洁,可扩展,可组合,可重用,并且您可以在执行时进行控制.


Bro*_*ass 6

您可以将a传递Func<Task, object>给您的方法进行排序:

public List<Task> GetTasksByAssignedTo(Guid contactId, Func<Task, object> someOrder)
{
    List<Task> tasks = dc.Tasks.Where(x => x.ContactId == contactId)
                               .OrderBy(someOrder)
                               .ToList();
    return tasks;
}
Run Code Online (Sandbox Code Playgroud)

现在你可以调用你的方法了

Func<Task, object> someOrder = (Task t) => t.DueDate;
List<Task> list = GetTasksByAssignedTo(someGuid, someOrder);
Run Code Online (Sandbox Code Playgroud)

一般来说,我同意这些意见 - 似乎并不是命名方法需要排序GetTasksByAssignedTo.

  • 你不必使用对象.您可以将其更改为**public List <Task> GetTasksByAssignedTo <T>(Guid contactId,Func <Task,T> someOrder)** (3认同)