1

代码 :

IList<Evento> Eventi = new List<Evento>() { };

Eventi = (from Evento ae in new Eventi()
             select ae).ToList();

if (strNome != "")
{
    Eventi = Eventi.Where(e => e.Titolo.ToLower().Contains(strNome.ToLower()) && e.Titolo != "").ToList();
}

if (strComune != "")
{
    Eventi = Eventi.Where(e => e.Comune != null && e.IDComune == strComune).ToList();
}

if (strMesi != "")
{
    Eventi = Eventi.Where(e => MesiSelezionati.Contains(DateTime.Parse(e.DataEvento).Month.ToString())).ToList();
}

我知道所有查询都在代码运行期间被合并,只有 1 个 LINQ 语句。但是,如您所见,我多次转换 List -> ToList() 。我想,这是我唯一浪费时间的部分,对吧?如何避免这种情况并提高性能?

4

4 回答 4

4

为什么有这么多列表/ToLists?IEnumerable/IQueryable 有什么问题?

var eventi = (from Evento ae in new Eventi() 
             select ae);

if (strNome != "") 
{ 
    eventi = eventi.Where(e => e.Titolo.ToLower().Contains(strNome.ToLower()) && e.Titolo != "");
} 

if (strComune != "") 
{ 
    eventi = eventi.Where(e => e.Comune != null && e.IDComune == strComune);
} 

if (strMesi != "") 
{ 
    eventi = eventi.Where(e => MesiSelezionati.Contains(DateTime.Parse(e.DataEvento).Month.ToString()));
} 

// if you do need a list, then do so right at the end
var results = eventi.ToList();

编辑:澄清Caesay的一些原则

Caesay,感谢您抽出时间来测试实现以确认延迟加载按预期工作;非常感谢!

我想解释为什么我不同意您对上述方法在运行时进行优化而您的在编译时进行优化的评论。

由于缺乏更好的描述,上述方法是预期的方法。这是因为对eventi的分配正确地将表达式附加到 IEnumerable/IQueryable 的

您的方法仅受某些提供程序支持,例如 Linq to Entities,它们希望将Func(Of T, TResult)传递给他们的SelectWhere等扩展。许多提供者,例如 Entity Framework 和 Linq to Sql 提供者,都提供了IQueryable,它实现了IEnumerable。但是,这里的区别在于IQueryableSelectWhere等希望您传递Expression(Of Func(Of T, TResult))

在这些情况下,您的代码不会像预期的那样运行(或者至少不会像我预期的那样),因为 Expression 不支持多行 lambda,因为编译器会正确解释我的语句并将它们编译成 Expression>。

举个简单的例子:

public void Test<T1, T2>(System.Linq.Expressions.Expression<Func<T1, T2>> arg)
{
    throw new NotImplementedException();
}

public void Test()
{
    Test((string x) => x.ToLower());
    Test((string x) =>
    {
        return x.ToLower();
    });
}

在上面的例子中,第一个表达式绝对没问题。第二个大致基于您的示例,将失败,但有以下例外:

A lambda expression with a statement body cannot be converted to an expression tree

编译器可能会将您的语句识别为它知道 IEnumerable 支持的Func 。结果将是对数据库的查询将不包括您的任何 Where 表达式,从而返回整个数据源。一旦数据源在内存中,它将应用您的 IEnumerable Where 子句。就个人而言,我更喜欢将这些东西传递给数据库,这样我就不会浪费带宽返回比我需要的更多的数据,并且我可以利用我的数据源功能来过滤可能的数据(并且在 Sql Server 的案例)比在内存中这样做更好。

我希望这是有道理的,对你有用吗?

于 2012-05-17T07:22:08.443 回答
0

您可以使用流利的语法开头(以避免列表)。

或者,您可以在一个查询中组合条件。

Eventi =(new Eventi()).Where(e => e.Titolo.ToLower().Contains(strNome.ToLower()) && e.Titolo != "" && e.Comune != null && e.IDComune == strComune &&MesiSelezionati.Contains(DateTime.Parse(e.DataEvento).Month.ToString())).ToList();

我假设 Eventi 实现了 IEnumerable < Evento >,就像您在查询语法中使用的类似

于 2012-05-17T07:23:15.277 回答
0

鉴于您测试 strNome 不为空,您的第一个 Where 子句的后半部分将永远不会被调用。

所以 e.Titolo.ToLower().Contains(strNome.ToLower()) && e.Titolo != "" 可以写成e.Titolo.ToLower().Contains(strNome.ToLower())

您还可以strNome.ToLower()在 lambda 之外进行计算,以确保它只计算一次。

此外,您可以按照其他人的建议简化 Where 子句并删除 ToList() 。

您应该注意的另一种选择是LinqKit,它允许您更轻松地组合 lambda 表达式,尽管在这种情况下不需要它,因为Where ... Where对于“和”来说已经足够了,但有一天你可能需要一个“或”,然后你需要一个不同的解决方案。

或者,更好的是,使用此处解释的方法创建您自己的And方法和Or执行表达式“魔术”的方法,为您提供一个表达式,您可以将其交给 Linq-to-Sql 或任何其他 Linq 提供程序。

于 2012-05-17T07:38:13.427 回答
0

将整个事情组合成 1 个 linq 查询,如下所示:

        var eventi = from Evento e in new Eventi() select e;

        eventi = eventi.Where(e =>
        {
            if (strNome != "")
            {
                if(!(e.Titolo.ToLower().Contains(strNome.ToLower()) && e.Titolo != ""))
                    return false;
            }
            if (strComune != "")
            {
                if(!(e.Comune != null && e.IDComune == strComune))
                    return false;
            }
            if (strMesi != "")
            {
                if(!(MesiSelezionati.Contains(DateTime.Parse(e.DataEvento).Month.ToString())))
                    return false;
            }

            return true;
        });

        var results = eventi.ToList();

这在逻辑上等同于您的代码,但应该更快。虽然我无法测试它,因为我无法编译它。

于 2012-05-17T08:04:22.923 回答