your programing

IEnumerable 반환

lovepro 2020. 9. 27. 13:34
반응형

IEnumerable 반환 대 IQueryable [닫은]


반환 IQueryable<T>반환의 차이점은 무엇입니까 IEnumerable<T>?

IQueryable<Customer> custs = from c in db.Customers
where c.City == "<City>"
select c;

IEnumerable<Customer> custs = from c in db.Customers
where c.City == "<City>"
select c;

둘 다 연기 된 실행이며 언제 하나가 다른 것보다 선호되어야합니까?


예, 둘 다 연기 된 실행 을 제공합니다 .

차이점은 IQueryable<T>LINQ-to-SQL (LINQ.-to-anything)이 작동 할 수 있도록하는 인터페이스입니다. 따라서에서 쿼리를 추가로 구체화 IQueryable<T>하면 가능한 경우 해당 쿼리가 데이터베이스에서 실행됩니다.

IEnumerable<T>경우 LINQ-to-object가됩니다. 즉, 원래 쿼리와 일치하는 모든 개체를 데이터베이스에서 메모리로로드해야합니다.

코드에서 :

IQueryable<Customer> custs = ...;
// Later on...
var goldCustomers = custs.Where(c => c.IsGold);

이 코드는 SQL을 실행하여 골드 고객 만 선택합니다. 반면에 다음 코드는 데이터베이스에서 원래 쿼리를 실행 한 다음 메모리에서 골드가 아닌 고객을 필터링합니다.

IEnumerable<Customer> custs = ...;
// Later on...
var goldCustomers = custs.Where(c => c.IsGold);

이것은 매우 중요한 차이점이며 IQueryable<T>많은 경우 작업 을 수행하면 데이터베이스에서 너무 많은 행을 반환하지 않아도됩니다. 또 다른 좋은 예는 페이징을 수행하는 것입니다. Takeand Skipon 을 사용 IQueryable하면 요청 된 행 수만 가져옵니다. 에서 그렇게 IEnumerable<T>하면 모든 행이 메모리에로드됩니다.


가장 좋은 대답은 좋지만 두 인터페이스가 "어떻게"다른지 설명하는 표현식 트리는 언급하지 않았습니다. 기본적으로 두 개의 동일한 LINQ 확장 집합이 있습니다. Where(), Sum(), Count(), FirstOrDefault()기능을 받아들이와 표현을 허용 하나, 등 모두 두 가지 버전이있다.

  • IEnumerable버전의 서명은 다음과 같습니다Where(Func<Customer, bool> predicate)

  • IQueryable버전의 서명은 다음과 같습니다Where(Expression<Func<Customer, bool>> predicate)

둘 다 동일한 구문을 사용하여 호출되기 때문에 두 가지 모두 인식하지 못한 채 두 가지를 모두 사용했을 것입니다.

예를 들어,이 Where(x => x.City == "<City>")모두에서 작동 IEnumerable하고IQueryable

  • 사용시 Where()IEnumerable컬렉션 컴파일러로 컴파일 된 함수를 전달Where()

  • 사용하는 경우 Where()IQueryable모음, 컴파일러에 식 트리를 전달합니다 Where(). 표현식 트리는 반사 시스템과 비슷하지만 코드 용입니다. 컴파일러는 코드를 쉽게 소화 할 수있는 형식으로 코드가 수행하는 작업을 설명하는 데이터 구조로 변환합니다.

이 표현 트리 일을 왜 귀찮게합니까? Where()데이터를 필터링 하고 싶습니다 . 주된 이유는 EF와 Linq2SQL ORM이 모두 표현식 트리를 SQL로 직접 변환하여 코드가 훨씬 빠르게 실행될 수 있기 때문입니다.

오, 무료 성능 향상처럼 들립니다 AsQueryable().이 경우 모든 곳에서 사용해야합니까? 아니요, IQueryable기본 데이터 공급자가 작업을 수행 할 수있는 경우에만 유용합니다. 정기적처럼 뭔가를 변환 List하는 것은 IQueryable당신에게 어떤 혜택을 제공하지 않습니다.


예, 둘 다 지연된 실행을 사용합니다. SQL Server 프로파일 러를 사용하여 차이점을 설명하겠습니다 ....

다음 코드를 실행할 때 :

MarketDevEntities db = new MarketDevEntities();

IEnumerable<WebLog> first = db.WebLogs;
var second = first.Where(c => c.DurationSeconds > 10);
var third = second.Where(c => c.WebLogID > 100);
var result = third.Where(c => c.EmailAddress.Length > 11);

Console.Write(result.First().UserName);

SQL Server 프로파일 러에서 다음과 같은 명령을 찾습니다.

"SELECT * FROM [dbo].[WebLog]"

1 백만 개의 레코드가있는 WebLog 테이블에 대해 해당 코드 블록을 실행하는 데 약 90 초가 걸립니다.

따라서 모든 테이블 레코드는 객체로 메모리에로드되고 각 .Where ()와 함께 이러한 객체에 대한 메모리의 또 다른 필터가됩니다.

위의 예 (두 번째 줄)에서 IQueryable대신 사용할 때 IEnumerable:

SQL Server 프로파일 러에서 다음과 같은 명령을 찾습니다.

"SELECT TOP 1 * FROM [dbo].[WebLog] WHERE [DurationSeconds] > 10 AND [WebLogID] > 100 AND LEN([EmailAddress]) > 11"

를 사용하여이 코드 블록을 실행하는 데 약 4 초가 걸립니다 IQueryable.

IQueryable에는 예제에서를 Expression사용할 때 생성되기 시작하는 트리 식 result(지연된 실행 이라고 함) 을 저장 하는 속성 이 있으며, 마지막에는이식이 데이터베이스 엔진에서 실행되도록 SQL 쿼리로 변환됩니다.


둘 다 연기 된 실행을 제공합니다.

어느 것이 다른 것보다 선호되는지는 기본 데이터 소스가 무엇인지에 따라 다릅니다.

를 반환하면 IEnumerable런타임에서 자동으로 LINQ to Objects를 사용하여 컬렉션을 쿼리합니다.

Returning an IQueryable (which implements IEnumerable, by the way) provides the extra functionality to translate your query into something that might perform better on the underlying source (LINQ to SQL, LINQ to XML, etc.).


In general terms I would recommend the following:

  • Return IQueryable<T> if you want to enable the developer using your method to refine the query you return before executing.

  • Return IEnumerable if you want to transport a set of Objects to enumerate over.

Imagine an IQueryable as that what it is - a "query" for data (which you can refine if you want to). An IEnumerable is a set of objects (which has already been received or was created) over which you can enumerate.


A lot has been said previously, but back to the roots, in a more technical way:

  1. IEnumerable is a collection of objects in memory that you can enumerate - an in-memory sequence that makes it possible to iterate through (makes it way easy for within foreach loop, though you can go with IEnumerator only). They reside in the memory as is.
  2. IQueryable is an expression tree that will get translated into something else at some point with ability to enumerate over the final outcome. I guess this is what confuses most people.

They obviously have different connotations.

IQueryable represents an expression tree (a query, simply) that will be translated to something else by the underlying query provider as soon as release APIs are called, like LINQ aggregate functions (Sum, Count, etc.) or ToList[Array, Dictionary,...]. And IQueryable objects also implement IEnumerable, IEnumerable<T> so that if they represent a query the result of that query could be iterated. It means IQueryable don't have to be queries only. The right term is they are expression trees.

Now how those expressions are executed and what they turn to is all up to so called query providers (expression executors we can think them of).

In the Entity Framework world (which is that mystical underlying data source provider, or the query provider) IQueryable expressions are translated into native T-SQL queries. Nhibernate does similar things with them. You can write your own one following the concepts pretty well described in LINQ: Building an IQueryable Provider link, for example, and you might want to have a custom querying API for your product store provider service.

So basically, IQueryable objects are getting constructed all the way long until we explicitly release them and tell the system to rewrite them into SQL or whatever and send down the execution chain for onward processing.

As if to deferred execution it's a LINQ feature to hold up the expression tree scheme in the memory and send it into the execution only on demand, whenever certain APIs are called against the sequence (the same Count, ToList, etc.).

The proper usage of both heavily depends on the tasks you're facing for the specific case. For the well-known repository pattern I personally opt for returning IList, that is IEnumerable over Lists (indexers and the like). So it is my advice to use IQueryable only within repositories and IEnumerable anywhere else in the code. Not saying about the testability concerns that IQueryable breaks down and ruins the separation of concerns principle. If you return an expression from within repositories consumers may play with the persistence layer as they would wish.

A little addition to the mess :) (from a discussion in the comments)) None of them are objects in memory since they're not real types per se, they're markers of a type - if you want to go that deep. But it makes sense (and that's why even MSDN put it this way) to think of IEnumerables as in-memory collections whereas IQueryables as expression trees. The point is that the IQueryable interface inherits the IEnumerable interface so that if it represents a query, the results of that query can be enumerated. Enumeration causes the expression tree associated with an IQueryable object to be executed. So, in fact, you can't really call any IEnumerable member without having the object in the memory. It will get in there if you do, anyways, if it's not empty. IQueryables are just queries, not the data.


In general you want to preserve the original static type of the query until it matters.

For this reason, you can define your variable as 'var' instead of either IQueryable<> or IEnumerable<> and you will know that you are not changing the type.

If you start out with an IQueryable<>, you typically want to keep it as an IQueryable<> until there is some compelling reason to change it. The reason for this is that you want to give the query processor as much information as possible. For example, if you're only going to use 10 results (you've called Take(10)) then you want SQL Server to know about that so that it can optimize its query plans and send you only the data you'll use.

A compelling reason to change the type from IQueryable<> to IEnumerable<> might be that you are calling some extension function that the implementation of IQueryable<> in your particular object either cannot handle or handles inefficiently. In that case, you might wish to convert the type to IEnumerable<> (by assigning to a variable of type IEnumerable<> or by using the AsEnumerable extension method for example) so that the extension functions you call end up being the ones in the Enumerable class instead of the Queryable class.


There is a blog post with brief source code sample about how misuse of IEnumerable<T> can dramatically impact LINQ query performance: Entity Framework: IQueryable vs. IEnumerable.

If we dig deeper and look into the sources, we can see that there are obviously different extension methods are perfomed for IEnumerable<T>:

// Type: System.Linq.Enumerable
// Assembly: System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Core.dll
public static class Enumerable
{
    public static IEnumerable<TSource> Where<TSource>(
        this IEnumerable<TSource> source, 
        Func<TSource, bool> predicate)
    {
        return (IEnumerable<TSource>) 
            new Enumerable.WhereEnumerableIterator<TSource>(source, predicate);
    }
}

and IQueryable<T>:

// Type: System.Linq.Queryable
// Assembly: System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Core.dll
public static class Queryable
{
    public static IQueryable<TSource> Where<TSource>(
        this IQueryable<TSource> source, 
        Expression<Func<TSource, bool>> predicate)
    {
        return source.Provider.CreateQuery<TSource>(
            Expression.Call(
                null, 
                ((MethodInfo) MethodBase.GetCurrentMethod()).MakeGenericMethod(
                    new Type[] { typeof(TSource) }), 
                    new Expression[] 
                        { source.Expression, Expression.Quote(predicate) }));
    }
}

The first one returns enumerable iterator, and the second one creates query through the query provider, specified in IQueryable source.


I recently ran into an issue with IEnumerable v. IQueryable. The algorithm being used first performed an IQueryable query to obtain a set of results. These were then passed to a foreach loop, with the items instantiated as an Entity Framework (EF) class. This EF class was then used in the from clause of a Linq to Entity query, causing the result to be IEnumerable.

I'm fairly new to EF and Linq for Entities, so it took a while to figure out what the bottleneck was. Using MiniProfiling, I found the query and then converted all of the individual operations to a single IQueryable Linq for Entities query. The IEnumerable took 15 seconds and the IQueryable took 0.5 seconds to execute. There were three tables involved and, after reading this, I believe that the IEnumerable query was actually forming a three table cross-product and filtering the results.

Try to use IQueryables as a rule-of-thumb and profile your work to make your changes measurable.


I would like to clarify a few things due to seemingly conflicting responses (mostly surrounding IEnumerable).

(1) IQueryable extends the IEnumerable interface. (You can send an IQueryable to something which expects IEnumerable without error.)

(2) Both IQueryable and IEnumerable LINQ attempt lazy loading when iterating over the result set. (Note that implementation can be seen in interface extension methods for each type.)

In other words, IEnumerables are not exclusively "in-memory". IQueryables are not always executed on the database. IEnumerable must load things into memory (once retrieved, possibly lazily) because it has no abstract data provider. IQueryables rely on an abstract provider (like LINQ-to-SQL), although this could also be the .NET in-memory provider.

Sample use case

(a) Retrieve list of records as IQueryable from EF context. (No records are in-memory.)

(b) Pass the IQueryable to a view whose model is IEnumerable. (Valid. IQueryable extends IEnumerable.)

(c) Iterate over and access the data set's records, child entities and properties from the view. (May cause exceptions!)

Possible Issues

(1) The IEnumerable attempts lazy loading and your data context is expired. Exception thrown because provider is no longer available.

(2) Entity Framework entity proxies are enabled (the default), and you attempt to access a related (virtual) object with an expired data context. Same as (1).

(3) Multiple Active Result Sets (MARS). If you are iterating over the IEnumerable in a foreach( var record in resultSet ) block and simultaneously attempt to access record.childEntity.childProperty, you may end up with MARS due to lazy loading of both the data set and the relational entity. This will cause an exception if it is not enabled in your connection string.

Solution

  • I have found that enabling MARS in the connection string works unreliably. I suggest you avoid MARS unless it is well-understood and explicitly desired.

Execute the query and store results by invoking resultList = resultSet.ToList() This seems to be the most straightforward way of ensuring your entities are in-memory.

In cases where the you are accessing related entities, you may still require a data context. Either that, or you can disable entity proxies and explicitly Include related entities from your DbSet.


The main difference between “IEnumerable” and “IQueryable” is about where the filter logic is executed. One executes on the client side (in memory) and the other executes on the database.

For example, we can consider an example where we have 10,000 records for a user in our database and let's say only 900 out which are active users, so in this case if we use “IEnumerable” then first it loads all 10,000 records in memory and then applies the IsActive filter on it which eventually returns the 900 active users.

While on the other hand on the same case if we use “IQueryable” it will directly apply the IsActive filter on the database which directly from there will return the 900 active users.

Reference Link


We can use both for the same way, and they are only different in the performance.

IQueryable only executes against the database in an efficient way. It means that it creates an entire select query and only gets the related records.

For example, we want to take the top 10 customers whose name start with ‘Nimal’. In this case the select query will be generated as select top 10 * from Customer where name like ‘Nimal%’.

But if we used IEnumerable, the query would be like select * from Customer where name like ‘Nimal%’ and the top ten will be filtered at the C# coding level (it gets all the customer records from the database and passes them into C#).


In addition to first 2 really good answers (by driis & by Jacob) :

IEnumerable interface is in the System.Collections namespace.

The IEnumerable object represents a set of data in memory and can move on this data only forward. The query represented by the IEnumerable object is executed immediately and completely, so the application receives data quickly.

When the query is executed, IEnumerable loads all the data, and if we need to filter it, the filtering itself is done on the client side.

IQueryable interface is located in the System.Linq namespace.

The IQueryable object provides remote access to the database and allows you to navigate through the data either in a direct order from beginning to end, or in the reverse order. In the process of creating a query, the returned object is IQueryable, the query is optimized. As a result, less memory is consumed during its execution, less network bandwidth, but at the same time it can be processed slightly more slowly than a query that returns an IEnumerable object.

What to choose?

If you need the entire set of returned data, then it's better to use IEnumerable, which provides the maximum speed.

If you DO NOT need the entire set of returned data, but only some filtered data, then it's better to use IQueryable.


In addition to the above, it's interesting to note that you can get exceptions if you use IQueryable instead of IEnumerable:

The following works fine if products is an IEnumerable:

products.Skip(-4);

However if products is an IQueryable and it's trying to access records from a DB table, then you'll get this error:

The offset specified in a OFFSET clause may not be negative.

This is because the following query was constructed:

SELECT [p].[ProductId]
FROM [Products] AS [p]
ORDER BY (SELECT 1)
OFFSET @__p_0 ROWS

and OFFSET can't have a negative value.

참고URL : https://stackoverflow.com/questions/2876616/returning-ienumerablet-vs-iqueryablet

반응형