有没有办法改善这个LINQ?

时间:2012-05-17 07:16:47

标签: linq list

代码:

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 - &gt; ToList()很多次。我认为,这是我浪费时间的唯一部分,对吧?如何避免这种情况并提高性能?

4 个答案:

答案 0 :(得分: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,感谢您抽出时间测试实施情况,以确认延期装载是否符合预期;非常荣幸!

我想解释一下为什么我不同意您在运行时在运行时进行优化的上述方法的评论

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

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

在这些情况下,您的代码将无法按预期行事(或至少如我所料),因为Expression不支持多行lambda,因为编译器将正确解释我的语句并将其编译为Expression&gt;。 / p>

举个简单的例子:

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中) case)比在内存中这样做更好。

我希望这对你有用并且对你有用吗?

答案 1 :(得分: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&lt; Evento&gt;正如您在查询语法中使用的类似

答案 2 :(得分:0)

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

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

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

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

你应该知道的另一个替代方法是LinqKit,它允许你更容易地组合lambda表达式,虽然在这种情况下不需要,因为Where ... Where对于'And'来说已经足够了,你可能会有一个一天需要一个'或'然后你需要一个不同的解决方案。

或者,更好的是,使用解释here的方法创建自己的AndOr方法,执行表达式'魔术',为您提供一个可以传递的单个表达式到Linq-to-Sql或任何其他Linq提供商。

答案 3 :(得分: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();

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