Bununla ilgili söylenecek çok şey var. Beni odaklanalım AsEnumerable
ve AsQueryable
ve söz ToList()
yol boyunca.
Bu yöntemler ne işe yarar?
AsEnumerable
ve AsQueryable
dökme veya dönüştürme için IEnumerable
ya IQueryable
sı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 IEnumerable
değ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?
AsEnumerable
IQueryable
ç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ı AsEnumerable
VS. ToList
olmasıdır AsEnumerable
sorguyu çalıştırmak değildir. AsEnumerable
ertelenmiş 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, ToList
bunu yapmanın bir yolu olabilir.
AsQueryable
LINQ 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!
AsEnumerable
ilaç 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 AsEnumerable
LINQ 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 Where
ilkini 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?
Select
Bir ü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 Include
arayarak 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 AsQueryable
ve AsEnumerable
yürütmek (veya yok numaralandırmak kaynak nesneyi). Sadece türlerini veya temsillerini değiştirirler. Her ikisi de ilgili arayüzler IQueryable
ve 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.AsEnumerable
IQueryable
IQueryable
IEnumerable
IQueryable
Özel Uygulamalar
Şimdiye kadar, bu sadece Queryable.AsQueryable
ve Enumerable.AsEnumerable
geniş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 AsEnumerable
uzatma yönteminin yaygın bir örneğidir DataTableExtensions.AsEnumerable
. DataTable
uygulanmaz IQueryable
veya IEnumerable
normal genişletme yöntemleri uygulanmaz.