使用Func列表<t,string =“”>委托</t,>对IQueryable执行.Select()

时间:2009-05-19 21:47:54

标签: c# .net linq linq-to-sql generics

首先,让我为标题没有意义而道歉。我很难理解我在做什么,更不用说能够使用正确的词语来描述我正在做的事情。

我正在构建一个通用网格类,我在其中通过标头/ linq表达式组合来定义列:

public class Column<T>
{
    public string Header { get; set; }
    public Func<T, string> ValueExpression { get; set; }
}

用法:

Columns = new List<Column<Employee>>
              {
                  new Column<Employee> {Header = "Employee Id", ValueExpression = e => e.EmployeeID.ToString()},
                  new Column<Employee> {Header = "Name", ValueExpression = e => e.FirstName + " " + e.LastName},
                  new Column<Employee> {Header = "Employee Birthday Year", ValueExpression = e => e.BirthDate.HasValue ? e.BirthDate.Value.Year.ToString() : ""}
              },

我想在IQueryable'员工'上投射ValueExpression(Func<T, string>):

var db = new NorthwindDataContext();
var employees = db.Employees.Select(e => e);

我可以从员工中提取IEnumerable<IEnumerable<string>>(我的观点中使用的字符串列表列表)来实现这一点:

var elementsList = employees.ToPagedList(PageIndex, PageSize);
var elementStringList = elementsList.ToStringList(Columns.Select(c => c.ValueExpression).ToArray());

不介意PagedList的东西,它与ToList()无关,有点相同;

这是ToStringList()扩展方法:

public static IEnumerable<IEnumerable<string>> ToStringList<T>(this IPagedList<T> enumerable, params Func<T, string>[] fields)
{
    foreach (var element in enumerable)
        yield return element.ToStringList(fields);
}

private static IEnumerable<string> ToStringList<T>(this T element, params Func<T, string>[] fields)
{
    foreach (var field in fields)
        yield return field(element);
}

问题是这种方法涉及在指定必须返回的字段之前执行IQueryable。

结果,以下查询在此过程中的某处执行:

SELECT [t0].[EmployeeID], [t0].[LastName], [t0].[FirstName], [t0].[Title], [t0].[TitleOfCourtesy], [t0].[BirthDate], [t0].[HireDate], [t0].[Address], [t0].[City], [t0].[Region], [t0].[PostalCode], [t0].[Country], [t0].[HomePhone], [t0].[Extension], [t0].[Photo], [t0].[Notes], [t0].[ReportsTo], [t0].[PhotoPath]
FROM [dbo].[Employees] AS [t0]

如您所知,从Employees表中检索所有字段是不受欢迎的。

我正在寻找一种方法以某种方式在IQueryable员工上构建一个扩展方法,我可以在其中传递一个Func列表(列的“ValueExpression”成员)并以这种方式“构建”一个新的IQuerable,执行只从数据库中检索所需字段的SQL。

所以我正在寻找类似的东西:

IQueryable employees = employees.SelectByExpressions(Columns.Select(c => c.ValueExpression).ToArray());

提前致谢。

4 个答案:

答案 0 :(得分:1)

您将遇到的基本问题是Select必须返回某种类型的对象,但您希望该类型具有不同的字段,具体取决于已生成的动态查询。使用匿名类型时,编译器会为您生成具有相应字段的类型。但是在你的情况下,你不知道在编译时字段列表会是什么样的,所以你不能让编译器为你生成类型。

虽然当然可以动态构建一个只包含你想要的字段的类型,但我必须停下来质疑这是否有必要。使用O / R映射器(如LINQ-to-SQL,尤其是与DataSet相反)的部分理由是,维护代码在不同时间动态返回不同字段集的成本不值得您从查询中获得的少量节省或从内存使用。事实上,在某些情况下,它甚至会使查询性能变差,因为数据库服务器无法使用不同的字段列表优化多个查询,就像它可以使用相同的字段列表优化多个查询一样。

另一种选择只是始终返回所有字段,但只显示所选字段。这肯定会更简单,更容易维护。但是,如果您已经测量了该解决方案的性能影响并确定它不符合您的要求,那么您当然可以考虑动态生成必要的类型。如果您需要有关该解决方案的帮助,我可以为您处理样本。但如果我是你,我绝对肯定在开始那个方向之前我需要走这条路。

更新:这里的另一个问题是这些查询的唯一目标是将它们显示在网格中,还是显示在其他类型的动态绑定界面中。如果是这种情况,那么可能同样容易使用一种“属性包”解决方案,其中没有涉及特定字段的具体类型,但仅仅是键/值对的容器,类似于DataRow 。但是,如果您尝试动态创建将在代码中操作的类型,我强烈建议不要使用它。技术实现可能很有趣,但维护很快就会成为一场噩梦。我之前被迫维持这样的应用程序,如果我有选择权,我将再也不会。

答案 1 :(得分:0)

我不确定我完全明白你想做什么,但它看起来很像Predicate Builder

如果没有,它可能会帮助你朝着正确的方向前进......

答案 2 :(得分:0)

有趣的问题。我认为这与这个有关:

How to create LINQ Expression Tree to select an anonymous type

您基本上需要动态选择。

答案 3 :(得分:0)

你看过Dynamic Linq吗?我相信它会做你想做的事。

如果您将列作为字符串,那么您可以使用它来执行选择谓词,并且它只会在执行的sql中包含那些列。