在方法参数中包含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", ??????);
我认为你的方法是错误的方式使用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
不算太坏,但是它没有告诉您返回类型是什么;
第二个查询,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;
我可以很容易地看出它在做什么。想象一下这个方法的助手方法名是什么!或者需要什么疯狂的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检查来执行OrderBy
或OrderByDescending
。
将逻辑隐藏在过滤器中,然后你可以在应用程序的任何地方重用它。
试试这个…输入参数为字符串形式。这是根据修改后的解决方案从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);
}
}