Bununla ilgili söylenecek çok şey var. Beni odaklanalım AsEnumerableve AsQueryableve söz ToList()yol boyunca.
Bu yöntemler ne işe yarar?
AsEnumerableve AsQueryabledökme veya dönüştürme için IEnumerableya IQueryablesırasıyla. Döküm ya da bir sebeple dönüştürmek diyorum :
Kaynak nesne zaten hedef arabirimini uygulayan, kaynak nesnenin kendisi döndürülür fakat döküm hedef arabirimine. Başka bir deyişle: tür değiştirilmez, ancak derleme zamanı türü değiştirilir.
Kaynak nesne hedef arabirimi uygulamadığında, kaynak nesne hedef arabirimi uygulayan bir nesneye dönüştürülür . Böylece hem tür hem de derleme zamanı türü değiştirilir.
Bunu bazı örneklerle göstereyim. Derleme zamanı türü ve bir nesnenin gerçek türü ( nezaket Jon Skeet ) bildiren bu küçük yöntem var :
void ReportTypeProperties<T>(T obj)
{
Console.WriteLine("Compile-time type: {0}", typeof(T).Name);
Console.WriteLine("Actual type: {0}", obj.GetType().Name);
}
Şimdi Table<T>uygulayan keyfi bir linq-sql'yi deneyelim IQueryable:
ReportTypeProperties(context.Observations);
ReportTypeProperties(context.Observations.AsEnumerable());
ReportTypeProperties(context.Observations.AsQueryable());
Sonuç:
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
Tablo sınıfının kendisinin her zaman döndürüldüğünü görürsünüz, ancak temsili değişir.
Şimdi bir nesne olduğunu uygular IEnumerabledeğil IQueryable:
var ints = new[] { 1, 2 };
ReportTypeProperties(ints);
ReportTypeProperties(ints.AsEnumerable());
ReportTypeProperties(ints.AsQueryable());
Sonuçlar:
Compile-time type: Int32[]
Actual type: Int32[]
Compile-time type: IEnumerable`1
Actual type: Int32[]
Compile-time type: IQueryable`1
Actual type: EnumerableQuery`1
İşte burada. AsQueryable()diziyi EnumerableQuery" IEnumerable<T>bir IQueryable<T>veri kaynağı olarak bir koleksiyonu temsil eder" dizisine dönüştürdü . (MSDN).
Ne yararı var?
AsEnumerableIQueryableçoğunlukla L2O'nun sahip olduğu işlevleri desteklemediği için herhangi bir uygulamadan LINQ'ya nesnelere (L2O) geçmek için kullanılır. Daha fazla ayrıntı için bkz . AsEnumerable () öğesinin LINQ Varlığı üzerindeki etkisi nedir? .
Örneğin, bir Entity Framework sorgusunda yalnızca sınırlı sayıda yöntem kullanabiliriz. Örneğin, bir sorguda kendi yöntemlerimizden birini kullanmamız gerekirse, genellikle
var query = context.Observations.Select(o => o.Id)
.AsEnumerable().Select(x => MySuperSmartMethod(x))
ToList - Bir dönüştüren IEnumerable<T>bir karşı List<T>- genellikle de bu amaç için kullanılır. Kullanmanın avantajı AsEnumerableVS. ToListolmasıdır AsEnumerablesorguyu çalıştırmak değildir. AsEnumerableertelenmiş yürütmeyi korur ve genellikle işe yaramaz bir ara liste oluşturmaz.
Öte yandan, bir LINQ sorgusunun zorla yürütülmesi istendiğinde, ToListbunu yapmanın bir yolu olabilir.
AsQueryableLINQ ifadelerinde numaralandırılabilir bir koleksiyon kabul ifadeleri yapmak için kullanılabilir. Daha fazla ayrıntı için buraya bakın: Koleksiyonda gerçekten AsQueryable () kullanmam gerekiyor mu? .
Madde bağımlılığı hakkında not!
AsEnumerableilaç gibi çalışır. Bu hızlı bir düzeltme, ancak bir maliyetle ve altta yatan sorunu çözmez.
Birçok Stack Overflow yanıtında, insanların AsEnumerableLINQ ifadelerinde desteklenmeyen yöntemlerle ilgili herhangi bir sorunu düzeltmek için başvurduğunu görüyorum . Ancak fiyat her zaman net değildir. Örneğin, bunu yaparsanız:
context.MyLongWideTable // A table with many records and columns
.Where(x => x.Type == "type")
.Select(x => new { x.Name, x.CreateDate })
... her şey düzgün bir şekilde filtreler ( Where) ve projeler ( Select) bir SQL deyimine çevrilir . Yani, SQL sonuç kümesinin sırasıyla uzunluğu ve genişliği azaltılır.
Şimdi kullanıcıların yalnızca tarih bölümünü görmek istediğini varsayalım CreateDate. Entity Framework'te bunu çabucak keşfedeceksiniz ...
.Select(x => new { x.Name, x.CreateDate.Date })
... desteklenmez (yazma sırasında). Neyse ki çözüm var AsEnumerable:
context.MyLongWideTable.AsEnumerable()
.Where(x => x.Type == "type")
.Select(x => new { x.Name, x.CreateDate.Date })
Tabii, muhtemelen çalışıyor. Ancak tüm tabloyu belleğe çeker ve ardından filtreyi ve çıkıntıları uygular. Çoğu insan Whereilkini yapacak kadar zekidir :
context.MyLongWideTable
.Where(x => x.Type == "type").AsEnumerable()
.Select(x => new { x.Name, x.CreateDate.Date })
Ancak yine de tüm sütunlar önce getirilir ve projeksiyon bellekte yapılır.
Gerçek düzeltme:
context.MyLongWideTable
.Where(x => x.Type == "type")
.Select(x => new { x.Name, DbFunctions.TruncateTime(x.CreateDate) })
(Ancak bu biraz daha fazla bilgi gerektirir ...)
Bu yöntemler ne YAPAMAZ?
IQueryable yeteneklerini geri yükleme
Şimdi önemli bir uyarı. Ne zaman yaparsın
context.Observations.AsEnumerable()
.AsQueryable()
sonunda temsil edilen kaynak nesneyle sonuçlanacaksınız IQueryable. (Çünkü her iki yöntem de sadece döküm yapar ve dönüştürmez).
Ama yaptığın zaman
context.Observations.AsEnumerable().Select(x => x)
.AsQueryable()
sonuç ne olacak?
SelectBir üretir WhereSelectEnumerableIterator. Bunun anlamı uygular dahili Net sınıftır IEnumerable, değilIQueryable . Böylece başka bir türe dönüştükten sonraAsQueryable orijinal kaynak artık geri döndürülemez.
Bunun anlamı, kullanmanın AsQueryable, bir sorgu sağlayıcısını belirli özellikleriyle sihirli bir şekilde numaralandırılabilir bir şekilde enjekte etmenin bir yolu olmamasıdır . Varsayalım
var query = context.Observations.Select(o => o.Id)
.AsEnumerable().Select(x => x.ToString())
.AsQueryable()
.Where(...)
Where koşulu asla SQL'e çevrilmez. AsEnumerable()ardından LINQ ifadeleri varlık çerçevesi sorgu sağlayıcısıyla bağlantıyı kesin olarak keser.
Bu örneği kasten gösteriyorum çünkü burada insanların örneğin Includearayarak yetenekleri bir koleksiyona 'enjekte etmeye' çalıştıkları sorular gördüm AsQueryable. Derler ve çalışır, ancak hiçbir şey yapmaz çünkü altta yatan nesneninInclude artık uygulaması yoktur.
gerçekleştirmek
Hem AsQueryableve AsEnumerableyürütmek (veya yok numaralandırmak kaynak nesneyi). Sadece türlerini veya temsillerini değiştirirler. Her ikisi de ilgili arayüzler IQueryableve IEnumerable"gerçekleşmeyi bekleyen bir numaralandırma" dan başka bir şey değildir. Örneğin, yukarıda belirtildiği gibi, arayarak zorla çalıştırılmadan infaz edilmezler ToList().
Bu , bir nesneyi IEnumerableçağırarak elde edilen bir öğeyi yürütmenin temelini yürüteceği anlamına gelir . Daha sonra iradenin yerine getirilmesi . Bu çok pahalı olabilir.AsEnumerableIQueryableIQueryableIEnumerableIQueryable
Özel Uygulamalar
Şimdiye kadar, bu sadece Queryable.AsQueryableve Enumerable.AsEnumerablegenişletme yöntemleri ile ilgiliydi . Ancak elbette herkes aynı adlara (ve işlevlere) sahip örnek yöntemleri veya genişletme yöntemleri yazabilir.
Aslında, belirli bir AsEnumerableuzatma yönteminin yaygın bir örneğidir DataTableExtensions.AsEnumerable. DataTableuygulanmaz IQueryableveya IEnumerablenormal genişletme yöntemleri uygulanmaz.