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

Ton*_*ell 14 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 };
Run Code Online (Sandbox Code Playgroud)

样本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();
Run Code Online (Sandbox Code Playgroud)

Luc*_*cas 23

更新:你已修复你的标题,所以忽略咆哮.

您的问题标题与您的代码示例无关.你的问题暗示一个语法是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
Run Code Online (Sandbox Code Playgroud)

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

IQueryable<string> surveryNames = db.Surveys.Select(s => s.Name);
Run Code Online (Sandbox Code Playgroud)

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

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

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


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

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

var items = source.Where(s => s > 5);
Run Code Online (Sandbox Code Playgroud)

此外,扩展方法语法可以更灵活,例如条件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);
Run Code Online (Sandbox Code Playgroud)

另外,有几种方法只能通过扩展方法语法(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();
Run Code Online (Sandbox Code Playgroud)

在另一方面,当查询变得更大,更复杂,查询综合语法可以更清晰,特别是一旦你开始有一些复杂let,group,join,等.

最后,我通常会使用哪个更适合每个特定查询.


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

现在,关于你的标题:关于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).

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

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