LINQ查询表达式和扩展方法之间有什么区别

时间:2009-04-28 04:37:08

标签: c# linq query-expressions

以下是两个返回相同数据的查询。除了风格,我不确定哪个更好。

哪些因素会影响这些查询? 使用一种风格而不是另一种风格有什么好处?

样本1

var x = from s in db.Surveys
    join sq in db.Survey_Questions on s.ID equals sq.Survey_ID
    join q in db.Questions on sq.Question_ID equals q.ID
    join qg in db.Question_Groups on q.ID equals qg.Question_ID
    where s.Type_ID.Equals(typeID) & s.Type.Equals(type)
    select new { question = sq.Question, status = sq.Status, grp = qg };

样本2

var x = db.Surveys.Where(s => s.Type_ID.Equals(typeID) & s.Type.Equals(type))
              .Join(db.Survey_Questions,
                        s => s.ID,
                        sq => sq.Survey_ID,
                        (s, sq) => new
                        {
                            question = sq.Question,
                            status = sq.Status
                        })
              .Join(db.Question_Groups,
                        q => q.question.ID,
                        qg => qg.Question_ID,
                        (q, qg) => new
                        {
                            question = q.question,
                            status = q.status,
                            group = qg
                        }).ToList();

8 个答案:

答案 0 :(得分:21)

更新:您已修复了标题,因此请忽略咆哮。

您的问题标题与您的代码示例无关。你的问题暗示一个语法是IEnumerable而另一个是IQueryable,但这是不正确的。在您的示例中,如果db.Surveys是IQueryable,那么两者您的样本都使用IQueryable。我将尝试回答两个问题。

您的两个代码示例只是编写相同LINQ查询的不同方式(假设它们写得很好)。示例1中的代码只是示例2中代码的简写。编译器以相同的方式处理两个示例中的代码。想想C#编译器将int?Nullable<System.Int32>相同的方式。 C#和VB.Net语言都提供了这种简写查询语法。其他语言可能没有此语法,您必须使用示例2语法。事实上,其他语言甚至可能不支持扩展方法或lambda表达式,你还必须使用更丑陋的语法。


更新

当你写这个(查询理解语法)时,进一步采用桑德的例子:

var surveyNames = from s in db.Surveys select s.Name

认为编译器将这种简写转换为此(扩展方法和lambda表达式):

IQueryable<string> surveryNames = db.Surveys.Select(s => s.Name);

但实际上扩展方法和lambda表达式本身就是简写。编译器发出类似这样的东西(不完全是,但只是为了给出一个想法):

Expression<Func<Survey, string>> selector = delegate(Survey s) { return s.Name; };
IQueryable<string> surveryNames = Queryable.Select(db.Surveys, selector);

请注意,Select()只是Queryable类中的静态方法。如果您的.NET语言不支持查询语法,lambdas或扩展方法,那么您必须自己编写代码。


  

使用一种风格比另一种风格有什么好处?

对于小型查询,扩展方法可以更紧凑:

var items = source.Where(s => s > 5);

此外,扩展方法语法可以更灵活,例如条件where子句:

var items = source.Where(s => s > 5);

if(smallerThanThen)
    items = items.Where(s => s < 10);
if(even)
    items = items.Where(s => (s % 2) == 0);

return items.OrderBy(s => s);

此外,有几种方法只能通过扩展方法语法(Count(),Aggregate(),Take(),Skip(),ToList(),ToArray()等)来实现,所以如果我使用一种方法其中,我通常会用这种语法编写整个查询,以避免混合使用这两种语法。

var floridaCount = source.Count(s => s.State == "FL");

var items = source
            .Where(s => s > 5)
            .Skip(5)
            .Take(3)
            .ToList();

另一方面,当查询变得更大且更复杂时,查询理解语法可以更清晰,尤其是当您开始使用少量letgroupjoin复杂化时,等

最后,我通常会针对每个特定查询使用更好的方法。


更新:你修改了你的标题,所以忽略其余的......

现在,关于你的标题:关于LINQ,IEnumerable和IQueryable非常相似。它们都具有几乎相同的扩展方法(Select,Where,Count等),主要(仅?)区别在于IEnumerable将Func<TIn,TOut>作为参数,IQueryable将Expression<Func<TIn,TOut>>作为参数。你用同样的方式表达(通常是lamba表达式),但在内部却完全不同。

IEnumerable是LINQ to Objects的通道。可以在任何IEnumerable(数组,列表,您可以使用foreach迭代的任何内容)上调用LINQ to Objects扩展方法,并且Func<TIn,TOut>在编译时转换为IL并且像普通方法代码一样运行运行。请注意,其他一些LINQ提供程序使用IEnumerable,因此实际上在幕后使用LINQ to Objects(LINQ to XML,LINQ to DataSet)。

IQueryable由LINQ to SQL,LINQ to Entities和其他LINQ提供程序使用,这些提供程序需要检查您的查询并转换它而不是直接执行代码。 IQueryable查询及其Expression<Func<TIn,TOut>>在编译时不会编译到IL中。而是创建表达式树,并且可以在运行时进行检查。这允许将语句翻译成其他查询语言(例如T-SQL)。表达式树可以编译成Func&lt; TIn,TOut&gt;在运行时并在需要时执行。

可以在this question中找到示例差异的示例,其中OP希望在SQL Server中执行LINQ to SQL查询的一部分,将对象引入托管代码,并执行其余查询LINQ to Objects。要实现这一点,他所要做的就是将IQueryable转换为IEnumerable,他希望切换发生。

答案 1 :(得分:3)

LINQ是一项技术的流行词。

IQueryable是LINQ使用的.NET接口。

除了风格,两者之间没有区别。使用您喜欢的任何一种风格。

我更喜欢长语句的第一种风格(如此处所示)和第二种非常简短的语句。

答案 2 :(得分:2)

第一个示例中的where子句实际上只是第二个方法中Where子句的语法糖。事实上,您可以编写自己的类,与Linq或IQueryable无关,只需使用Where方法,就可以使用该语法糖。例如:

    public class MyClass
    {

        public MyClass Where<T>(Func<MyClass, T> predicate)
        {
            return new MyClass { StringProp = "Hello World" };
        }

        public MyClass Select<T>(Func<MyClass, T> predicate)
        {
            return new MyClass ();
        }



        public string StringProp { get; set; }
    }

这显然是一个愚蠢的例子,但请注意,有一个Where方法只返回一个新的MyClass,其中stringprop设置为Hello World。为了证明:

MyClass a = new MyClass();
            var q = from p in a
                    where p.StringProp == "foo" // doesnt matter what we put here, as we're not really checking the predicate
                    select p;
            Console.WriteLine(q.StringProp);

这将导致写出“Hello World”。同样,这个例子显然毫无意义,但它证明了“where”语法只是在代码中查找带有Func的Where方法。

答案 3 :(得分:2)

查询表达式和扩展方法是两种完全相同的方法。在编译时,查询表达式会转换为扩展方法 - 它们只是对SQL更熟悉的人的语法糖。

当你这样写:

var surveyNames = from s in db.Surveys select s.Name;

编译器将其转换为:

IQueryable<string> surveryNames = db.Surveys.Select(s => s.Name);

实际上,我认为查询表达式只是出于营销原因而创建的 - 一种类似SQL的语言结构,在LINQ开发时充当引人注目的东西,而不是提供很多实际用途的东西。我发现大多数人只是直接使用扩展方法,因为它们会产生更统一的编码风格,而不是混合使用C#和SQL。

答案 4 :(得分:1)

1. /您的问题标题与您提出的问题不符 2. /你的问题标题没有意义。 Linq代表语言集成查询,是一系列技术和实践的总称,IQueryable是一个常用于促进Linq的接口。你正在比较苹果和橘子
3. /关于你的实际问题,主要区别在于风格,对于像这样的复杂查询,我的个人偏好是第二版,因为它清楚地显示了结果集的进展。

答案 5 :(得分:1)

Sample1 是Linq的顶级代表,它更具可读性,在编译时它会转换为表达式树,即 Sample2

var x = from s in db.Surveys
    join sq in db.Survey_Questions on s.ID equals sq.Survey_ID
    join q in db.Questions on sq.Question_ID equals q.ID
    join qg in db.Question_Groups on q.ID equals qg.Question_ID
    where s.Type_ID.Equals(typeID) & s.Type.Equals(type)
    select new { question = sq.Question, status = sq.Status, grp = qg };

您可以尝试下面的代码来获取书面查询的表达式

var exp=x.Expression;

查询不太复杂时使用表达式

答案 6 :(得分:0)

我认为你的问题更像是这样说的,“关于LINQ,IEnumerable&lt; T&gt;和IQueryable&lt; T&gt;”之间的区别是什么?

LINQ查询返回IQueryable&lt; T&gt;默认情况下。 IQueryable的&LT; T&GT;允许您在执行查询之前将其他过滤器或“子句”附加到查询中。

您的LINQ查询(第一个示例)和LINQ using method chaining(第二个示例)使用不同的语法生成相同的结果。

可以将LINQ查询编写为LINQ方法链,反之亦然。这实际上取决于你的偏好。

@Lucas:不同的是IEnumerable&lt; T&gt;内存中查询和IQueryable&lt; T&gt;没有内存。这意味着,一旦你在foreach迭代器中,你正在使用IEnumerable,当你通过扩展方法或使用LINQ from o in object synatax构建查询时,你正在构建一个IQueryable&lt; T&gt;。 IQueryable&lt; T&gt;触摸枚举器后立即执行。

答案 7 :(得分:0)

值得一提的另一点是Linq扩展方法遵循C#语言,而查询理解的东西是预编译的,就像内置在编译器中一样。 即你可以导航到.Select的定义(x =&gt; 而你不能from ... where ... select