مقدمه
در درسهای گذشته، زیرساخت Entity Framework Core را تنظیم کرده، مدلهای خود را تعریف و
پایگاه دادهی متناظر با آنها را ایجاد کردیم. اکنون به یکی از جذابترین بخشهای کار با EF Core
میرسیم: کوئری زدن به دادهها با استفاده از LINQ.
EF Core به ما اجازه میدهد تا با استفاده از همان سینتکس آشنای LINQ که برای
کار با کالکشنها یاد گرفتیم، کوئریهای خود را به زبان C# بنویسیم. سپس، EF Core این
کوئریهای LINQ را به کد SQL بهینه ترجمه کرده، آن را بر
روی پایگاه داده اجرا میکند و نتایج را به صورت اشیاء C# به ما برمیگرداند. این فرآیند
به ما اجازه میدهد تا بدون نوشتن حتی یک خط کد SQL، با پایگاه دادهی خود به صورت
کاملاً شیءگرا تعامل داشته باشیم.
DbContext و DbSet به عنوان نقطهی شروع
همانطور که قبلاً دیدیم، نقطهی شروع ما برای هرگونه تعامل با پایگاه داده، یک نمونه از کلاس
DbContext سفارشی ماست. پراپرتیهای DbSet<T> که در این
کلاس تعریف کردهایم، نمایندهی جداول
پایگاه داده هستند و به عنوان منبع اصلی برای کوئریهای LINQ عمل میکنند.
بیایید ابتدا چند دادهی نمونه را به جدول Products خود اضافه کنیم تا بتوانیم روی آنها کوئری
بزنیم.
Program.cs
using (var db = new MyDbContext())
{
if (!db.Products.Any())
{
db.Products.Add(new Product { Name = "Laptop", Price = 1200m, Stock = 10 });
db.Products.Add(new Product { Name = "Mouse", Price = 25m, Stock = 50 });
db.Products.Add(new Product { Name = "Keyboard", Price = 75m, Stock = 30 });
db.SaveChanges();
}
}
در این کد، ما ابتدا با استفاده از db.Products.Any() بررسی میکنیم که آیا جدولی خالی است یا
خیر. اگر خالی بود، چند محصول جدید به DbSet اضافه کرده و در نهایت، با فراخوانی متد
SaveChanges() این تغییرات را در پایگاه داده ذخیره میکنیم.
خواندن و فیلتر کردن دادهها
اکنون که داده داریم، میتوانیم با استفاده از LINQ آنها را بخوانیم.
Program.cs
using (var db = new MyDbContext())
{
var expensiveProducts = db.Products
.Where(p => p.Price > 100m)
.ToList();
Console.WriteLine("--- Expensive Products ---");
foreach (var product in expensiveProducts)
{
Console.WriteLine($"{product.Name} costs {product.Price:C}");
}
}
این کد بسیار شبیه به کار با یک کالکشن عادی در حافظه است، اما یک اتفاق بسیار مهم در پشت صحنه
میافتد: عبارت db.Products.Where(p => p.Price > 100m) به کد
SQL زیر (یا مشابه آن) ترجمه میشود:
SELECT [Id], [Name], [Price], [Stock]
FROM [Products]
WHERE [Price] > 100
این کوئری SQL بر روی سرور پایگاه داده اجرا میشود و تنها نتایجی که با شرط مطابقت
دارند، به برنامهی ما برگردانده میشوند. این فرآیند بسیار بهینه است، زیرا ما تمام دادهها را به
کلاینت منتقل نکرده و فیلترینگ را در سمت سرور انجام میدهیم.
نکته مهم: مانند LINQ to Objects، کوئریهای EF Core نیز
دارای اجرای تأخیری (Deferred Execution) هستند. کوئری تنها زمانی به
SQL ترجمه و اجرا میشود که شما شروع به پیمایش نتایج آن کنید (مثلاً با یک حلقهی
foreach) یا با فراخوانی متدهایی مانند ToList()، ToArray() یا FirstOrDefault() صراحتاً
درخواست اجرای آن را بدهید.
ترکیب عملگرهای LINQ
ما میتوانیم تمام عملگرهای استاندارد LINQ را با هم زنجیره کنیم تا کوئریهای
پیچیدهتری بسازیم.
Program.cs
using (var db = new MyDbContext())
{
var lowStockProductNames = db.Products
.Where(p => p.Stock < 40)
.OrderBy(p => p.Name)
.Select(p => p.Name)
.ToList();
Console.WriteLine("\n--- Low Stock Products (Names Only) ---");
foreach (var name in lowStockProductNames)
{
Console.WriteLine(name);
}
}
در این مثال، EF Core کل این زنجیره را به یک کوئری SQL واحد و بهینه ترجمه
میکند که فیلتر کردن، مرتبسازی و انتخاب ستون Name را مستقیماً بر روی سرور پایگاه داده انجام
میدهد. این کارایی بالایی را تضمین میکند، زیرا تنها دادههای مورد نیاز (یک لیست از نامها) از
طریق شبکه به برنامهی ما منتقل میشوند.