如何在自定义方法中实现Linq扩展?

时间:2013-06-24 23:39:17

标签: c# .net performance linq c#-4.0

我有一种方法,我使用Linq对其进行过滤并将其转换为List,但是对于请求的调用,它会提前转换为列表。请参阅此处的自定义方法示例:

public IEnumerable<ItemModel> GetAll()
{
    var output = new List<ItemModel>();

    this.DataLayer.GetItems() //returns IQueryable<SomeWeirdItemModel>
        .Where(i => i.IsActive == true) 
        .ToList()
        .ForEach(i => output.Add(new ItemModel(i)));

    return output;
}

问题是,当我执行this.GetAll().Where(i => i.StartDate >= DateTime.Now)时,我的自定义方法将其转换为从数据库中检索一切的List,然后我的请求按日期过滤。如何获取被调用的Linq并将其实现到我的自定义方法中?

这样的东西? this.DataLayer.GetNewsItems().Where(i => i.IsActive == true && (REQUESTED FILTER HERE?))

1 个答案:

答案 0 :(得分:2)

这里有几个问题。

首先,ToList()将返回与数据库中的Where子句匹配的所有项。你不能做ToList()并且不能立即执行查询。

其次,你要归还IEnumerable<ItemModel>。 IEnumerables也会立即执行查询。如果要向返回的类型添加其他参数,则必须返回IQueryable<ItemModel>

第三,使用Constructor参数进行此操作可能很困难,因为这需要在代码中进行处理,而不是在数据库中进行处理。 @dasblinkenlight的解决方案可能有效,但我无法测试。我猜他知道他在说什么。

您需要将其代码更改为:

public IQueryable<ItemModel> GetAll()
{
    return this.DataLayer.GetNewsItems()
       .Where(i => i.IsActive) 
       .Select((v,i) => new ItemModel(i));
}

修改

如果您坚持使用构造函数参数作为投影类型,那么您将不得不在某处妥协,例如将过滤器传递给您的方法。

这样的事情:

public IEnumerable<ItemModel> GetItemsByDate(DateTime date)
{
    return this.DataLayer.GetNewsItems()
       .Where(i => i.IsActive && i.Date == date)
       .AsEnumerable()
       .Select(x => new ItemModel(x));
}

当您调用该方法时,它仍然会执行查询(您不能对输出应用任何其他过滤器并让它在数据库中执行),但它只会返回与IsActive和Date过滤器匹配的对象。

你也可以应用这样的任意表达式:

public IEnumerable<ItemModel> GetItemsByDate(Expression<Func<SomeWeirdItemModel,bool>> filter)
{
    return this.DataLayer.GetNewsItems()
       .Where(i => i.IsActive && filter)
       .AsEnumerable()
       .Select(x => new ItemModel(x));
}

然后你可以这样做:

var items = DataLayer.GetAll(x => x.Date == date);
var others = DataLayer.GetAll(x => x.Date == date && x.Title.Length > 5 && x.Test = "X");
// etc..

不幸的是,您必须在参数中公开SomeWeirdModel,因为表达式必须对其进行过滤,否则您将不得不做很多工作来尝试翻译过滤器。