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

14

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

影响这些查询的因素有哪些? 使用其中一种风格的好处是什么?

示例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();

值得一提的是,你的样本并不相同... - Sam Saffron
此外,末尾的".ToList()" 强制执行了不同的操作。如果想要它们等价,应该去掉它。第一个x的类型是IQueryable<T>,而第二个是IList<T>。 - Orion Adrian
8个回答

24
更新:您已经修复了标题,因此请忽略抱怨。
您的问题标题与您的代码示例无关。您的问题暗示一个语法是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语言不支持查询语法、lambda或扩展方法,那么您就必须自己编写代码。

使用一种风格而不是另一种风格有什么好处?

对于小查询,扩展方法可以更加简洁:

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>>作为参数。您通常以相同的方式表示两者(通常是lambda表达式),但在内部它们完全不同。

IEnumerable是连接到LINQ to Objects的门户。可以在任何IEnumerable上调用LINQ to Objects扩展方法(数组,列表,您可以使用foreach迭代的任何内容),并且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<TIn,TOut>并执行(如果需要)。

一个例子可以阐明它们之间的区别,可以在这个问题中找到,其中OP想要在SQL Server中执行LINQ to SQL查询的一部分,将对象带入托管代码,并在LINQ to Objects中执行其余查询。为了实现这一点,他只需将IQueryable强制转换为IEnumerable,以在所需位置进行切换。


4

LINQ是一种技术的流行词。

IQueryable是被LINQ使用的.NET接口。

除了风格之外,这两者之间没有区别。使用你偏好的任何一种风格。

我喜欢在长语句(如此处所示)中使用第一种风格,在非常短的语句中使用第二种风格。


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方法。

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。


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;

当查询较简单时,可以使用表达式。


1

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


0
另一个值得一提的点是,Linq扩展方法遵循C#语言规范,而查询理解部分则像内置于编译器中的预处理。 也就是说,您可以导航到.Select(x =>的定义, 但您无法为from ... where ... select导航到定义。

0

我认为你的问题最好这样表述,“IEnumerable<T>和IQueryable<T>在LINQ方面有什么区别?”

LINQ查询默认返回一个IQueryable<T>。IQueryable<T>允许您在执行查询之前附加其他过滤器或“子句”。

您的LINQ查询(第一个示例)和使用方法链接的LINQ(第二个示例)产生相同的结果,具有不同的语法。

可以将LINQ查询编写为LINQ方法链,反之亦然。这真的取决于您的喜好。

@Lucas:不同之处在于IEnumerable<T>进行内存查询,而IQueryable<T>进行外存查询。也就是说,一旦进入foreach迭代器,您就使用IEnumerable,而当您通过扩展方法或使用LINQ from o in object语法构建查询时,您正在构建一个IQueryable<T>。只要触摸枚举器,就会执行IQueryable<T>。


LINQ查询默认返回IQueryable<T>。这是针对LINQ to SQL或LINQ to Entities的语句。而LINQ to Objects则使用IEnumerable。 - Lucas
"IQueryable<T> 允许您在执行查询之前附加其他过滤器或“子句”。IEnumerable 也是如此。" - Lucas
哦,问题的标题已经修正 :) - Lucas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接