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();
4

8 回答 8

24

更新:你已经修正了你的标题,所以忽略咆哮。

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

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


更新:

进一步以 Sander 为例,当您编写此代码时(查询理解语法):

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

另一方面,当查询变得更大更复杂时,查询理解语法会更清晰,尤其是当您开始使用一些复杂的let, group,join等时。

最后,我通常会使用对每个特定查询效果更好的那个。


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

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

IEnumerable 是 LINQ to Objects 的入口。LINQ to Objects 扩展方法可以在任何 IEnumerable(数组、列表、任何可以迭代的对象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>>s 在编译时不会编译到 IL 中。相反,会创建一个表达式树,并且可以在运行时进行检查。这允许将语句翻译成其他查询语言(例如 T-SQL)。表达式树可以在运行时编译成 Func<TIn,TOut> 并在需要时执行。

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

于 2009-04-28T05:40:31.770 回答
4

LINQ 是一种技术的流行语。

IQueryable 是 LINQ 使用的 .NET 接口。

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

我更喜欢第一种风格的长陈述(就像这里显示的那样),第二种风格很短的陈述。

于 2009-04-28T04:41:54.733 回答
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 方法。

于 2009-04-28T04:50:00.580 回答
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 的混合。

于 2009-04-28T05:31:13.557 回答
1

1./您的问题标题与您所问的不符。
2./你的问题标题没有真正的意义。Linq 代表 Language Integrated Query,是一堆技术和实践的总称,IQueryable 是一个常用来促进 Linq 的接口。您正在比较 Apples 和 Oranges
3./关于您的实际问题,主要区别在于样式,对于像这样的复杂查询,我个人更喜欢第二个版本,因为它清楚地显示了结果集的进展。

于 2009-04-28T04:48:59.397 回答
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;

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

于 2009-04-28T06:22:24.633 回答
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> 在您触摸 Enumerator 时立即执行。

于 2009-04-28T04:43:06.010 回答
0

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

于 2015-06-23T06:07:00.850 回答