What is the difference between LINQ query expressi

2019-01-31 09:50发布

Below are two queries that return the same data. Other then style I am not sure which is better.

What factors influence these queries? What are the benefits of using one style over the other?

Sample 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 };

Sample 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条回答
小情绪 Triste *
2楼-- · 2019-01-31 10:13

Update: You have fixed your title, so ignore the rant.

The title of your question has nothing to do with your code samples. Your question implies that one syntax is IEnumerable and the other is IQueryable, but this is incorrect. In your samples, if db.Surveys is an IQueryable, then both your samples are using IQueryable. I will try to answer both questions.

Your two code samples are just different ways of writing the same LINQ queries (assuming they are well-written). The code in sample 1 is just shorthand for the code in sample 2. The compiler treats the code in both samples the same way. Think of the way the C# compiler will treat int? the same as Nullable<System.Int32>. Both the C# and VB.Net languages provide this shorthand query syntax. Other languages might not have this syntax and you would have to use the sample 2 syntax. In fact, other languages might not even support extension methods or lambda expressions, and you would have to use an uglier syntax yet.


Update:

To take Sander's example further, when you write this (query comprehension syntax):

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

You think the compiler turns that shorthand into this (extension methods and lambda expression):

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

But actually extension methods and lambda expressions are shorthand themselves. The compilers emits something like this (not exactly, but just to give an idea):

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

Note that Select() is just a static method in the Queryable class. If your .NET language did not support query syntax, lambdas, or extension methods, that is kinda how you would have to write the code yourself.


What are the benefits of using one style over the other?

For small queries, extension methods can be more compact:

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

Also, the extension method syntax can be more flexible, such as conditional where clauses:

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

In addition, several methods are only available through extension method syntax (Count(), Aggregate(), Take(), Skip(), ToList(), ToArray(), etc), so if I'll use one of these, I'll usually write the whole query in this syntax to avoid mixing both syntaxes.

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

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

On the other hand, when a query gets bigger and more complex, query comprehension syntax can be clearer, especially once you start complicating with a few let, group, join, etc.

In the end I will usually use whichever works better for each specific query.


Update: you fixed your title, so ignore the rest...

Now, about your title: With respect to LINQ, IEnumerable and IQueryable are very similar. They both have pretty much the same extension methods (Select, Where, Count, etc), with the main (only?) difference being that IEnumerable takes Func<TIn,TOut> as paremeters and IQueryable takes Expression<Func<TIn,TOut>> as parameters. You express both the same way (usually lamba expressions), but internally they are completely different.

IEnumerable is the doorway to LINQ to Objects. The LINQ to Objects extension methods can be called on any IEnumerable (arrays, lists, anything you can iterate with foreach) and the Func<TIn,TOut> is converted to IL at compile time and runs like a normal method code at run time. Note that some other LINQ providers use IEnumerable and so are actually using LINQ to Objects behind the scenes (LINQ to XML, LINQ to DataSet).

IQueryable is used by LINQ to SQL, LINQ to Entities, and other LINQ providers which need to examine your query and translate it instead of executing your code directly. IQueryable queries and their Expression<Func<TIn,TOut>>s are not compiled into IL at compile time. Instead an expression tree is created and can be examined at run time. This allows the statements to be translated into other query languages (for example T-SQL). An expression tree can be compiled into a Func<TIn,TOut> at run time and executed if desired.

An example that illustrates the difference can be found in this question where the OP wants to do part of a LINQ to SQL query in SQL Server, bring the objects into managed code, and do the rest of the query in LINQ to Objects. To achieve this all he has to do is cast the IQueryable into an IEnumerable where he wants the switch to happen.

查看更多
叼着烟拽天下
3楼-- · 2019-01-31 10:17

I think your question is better phrased like this, "What is the difference between IEnumerable<T> and IQueryable<T> with respect to LINQ"

LINQ queries return an IQueryable<T> by default. IQueryable<T> allows you to append other filters or "clauses" onto your query before you execute it.

Your LINQ query (first example) and your LINQ using method chaining (second example) produce the same result, with different syntax.

It is possible to write a LINQ query as a LINQ method chain and visa versa. It really depends on your preference.

@Lucas: The different is IEnumerable<T> does in-memory querying and IQueryable<T> does out-of-memory. Meaning, once you are in a foreach iterator, you are using IEnumerable, and when you are building your query, via either extension methods or using LINQ from o in object synatax, you are building an IQueryable<T>. The IQueryable<T> is executed as soon as you touch the Enumerator.

查看更多
仙女界的扛把子
4楼-- · 2019-01-31 10:18

Your Sample1 is top level representation of Linq, it is more readable, and while compiling it'll converted to expression tree i.e your 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 };

you can try below code to get expression for written query

var exp=x.Expression;

Expressions are used when query less complicated

查看更多
地球回转人心会变
5楼-- · 2019-01-31 10:18

Another point worth mentioning is that the Linq extension methods adhere to C# language whereas the query comprehension stuff is preprocessed like is built into the compiler. i.e you can navigate to the definition of .Select(x => whereas you cannot for from ... where ... select

查看更多
Explosion°爆炸
6楼-- · 2019-01-31 10:20

The where clause in the first example is actually just syntactic sugar for the Where clause in your second method. In fact, you can write your own class that has nothing to do with Linq or IQueryable and just by having a Where method, you can use that syntactic sugar. For example:

    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; }
    }

This is obviously a stupid example, but note that there's a Where method that just returns a new MyClass with stringprop set to Hello World. To demonstrate:

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

This will result in writing out "Hello World". Again, this example is obviously pointless, but it proves the point that the "where" syntax just looks for a Where method in your code that takes a Func.

查看更多
霸刀☆藐视天下
7楼-- · 2019-01-31 10:27

1./ Your question title does not match what you asked.
2./ Your question title does not really make sense. Linq stands for Language Integrated Query and is an umbrella term for a bunch of technologies and practices, IQueryable is an interface that is commonly used to facilitate Linq. you are comparing Apples and Oranges
3./ About your actual question, the main difference is style, for complex queries like this one, my personal preference is the 2nd version, as it clearly shows the progression of the result sets.

查看更多
登录 后发表回答