Linq ile IQueryable'ı kullanma


257

IQueryableLINQ bağlamında kullanımı nedir ?

Uzatma yöntemleri veya başka bir amaç geliştirmek için kullanılıyor mu?

Yanıtlar:


508

Marc Gravell'in cevabı çok eksiksiz, ancak kullanıcının bakış açısından da bu konuda bir şeyler ekleyeceğimi düşündüm ...


Bir kullanıcının bakış açısından ana fark, kullandığınızda IQueryable<T> (işleri doğru bir şekilde destekleyen bir sağlayıcıyla) kullandığınızda, çok fazla kaynak kaydedebilmenizdir.

Örneğin, birçok ORM sistemiyle uzak bir veritabanına karşı çalışıyorsanız, bir tablodan veri döndüren IEnumerable<T>ve biri döndüren biri olmak üzere iki şekilde veri alma seçeneğiniz vardır IQueryable<T>. Örneğin, bir Ürünler tablonuz var ve maliyeti> 25 ABD doları olan tüm ürünleri almak istiyorsunuz.

Yaparsan:

 IEnumerable<Product> products = myORM.GetProducts();
 var productsOver25 = products.Where(p => p.Cost >= 25.00);

Burada ne olur, veritabanı tüm ürünleri yükler ve onları telden programınıza geçirir. Programınız daha sonra verileri filtreler. Özünde, veritabanı a yapar SELECT * FROM Productsve HER ürün size döner.

Doğru IQueryable<T>sağlayıcı ile diğer taraftan şunları yapabilirsiniz:

 IQueryable<Product> products = myORM.GetQueryableProducts();
 var productsOver25 = products.Where(p => p.Cost >= 25.00);

Kod aynı görünüyor, ancak buradaki fark, yürütülen SQL'in SELECT * FROM Products WHERE Cost >= 25 .

Bir geliştirici olarak POV'nuzdan bu aynı görünüyor. Ancak, performans açısından, 20.000 yerine ağda yalnızca 2 kayıt döndürebilirsiniz.


9
"GetQueryableProducts ();" tanımı nerede?
Pankaj

12
@StackOverflowUser IQueryable<Product>ORM veya deponuza özgü bir - döndüren herhangi bir yöntem olması amaçlanmıştır
Reed Copsey

sağ. ama siz bu fonksiyondan sonra nerede fıkradan bahsetmiştiniz. Sistem hala filtrenin farkında değil. Demek istediğim, hala tüm ürün kayıtlarını getiriyor. sağ?
Pankaj

@StackOverflowUser Hayır - bu IQueryable <T> 'nin güzelliği - sonuçları aldığınızda değerlendirmek için ayarlanabilir - bu, gerçekten sonra kullanılan Where cümlesi hala sunucuda çalışan bir SQL deyimine çevrilecek ve tel boyunca sadece gerekli elemanları çekin ...
Reed Copsey

40
@Testing Aslında hala DB'ye gitmiyorsunuz. Sonuçları gerçekten numaralandırana kadar (yani: a foreachveya çağrı kullanın ToList()), DB'ye gerçekten vurmazsınız.
Reed Copsey

188

Özünde işi IEnumerable<T>- sorgulanabilir bir veri kaynağını temsil etmek - çok farklı LINQ yöntemlerinin (on Queryable) daha spesifik olabilmesi, Expressiondelegelerden ziyade ağaçları kullanarak sorguyu oluşturmaktır.Enumerable .

İfade ağaçları, seçtiğiniz LINQ sağlayıcısı tarafından denetlenebilir ve gerçek bir sorguya dönüştürülebilir - ancak bu kendi içinde siyah bir sanattır.

Bu gerçekten ElementType, Expressionve Provider- ama gerçekte nadiren bunu bir kullanıcı olarak önemsemeniz gerekir . Sadece bir LINQ uygulayıcısı kanlı detayları bilmelidir.


Yeniden yorumlar; Örnek olarak ne istediğinizden emin değilim ama LINQ-to-SQL'i düşünün; Buradaki ana nesne DataContext, veritabanı paketleyicimizi temsil eden bir a . Bu genellikle tablo başına bir özelliğe (örneğin Customers) sahiptir ve bir tablo uygular IQueryable<Customer>. Ama bunu doğrudan kullanmıyoruz; düşünmek:

using(var ctx = new MyDataContext()) {
    var qry = from cust in ctx.Customers
              where cust.Region == "North"
              select new { cust.Id, cust.Name };
    foreach(var row in qry) {
        Console.WriteLine("{0}: {1}", row.Id, row.Name);
    }
}

bu olur (C # derleyicisi tarafından):

var qry = ctx.Customers.Where(cust => cust.Region == "North")
                .Select(cust => new { cust.Id, cust.Name });

yine (C # derleyicisi tarafından) şu şekilde yorumlanır:

var qry = Queryable.Select(
              Queryable.Where(
                  ctx.Customers,
                  cust => cust.Region == "North"),
              cust => new { cust.Id, cust.Name });

Daha da önemlisi, Queryablenormal IL yerine bir nesne modeline derlenen ifade ağaçlarını almadaki statik yöntemler . Örneğin - sadece "Nerede" ye baktığımızda, bu bize aşağıdakilerle karşılaştırılabilir bir şey verir:

var cust = Expression.Parameter(typeof(Customer), "cust");
var lambda = Expression.Lambda<Func<Customer,bool>>(
                  Expression.Equal(
                      Expression.Property(cust, "Region"),
                      Expression.Constant("North")
                  ), cust);

... Queryable.Where(ctx.Customers, lambda) ...

Derleyici bizim için çok şey yapmadı mı? Bu nesne modeli parçalanabilir, ne anlama geldiğini kontrol edilebilir ve TSQL jeneratörü tarafından tekrar bir araya getirilebilir:

 SELECT c.Id, c.Name
 FROM [dbo].[Customer] c
 WHERE c.Region = 'North'

(dize bir parametre olarak bitebilir; hatırlayamıyorum)

Delege kullanmış olsaydık bunların hiçbiri mümkün olmazdı. Ve bu nokta Queryable/ IQueryable<T>: ifade ağaçlarını kullanmak için giriş noktası sağlar.

Bütün bunlar çok karmaşık, bu yüzden derleyicinin bizim için güzel ve kolay olmasını sağlamak iyi bir iş.

Daha fazla bilgi için , her ikisi de bu konuların kapsamını sağlayan " C # Derinlik " veya " LINQ Hareket Halinde " konularına bakın.


2
Eğer sakıncası yoksa beni basit anlaşılır bir örnekle güncelleyebilir misin (zamanın varsa).
user190560

"GetQueryableProducts () tanımı nedir? "?" Reed Copsey cevap
Pankaj

İfadeleri bir sorguya çevirme çizgisinden keyif
aldık

Bir delege kullansaydınız neden mümkün olmazdı?
David Klempfner

1
@Backwards_Dave çünkü bir temsilci (esasen) IL'yi işaret eder ve IL, SQL oluşturma amacını yeterince bozmayı denemeyi makul kılmak için yeterince etkileyici değildir. IL ayrıca izin verir çok fazla IL ifade edilebilir yani birçok şey - şey olamazdı o SQL gibi şeyler dönüşme makul olduğunu sınırlı sözdiziminde ifade
Marc Gravell

17

Reed Copsey ve Marc Gravell zaten yeterince IQueryable(ve ayrıca IEnumerable) hakkında açıklamış olsa da , küçük bir örnek vererek IQueryableve IEnumerablebirçok kullanıcının istediği gibi buraya biraz daha eklemek istiyorum

Örnek : Veritabanında iki tablo oluşturdum

   CREATE TABLE [dbo].[Employee]([PersonId] [int] NOT NULL PRIMARY KEY,[Gender] [nchar](1) NOT NULL)
   CREATE TABLE [dbo].[Person]([PersonId] [int] NOT NULL PRIMARY KEY,[FirstName] [nvarchar](50) NOT NULL,[LastName] [nvarchar](50) NOT NULL)

PersonIdTablonun Birincil anahtarı ( ) da tablonun Employeebir forgein anahtarıdır ( personid)Person

Daha sonra benim uygulama ado.net varlık modeli ekledi ve bu konuda aşağıda hizmet sınıfı oluşturmak

public class SomeServiceClass
{   
    public IQueryable<Employee> GetEmployeeAndPersonDetailIQueryable(IEnumerable<int> employeesToCollect)
    {
        DemoIQueryableEntities db = new DemoIQueryableEntities();
        var allDetails = from Employee e in db.Employees
                         join Person p in db.People on e.PersonId equals p.PersonId
                         where employeesToCollect.Contains(e.PersonId)
                         select e;
        return allDetails;
    }

    public IEnumerable<Employee> GetEmployeeAndPersonDetailIEnumerable(IEnumerable<int> employeesToCollect)
    {
        DemoIQueryableEntities db = new DemoIQueryableEntities();
        var allDetails = from Employee e in db.Employees
                         join Person p in db.People on e.PersonId equals p.PersonId
                         where employeesToCollect.Contains(e.PersonId)
                         select e;
        return allDetails;
    }
}

onlar aynı linq içerir. program.csAşağıda tanımlandığı gibi çağrıldı

class Program
{
    static void Main(string[] args)
    {
        SomeServiceClass s= new SomeServiceClass(); 

        var employeesToCollect= new []{0,1,2,3};

        //IQueryable execution part
        var IQueryableList = s.GetEmployeeAndPersonDetailIQueryable(employeesToCollect).Where(i => i.Gender=="M");            
        foreach (var emp in IQueryableList)
        {
            System.Console.WriteLine("ID:{0}, EName:{1},Gender:{2}", emp.PersonId, emp.Person.FirstName, emp.Gender);
        }
        System.Console.WriteLine("IQueryable contain {0} row in result set", IQueryableList.Count());

        //IEnumerable execution part
        var IEnumerableList = s.GetEmployeeAndPersonDetailIEnumerable(employeesToCollect).Where(i => i.Gender == "M");
        foreach (var emp in IEnumerableList)
        {
           System.Console.WriteLine("ID:{0}, EName:{1},Gender:{2}", emp.PersonId, emp.Person.FirstName, emp.Gender);
        }
        System.Console.WriteLine("IEnumerable contain {0} row in result set", IEnumerableList.Count());

        Console.ReadKey();
    }
}

Çıktı her ikisi için de aynı

ID:1, EName:Ken,Gender:M  
ID:3, EName:Roberto,Gender:M  
IQueryable contain 2 row in result set  
ID:1, EName:Ken,Gender:M  
ID:3, EName:Roberto,Gender:M  
IEnumerable contain 2 row in result set

Soru şu: Fark ne / nerede? Fark yok gibi görünüyor değil mi? Gerçekten mi!!

Bu dönemde varlık çerçevesi 5 tarafından oluşturulan ve yürütülen sql sorgularına bir göz atalım

IQueryable yürütme bölümü

--IQueryableQuery1 
SELECT 
[Extent1].[PersonId] AS [PersonId], 
[Extent1].[Gender] AS [Gender]
FROM [dbo].[Employee] AS [Extent1]
WHERE ([Extent1].[PersonId] IN (0,1,2,3)) AND (N'M' = [Extent1].[Gender])

--IQueryableQuery2
SELECT 
[GroupBy1].[A1] AS [C1]
FROM ( SELECT 
    COUNT(1) AS [A1]
    FROM [dbo].[Employee] AS [Extent1]
    WHERE ([Extent1].[PersonId] IN (0,1,2,3)) AND (N'M' = [Extent1].[Gender])
)  AS [GroupBy1]

IEnumerable yürütme bölümü

--IEnumerableQuery1
SELECT 
[Extent1].[PersonId] AS [PersonId], 
[Extent1].[Gender] AS [Gender]
FROM [dbo].[Employee] AS [Extent1]
WHERE [Extent1].[PersonId] IN (0,1,2,3)

--IEnumerableQuery2
SELECT 
[Extent1].[PersonId] AS [PersonId], 
[Extent1].[Gender] AS [Gender]
FROM [dbo].[Employee] AS [Extent1]
WHERE [Extent1].[PersonId] IN (0,1,2,3)

Her iki yürütme bölümü için ortak komut dosyası

/* these two query will execute for both IQueryable or IEnumerable to get details from Person table
   Ignore these two queries here because it has nothing to do with IQueryable vs IEnumerable
--ICommonQuery1 
exec sp_executesql N'SELECT 
[Extent1].[PersonId] AS [PersonId], 
[Extent1].[FirstName] AS [FirstName], 
[Extent1].[LastName] AS [LastName]
FROM [dbo].[Person] AS [Extent1]
WHERE [Extent1].[PersonId] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=1

--ICommonQuery2
exec sp_executesql N'SELECT 
[Extent1].[PersonId] AS [PersonId], 
[Extent1].[FirstName] AS [FirstName], 
[Extent1].[LastName] AS [LastName]
FROM [dbo].[Person] AS [Extent1]
WHERE [Extent1].[PersonId] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=3
*/

Şimdi birkaç sorunuz var, bunları tahmin edeyim ve cevaplamaya çalışalım

Aynı sonuç için neden farklı komut dosyaları üretiliyor?

Burada bazı noktalar bulalım,

tüm sorguların ortak bir bölümü vardır

WHERE [Extent1].[PersonId] IN (0,1,2,3)

neden? Fonksiyon hem Çünkü IQueryable<Employee> GetEmployeeAndPersonDetailIQueryableve IEnumerable<Employee> GetEmployeeAndPersonDetailIEnumerablebir SomeServiceClasslinq sorguları tek ortak satır içerir

where employeesToCollect.Contains(e.PersonId)

Her iki işlev çağrısında program.cs` kullanılırken, parça yürütme bölümünde neden AND (N'M' = [Extent1].[Gender])eksik.IEnumerableWhere(i => i.Gender == "M") in

Şimdi farkın ortaya çıktığı noktadayız IQueryableve IEnumerable

Varlık çerçevelemesi, IQueryable yöntem çağrıldığında yöntem yöntem içinde yazılı linq deyimi alır ve sonuç kümesinde daha fazla linq ifadesi tanımlanıp tanımlanmadığını bulmaya çalışır, daha sonra sonuç getirilmesi gerekene kadar tanımlanan tüm linq sorgularını toplar ve daha uygun sql oluşturur yürütmek için sorgu.

Gibi birçok fayda sağlar,

  • yalnızca tüm sql server yürütme tarafından geçerli olabilecek sql sunucusu tarafından doldurulmuş satırlar
  • gereksiz satırları seçerek sql sunucusu performansına yardımcı olur
  • ağ maliyeti azalıyor

gibi burada sql sunucusu IQueryable yürütme` sonra sadece iki satır uygulamaya döndü ama IEnumerable sorgu için ÜÇ satır döndürdü neden?

IEnumerableYöntem durumunda , varlık çerçevesi yöntem içinde yazılı linq deyimi aldı ve sonuç getirilmesi gerektiğinde sql sorgusu oluşturur. sql sorgusunu oluşturmak için rest linq parçasını içermez. Burada olduğu gibi sütun üzerinde sql sunucusunda hiçbir filtreleme yapılmaz gender.

Ama çıktılar aynı mı? Çünkü 'IEnumerable, sonucu sql sunucusundan aldıktan sonra uygulama düzeyinde daha fazla filtreler

SO, birileri ne seçmeliyim? Ben şahsen fonksiyon sonucunu tanımlamayı tercih ediyorum IQueryable<T>çünkü çok fazla faydası varIEnumerable gibi, sql sunucusuna daha spesifik komut dosyası üreten iki veya daha fazla IQueryable fonksiyonları birleştirebilirsiniz.

Burada örnekte benim bakış açımdan çok daha kabul edilebilir IQueryable Query(IQueryableQuery2)olandan daha spesifik bir komut dosyası IEnumerable query(IEnumerableQuery2)oluşturduğunu görebilirsiniz.


2

Hat üzerinde daha fazla sorgulamaya izin verir. Bu bir hizmet sınırının ötesinde olsaydı, bu IQueryable nesnesinin kullanıcısının onunla daha fazlasını yapmasına izin verilirdi.

Örneğin nhibernate ile tembel yükleme kullanıyorsanız, bu gerektiğinde / gerekirse grafiğin yüklenmesine neden olabilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.