IQueryable
LINQ bağlamında kullanımı nedir ?
Uzatma yöntemleri veya başka bir amaç geliştirmek için kullanılıyor mu?
IQueryable
LINQ bağlamında kullanımı nedir ?
Uzatma yöntemleri veya başka bir amaç geliştirmek için kullanılıyor mu?
Yanıtlar:
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 Products
ve 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.
IQueryable<Product>
ORM veya deponuza özgü bir - döndüren herhangi bir yöntem olması amaçlanmıştır
foreach
veya çağrı kullanın ToList()
), DB'ye gerçekten vurmazsınız.
Özünde işi IEnumerable<T>
- sorgulanabilir bir veri kaynağını temsil etmek - çok farklı LINQ yöntemlerinin (on Queryable
) daha spesifik olabilmesi, Expression
delegelerden 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
, Expression
ve 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, Queryable
normal 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.
Reed Copsey ve Marc Gravell zaten yeterince IQueryable
(ve ayrıca IEnumerable
) hakkında açıklamış olsa da , küçük bir örnek vererek IQueryable
ve IEnumerable
birç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)
PersonId
Tablonun Birincil anahtarı ( ) da tablonun Employee
bir 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.cs
Aş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> GetEmployeeAndPersonDetailIQueryable
ve
IEnumerable<Employee> GetEmployeeAndPersonDetailIEnumerable
bir SomeServiceClass
linq 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.IEnumerable
Where(i => i.Gender == "M") in
Şimdi farkın ortaya çıktığı noktadayız
IQueryable
veIEnumerable
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,
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?
IEnumerable
Yö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.
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.