بهینه‌سازی عملکرد Entity Framework Core: نکات و بهترین روش‌ها

بهینه‌سازی عملکرد Entity Framework Core: نکات و بهترین روش‌ها
آنچه در این پست میخوانید

Entity Framework Core (EF Core) یک ORM قدرتمند است که فرآیند دسترسی به داده‌ها را ساده می‌کند و پیچیدگی‌های تعاملات پایگاه داده را به حداقل می‌رساند. با این حال، اگر به درستی استفاده نشود، ممکن است باعث کاهش کارایی و ایجاد گلوگاه‌های عملکردی شود. در این مقاله به بررسی روش‌های مؤثر بهینه‌سازی عملکرد EF Core پرداخته‌ایم تا برنامه‌هایتان حتی در مواجهه با مجموعه داده‌های بزرگ، به صورت کارآمد اجرا شوند.

۱. بهینه‌سازی کوئری‌ها (Query Optimization)

EF Core کوئری‌های SQL را برای شما تولید می‌کند، اما این بدان معنا نیست که همیشه بهینه‌ترین کوئری‌ها تولید می‌شوند. برای جلوگیری از مشکلات عملکردی، باید بهینه‌نویسی کوئری‌های LINQ که به SQL تبدیل می‌شوند را یاد بگیرید.

فیلترگذاری زودهنگام (Early Filtering)

همیشه فیلترها (Where) را در ابتدای کوئری اعمال کنید تا داده‌های غیرضروری به حافظه بارگذاری نشوند. این کار باعث جلوگیری از دریافت داده‌های بی‌ربط می‌شود.

var orders = context.Orders
                    .Where(o => o.CustomerId == customerId)
                    .ToList();

جلوگیری از مشکل N+1

در هنگام کوئری‌گیری داده‌های مرتبط، از تکرار درخواست‌های پایگاه داده جلوگیری کنید و از بارگذاری مشتاقانه (Eager Loading) استفاده کنید تا داده‌های مرتبط در یک درخواست بارگذاری شوند.

var orders = context.Orders
                    .Include(o => o.Customer)
                    .ToList();

استفاده از Projections (پروژکشن‌ها)

برای دریافت تنها داده‌های مورد نیاز، به جای بارگذاری تمامی موجودیت‌ها از Projections (پروژکشن‌ها) استفاده کنید.

var orderSummaries = context.Orders
                            .Where(o => o.CustomerId == customerId)
                            .Select(o => new { o.OrderId, o.TotalAmount })
                            .ToList();

۲. استفاده از Tracking و No-Tracking

به‌طور پیش‌فرض، EF Core تغییرات موجودیت‌ها را پیگیری می‌کند. اگر فقط به داده‌ها برای خواندن نیاز دارید، از No-Tracking استفاده کنید تا هزینه‌های اضافی پیگیری تغییرات کاهش یابد.

زمانی که باید از Tracking استفاده کنید:

  • هنگام نیاز به بروزرسانی یا حذف موجودیت‌ها.
var customer = context.Customers.FirstOrDefault(c => c.Id == id);
customer.Name = "Updated Name";
context.SaveChanges();  // Tracking فعال است.

زمانی که باید از No-Tracking استفاده کنید:

  • هنگامی که داده‌ها فقط برای خواندن هستند.
var customers = context.Customers.AsNoTracking().ToList();

۳. استراتژی‌های بارگذاری (Loading Strategies)

EF Core چندین استراتژی برای بارگذاری موجودیت‌های مرتبط دارد که انتخاب درست آنها می‌تواند تأثیر زیادی بر عملکرد داشته باشد.

بارگذاری مشتاقانه (Eager Loading)

در این روش، داده‌های مرتبط با استفاده از متد Include در همان کوئری اول بارگذاری می‌شوند. این روش برای مواقعی مفید است که می‌دانید به داده‌های مرتبط نیاز دارید.

var customers = context.Customers
                       .Include(c => c.Orders)
                       .ToList();

بارگذاری تنبل (Lazy Loading)

در این روش، داده‌های مرتبط تنها در زمانی بارگذاری می‌شوند که به آنها دسترسی پیدا کنید. با این حال، ممکن است منجر به مشکل N+1 شود.

var customer = context.Customers.First();
var orders = customer.Orders;

بارگذاری صریح (Explicit Loading)

این روش زمانی مفید است که می‌خواهید کنترل بیشتری بر زمان بارگذاری داده‌های مرتبط داشته باشید.

var customer = context.Customers.First();
context.Entry(customer).Collection(c => c.Orders).Load();

۴. بروزرسانی داده‌ها (Data Updates)

برای بروزرسانی و حذف دسته‌ای داده‌ها، بهتر است از دستورات مستقیم SQL یا ExecuteUpdateAsync در EF Core 7.0+ استفاده کنید تا عملیات به صورت کارآمدتری انجام شود.

await context.Customers
             .Where(c => c.IsInactive)
             .ExecuteUpdateAsync(c => c.SetProperty(c => c.Status, "Archived"));

۵. استفاده از کوئری‌های کامپایل شده (Compiled Queries)

EF Core اجازه می‌دهد تا طرح اجرای کوئری‌های پر استفاده را ذخیره کنید و از کوئری‌های کامپایل شده برای بهبود عملکرد استفاده کنید.

static readonly Func getCustomerByName = 
    EF.CompileQuery((MyDbContext context, string name) =>
        context.Customers.FirstOrDefault(c => c.Name == name));

۶. استفاده از متدهای غیرهمزمان (Async Methods)

استفاده از متدهای غیرهمزمان باعث بهبود مقیاس‌پذیری می‌شود، به خصوص در محیط‌هایی مانند برنامه‌های وب.

var orders = await context.Orders
                          .Where(o => o.CustomerId == customerId)
                          .ToListAsync();

۷. استفاده از Pagination (صفحه‌بندی) برای محدود کردن داده‌ها

به جای فراخوانی داده‌های بزرگ به صورت یکجا، از روش‌های Skip و Take برای پیاده‌سازی Pagination (صفحه‌بندی) استفاده کنید.

var customers = await context.Customers
                             .OrderBy(c => c.Id)
                             .Skip((pageNumber - 1) * pageSize)
                             .Take(pageSize)
                             .ToListAsync();

۸. جلوگیری از بارگذاری تنبل (Lazy Loading) در برنامه‌های حیاتی

برای جلوگیری از افزایش تعداد کوئری‌ها، در برنامه‌های حیاتی بهتر است از بارگذاری تنبل (Lazy Loading) استفاده نکنید.

۹. کش کردن داده‌های پر استفاده (Caching Frequently Used Data)

برای داده‌هایی که تغییر نمی‌کنند، به جای کوئری مجدد از کش (Cache) استفاده کنید.

if (!_cache.TryGetValue("ProductList", out List products))
{
    products = dbContext.Products.AsNoTracking().ToList();
    _cache.Set("ProductList", products, TimeSpan.FromMinutes(30));
}

۱۰. استفاده از Batching (دسته‌بندی) برای درج/بروزرسانی‌های متعدد

در صورت نیاز به درج یا بروزرسانی چندین موجودیت، از Batching (دسته‌بندی) استفاده کنید.

var newProducts = new List
{
    new Product { Name = "Product1" },
    new Product { Name = "Product2" }
};
dbContext.Products.AddRange(newProducts);
dbContext.SaveChanges();

۱۱. ایندکس‌گذاری ستون‌های پایگاه داده (Indexing Database Columns)

ایندکس‌گذاری ستون‌هایی که بیشتر مورد استفاده قرار می‌گیرند باعث بهبود کارایی کوئری‌ها می‌شود.

modelBuilder.Entity()
    .HasIndex(p => p.Name);

۱۲. اجتناب از تراکنش‌های بزرگ (Avoid Large Transactions)

عملیات‌های سنگین در یک تراکنش بزرگ باعث افت عملکرد می‌شود. به جای آن، عملیات‌ها را به بخش‌های کوچک‌تر تقسیم کنید.

۱۳. بهینه‌سازی مهاجرت‌ها (Migrations Optimization)

مهاجرت‌های پایگاه داده باید با دقت بررسی شوند تا از اعمال تغییرات غیرضروری به جداول بزرگ جلوگیری شود.

dbContext.Database.EnsureCreated();

۱۴. بهینه‌سازی Pooling اتصالات (Connection Pooling Optimization)

بهینه‌سازی Pooling (پولینگ) اتصالات با تنظیم مقادیر Max Pool Size و Min Pool Size در رشته اتصال می‌تواند به بهبود عملکرد کمک کند.

جمع‌بندی

بهینه‌سازی عملکرد EF Core نیاز به ترکیب طراحی هوشمندانه کوئری‌ها و استفاده درست از استراتژی‌های بارگذاری و ردیابی دارد. با پیاده‌سازی روش‌هایی مانند استفاده از کوئری‌های بدون ردیابی (No-Tracking)، بارگذاری مشتاقانه (Eager Loading)، کوئری‌های کامپایل شده (Compiled Queries) و عملیات‌های دسته‌ای (Batching)، می‌توانید از عملکرد بهینه در برنامه‌های خود اطمینان حاصل کنید.

پست های مرتبط

مطالعه این پست ها رو از دست ندین!
Express Js چیست؟

Express Js چیست؟

آنچه در این پست میخوانید مقدمه‌ای بر Express.js ویژگی‌های کلیدی Express.js نصب و راه‌اندازی Express.js ساخت اولین برنامه با Express.js…

بیشتر بخوانید
انگولار Angular چیست؟

انگولار Angular چیست؟

آنچه در این پست میخوانید مقدمه تاریخچه و تکامل انگولار ویژگی‌ها و مزایای انگولار ویژگی‌های کلیدی مزایا معماری انگولار ساختار…

بیشتر بخوانید
Fast API چیست؟

Fast API چیست؟

آنچه در این پست میخوانید مقدمه ویژگی‌های کلیدی FastAPI تکنولوژی‌ها و فریم‌ورک‌های مرتبط با پایتون کاربردهای FastAPI مثال‌های کاربردی از…

بیشتر بخوانید

نظرات

سوالات و نظراتتون رو با ما به اشتراک بذارید

برای ارسال نظر لطفا ابتدا وارد حساب کاربری خود شوید.