Programing

.ToList (), .AsEnumerable (), AsQueryable ()의 차이점은 무엇입니까?

crosscheck 2020. 5. 30. 09:23
반응형

.ToList (), .AsEnumerable (), AsQueryable ()의 차이점은 무엇입니까?


LINQ to Entities와 LINQ to Objects의 차이점은 첫 번째 구현 IQueryable과 두 번째 구현 IEnumerable이며 내 질문 범위는 EF 5 내에 있습니다.

내 질문은이 세 가지 방법의 기술적 차이점은 무엇입니까? 나는 많은 상황에서 그들 모두가 작동하는 것을 본다. 또한 같은 조합을 사용하는 것을 볼 수 있습니다 .ToList().AsQueryable().

  1. 그 방법은 정확히 무엇을 의미합니까?

  2. 성능 문제 또는 다른 것을 사용하는 데 문제가 있습니까?

  3. 예를 들어 왜 .ToList().AsQueryable()대신에 사용 .AsQueryable()합니까?


이것에 대해 할 말이 많습니다. 저에 집중하자 AsEnumerableAsQueryable및 언급 ToList()길을 따라.

이 방법들은 무엇을 하는가?

AsEnumerableAsQueryable캐스트 또는 변환에 IEnumerable또는 IQueryable각각. 나는 이유를 가지고 캐스트하거나 변환 한다고 말합니다 .

  • 소스 오브젝트가 이미 대상 인터페이스를 구현하면 소스 오브젝트 자체가 리턴되지만 대상 인터페이스로 캐스트 됩니다. 즉, 유형은 변경되지 않지만 컴파일 타임 유형은 변경됩니다.

  • 소스 객체가 대상 인터페이스를 구현하지 않으면 소스 객체가 대상 인터페이스를 구현하는 객체로 변환 됩니다. 따라서 유형과 컴파일 타임 유형이 모두 변경됩니다.

몇 가지 예를 보여 드리겠습니다. 컴파일 타임 유형과 객체의 실제 유형 ( Jon Skeet 제공 ) 을보고하는이 작은 방법이 있습니다 .

void ReportTypeProperties<T>(T obj)
{
    Console.WriteLine("Compile-time type: {0}", typeof(T).Name);
    Console.WriteLine("Actual type: {0}", obj.GetType().Name);
}

의 임의 LINQ - 투 - SQL 해보자 Table<T>, 구현을 IQueryable:

ReportTypeProperties(context.Observations);
ReportTypeProperties(context.Observations.AsEnumerable());
ReportTypeProperties(context.Observations.AsQueryable());

결과:

Compile-time type: Table`1
Actual type: Table`1

Compile-time type: IEnumerable`1
Actual type: Table`1

Compile-time type: IQueryable`1
Actual type: Table`1

테이블 클래스 자체는 항상 반환되지만 해당 표현은 변경됩니다.

이제가 IEnumerable아닌 을 구현하는 객체 IQueryable:

var ints = new[] { 1, 2 };
ReportTypeProperties(ints);
ReportTypeProperties(ints.AsEnumerable());
ReportTypeProperties(ints.AsQueryable());

결과 :

Compile-time type: Int32[]
Actual type: Int32[]

Compile-time type: IEnumerable`1
Actual type: Int32[]

Compile-time type: IQueryable`1
Actual type: EnumerableQuery`1

거기는. AsQueryable()배열을로 변환 했습니다 . " 컬렉션을 데이터 소스 EnumerableQuery나타냅니다 ." (MSDN).IEnumerable<T>IQueryable<T>

용도는 무엇입니까?

AsEnumerableIQueryable구현이 LINQ에서 객체 (L2O) 로 전환하는 데 자주 사용됩니다 . 대부분 전자가 L2O의 기능을 지원하지 않기 때문입니다. 자세한 내용은 LINQ 엔터티에 AsEnumerable ()의 영향무엇입니까?를 참조하십시오 . .

For example, in an Entity Framework query we can only use a restricted number of methods. So if, for example, we need to use one of our own methods in a query we would typically write something like

var query = context.Observations.Select(o => o.Id)
                   .AsEnumerable().Select(x => MySuperSmartMethod(x))

ToList – which converts an IEnumerable<T> to a List<T> – is often used for this purpose as well. The advantage of using AsEnumerable vs. ToList is that AsEnumerable does not execute the query. AsEnumerable preserves deferred execution and does not build an often useless intermediate list.

On the other hand, when forced execution of a LINQ query is desired, ToList can be a way to do that.

AsQueryable can be used to make an enumerable collection accept expressions in LINQ statements. See here for more details: Do i really need use AsQueryable() on collection?.

Note on substance abuse!

AsEnumerable works like a drug. It's a quick fix, but at a cost and it doesn't address the underlying problem.

In many Stack Overflow answers, I see people applying AsEnumerable to fix just about any problem with unsupported methods in LINQ expressions. But the price isn't always clear. For instance, if you do this:

context.MyLongWideTable // A table with many records and columns
       .Where(x => x.Type == "type")
       .Select(x => new { x.Name, x.CreateDate })

...everything is neatly translated into a SQL statement that filters (Where) and projects (Select). That is, both the length and the width, respectively, of the SQL result set are reduced.

Now suppose users only want to see the date part of CreateDate. In Entity Framework you'll quickly discover that...

.Select(x => new { x.Name, x.CreateDate.Date })

...is not supported (at the time of writing). Ah, fortunately there's the AsEnumerable fix:

context.MyLongWideTable.AsEnumerable()
       .Where(x => x.Type == "type")
       .Select(x => new { x.Name, x.CreateDate.Date })

Sure, it runs, probably. But it pulls the entire table into memory and then applies the filter and the projections. Well, most people are smart enough to do the Where first:

context.MyLongWideTable
       .Where(x => x.Type == "type").AsEnumerable()
       .Select(x => new { x.Name, x.CreateDate.Date })

But still all columns are fetched first and the projection is done in memory.

The real fix is:

context.MyLongWideTable
       .Where(x => x.Type == "type")
       .Select(x => new { x.Name, DbFunctions.TruncateTime(x.CreateDate) })

(But that requires just a little bit more knowledge...)

What do these methods NOT do?

Now an important caveat. When you do

context.Observations.AsEnumerable()
                    .AsQueryable()

you will end up with the source object represented as IQueryable. (Because both methods only cast and don't convert).

But when you do

context.Observations.AsEnumerable().Select(x => x)
                    .AsQueryable()

what will the result be?

The Select produces a WhereSelectEnumerableIterator. This is an internal .Net class that implements IEnumerable, not IQueryable. So a conversion to another type has taken place and the subsequent AsQueryable can never return the original source anymore.

The implication of this is that using AsQueryable is not a way to magically inject a query provider with its specific features into an enumerable. Suppose you do

var query = context.Observations.Select(o => o.Id)
                   .AsEnumerable().Select(x => x.ToString())
                   .AsQueryable()
                   .Where(...)

The where condition will never be translated into SQL. AsEnumerable() followed by LINQ statements definitively cuts the connection with entity framework query provider.

I deliberately show this example because I've seen questions here where people for instance try to 'inject' Include capabilities into a collection by calling AsQueryable. It compiles and runs, but it does nothing because the underlying object does not have an Include implementation anymore.

Specific Implementations

So far, this was only about the Queryable.AsQueryable and Enumerable.AsEnumerable extension methods. But of course anybody can write instance methods or extension methods with the same names (and functions).

In fact, a common example of a specific AsEnumerable extension method is DataTableExtensions.AsEnumerable. DataTable does not implement IQueryable or IEnumerable, so the regular extension methods don't apply.


ToList()

  • Execute the query immediately

AsEnumerable()

  • lazy (execute the query later)
  • Parameter: Func<TSource, bool>
  • Load EVERY record into application memory, and then handle/filter them. (e.g. Where/Take/Skip, it will select * from table1, into the memory, then select the first X elements) (In this case, what it did: Linq-to-SQL + Linq-to-Object)

AsQueryable()

  • lazy (execute the query later)
  • Parameter: Expression<Func<TSource, bool>>
  • Convert Expression into T-SQL (with the specific provider), query remotely and load result to your application memory.
  • That’s why DbSet (in Entity Framework) also inherits IQueryable to get the efficient query.
  • Do not load every record, e.g. if Take(5), it will generate select top 5 * SQL in the background. This means this type is more friendly to SQL Database, and that is why this type usually has higher performance and is recommended when dealing with a database.
  • So AsQueryable() usually works much faster than AsEnumerable() as it generate T-SQL at first, which includes all your where conditions in your Linq.

ToList() will being everything in memory and then you will be working on it. so, ToList().where ( apply some filter ) is executed locally. AsQueryable() will execute everything remotely i.e. a filter on it is sent to the database for applying. Queryable doesn't do anything til you execute it. ToList, however executes immediately.

Also, look at this answer Why use AsQueryable() instead of List()?.

EDIT : Also, in your case once you do ToList() then every subsequent operation is local including AsQueryable(). You can't switch to remote once you start executing locally. Hope this makes it a little bit more clearer.


Encountered a bad performance on below code.

void DoSomething<T>(IEnumerable<T> objects){
    var single = objects.First(); //load everything into memory before .First()
    ...
}

Fixed with

void DoSomething<T>(IEnumerable<T> objects){
    T single;
    if (objects is IQueryable<T>)
        single = objects.AsQueryable().First(); // SELECT TOP (1) ... is used
    else
        single = objects.First();

}

For an IQueryable, stay in IQueryable when possible, try not be used like IEnumerable.

Update. It can be further simplified in one expression, thanks Gert Arnold.

T single =  objects is IQueryable<T> q? 
                    q.First(): 
                    objects.First();

참고URL : https://stackoverflow.com/questions/17968469/whats-the-differences-between-tolist-asenumerable-asqueryable

반응형