在方法参数中包含OrderBy委托

本文关键字:OrderBy 委托 包含 方法 参数 | 更新日期: 2023-09-27 18:08:59

我有一个方法;

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

返回一个项目列表。假设我现在想要指定返回列表的排序顺序。

我可以按Name, DueDate, Completed等排序

如何将其作为参数包含在方法中?我不想使用switch语句,如果可能的话,我想使用lambda。

;

List<Task> list = GetTasksByAssignedTo("guid", ??????);

在方法参数中包含OrderBy委托

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

LINQ使用延迟执行模型是有原因的。它允许您将一系列操作链接在一起,这些操作只有在您告诉它计算结果时才会执行-通常使用.ToList(), .ToArray(), .First() -但您也可以通过使用Func<T, ?>作为参数的OrderBy子句过滤来强制计算。

现在你返回了一个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();
}

要在数据库中执行,需要这样修改:

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
}

但是问题是如果你这样做了呢:

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

因为您的GetTasksByAssignedTo将记录带入内存,所以您正在内存中执行连接。(是的,这个查询有点做作,但原理是可靠的。)

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

修复方法:

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

现在上面的查询将不会执行,直到您执行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();

第一个查询,tasks1不算太坏,但是它没有告诉您返回类型是什么;

第二个查询,tasks2t和属性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;

我可以很容易地看出它在做什么。想象一下这个方法的助手方法名是什么!或者需要什么疯狂的helper方法嵌套。

底线是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);
    }
}

你可以这样写:

var tasks = dc.Tasks
    .WhereAssignedTo(contactId)
    .OrderByName()
    .ToList();

那就是清晰,简洁,可扩展,可组合,可重用,并且您可以控制何时执行

您可以将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;
}
现在你可以像 那样调用你的方法
Func<Task, object> someOrder = (Task t) => t.DueDate;
List<Task> list = GetTasksByAssignedTo(someGuid, someOrder);

总的来说,我同意这些评论-似乎排序不是必需的对于一个名为GetTasksByAssignedTo的方法

@BrokenGlass抢先一步。

另一个选择是使用一个隐藏开关的扩展方法,并将不同的排序选项表示为枚举。

public static IEnumerable<Task> WithOrdering(this IEnumerable<Task> source, TaskOrdering order)
{
   switch (order)
   {
      case TaskOrdering.Name:
         return source.OrderBy(task => task.Name);
      case TaskOrdering.DueDate:
         return source.OrderByDescending(task => task.DueDate);       
   }
}

然后:

public List<Task> GetTasksByAssignedTo(Guid contactId, TaskOrdering order)
{
    List<Task> tasks = dc.Tasks.Where(x => x.ContactId == contactId)
                               .WithOrdering(order)
                               .ToList();
    return tasks;
}

我一直这样做。允许谓词作为方法参数可能会很棘手,因为如果要进行升序/降序操作会发生什么?您将需要另一个参数(bool),然后执行if/else检查来执行OrderByOrderByDescending

将逻辑隐藏在过滤器中,然后你可以在应用程序的任何地方重用它。

试试这个…输入参数为字符串形式。这是根据修改后的解决方案从StackOverflow

    /// <summary>
    /// Sort List<typeparam name="T"></typeparam> objects base on string options
    /// </summary>      
    /// <param name="SortDirection">Ascending or Descending</param>   
    /// <param name="ColumnName">Column name in complex object (object.ColumnName)</param> 
 public static class ListExtension
{
    public static List<T> SortList<T>(this List<T> data, string sortDirection, string sortExpression)
    {
        try
        {
            switch (sortDirection)
            {
                case "Ascending":
                    data = (from n in data
                            orderby GetDynamicSortProperty(n, sortExpression) ascending
                            select n).ToList();
                    break;
                case "Descending":
                    data = (from n in data
                            orderby GetDynamicSortProperty(n, sortExpression) descending
                            select n).ToList();
                    break;
                default:
                    data = null; //NUL IF IS NO OPTION FOUND (Ascending or Descending)
                    break;
            }
            return data;
        } catch(Exception ex){
            throw new Exception("Unable to sort data", ex);
        }
    }
    private static object GetDynamicSortProperty(object item, string propName)
    {
        //Use reflection to get order type          
        return item.GetType().GetProperty(propName).GetValue(item, null);
    }
}