مدل مورد بررسی
public class User { public int Id { get; set; } public string Name { get; set; } public virtual ICollection<BlogPost> BlogPosts { get; set; } } public class BlogPost { public int Id { get; set; } public string Title { get; set; } public string Content { get; set; } [ForeignKey("UserId")] public virtual User User { get; set; } public int UserId { get; set; } }
مشکل 1: بارگذاری تعداد زیادی ردیف
var data = context.BlogPosts.ToList();
راه حل: با استفاده از Skip و Take، مباحث صفحهی بندی را اعمال کنید.
مشکل 2: بازگرداندن تعداد زیادی ستون
var data = context.BlogPosts.ToList();
راه حل: اگر تنها نیاز به خاصیت Content است، از Select و سپس ToList استفاده کنید؛ البته به همراه نکته 1.
var list = context.BlogPosts.Select(x => x.Content).Skip(15).Take(15).ToList();
مشکل 3: گزارشگیریهایی که بیشباهت به حملهی به دیتابیس نیستند
foreach (var post in context.BlogPosts) { Console.WriteLine(post.User.Name); }
این مورد به lazy loading مشهور است و در مواردی که قرار است با یک مطلب و یک نویسنده کار شود، شاید اهمیتی نداشته باشد. اما در حین نمایش لیستی از اطلاعات، بیشباهت به یک حملهی شدید به بانک اطلاعاتی نیست.
راه حل: در گزارشگیریها اگر نیاز به نمایش اطلاعات روابط یک موجودیت وجود دارد، از متد Include استفاده کنید تا Lazy loading لغو شود.
foreach (var post in context.BlogPosts.Include(x=>x.User))
مشکل 4: فعال بودن بیجهت مباحث ردیابی اطلاعات
var data = context.BlogPosts.ToList();
راه حل: در گزاشگیریها، dynamic proxies را توسط متد AsNoTracking غیرفعال کنید:
var data = context.BlogPosts.AsNoTracking().Skip(15).Take(15).ToList();
مشکل 5: باز کردن تعداد اتصالات زیاد به بانک اطلاعاتی در طول یک درخواست
هر Context دارای اتصال منحصربفرد خود به بانک اطلاعاتی است. اگر در طول یک درخواست، بیش از یک Context مورد استفاده قرار گیرد، بدیهی است به همین تعداد اتصال باز شده به بانک اطلاعاتی، خواهیم داشت. نتیجهی آن فشار بیشتر بر بانک اطلاعاتی و همچنین کاهش سرعت برنامه است؛ از این لحاظ که اتصالات TCP برقرار شده، هزینهی بالایی را به همراه دارند.
روش تشخیص:
private void problem5MoreThan1ConnectionPerRequest() { using (var context = new MyContext()) { var count = context.BlogPosts.ToList(); } }
راه حل: برای حل این مساله باید از روشهای تزریق وابستگیها استفاده کرد. یک Context وهله سازی شدهی در طول عمر یک درخواست، باید بین وهلههای مختلف اشیایی که نیاز به Context دارند، زنده نگه داشته شده و به اشتراک گذاشته شود.
مشکل 6: فرق است بین IList و IEnumerable
DataContext = from user in context.Users where user.Id>10 select user;
زمانیکه در حال تهیهی گزارشی هستید، ابزارهای گزارشگیر ممکن است چندین بار از نتیجهی کوئری شما در حین تهیهی گزارش استفاده کنند. بنابراین برخلاف تصور، data binding انجام شده، تنها یکبار سبب اجرای این کوئری نمیشود؛ بسته به ساز و کار درونی گزارشگیر، چندین بار ممکن است این کوئری فراخوانی شود.
راه حل: یک ToList را به انتهای این کوئری اضافه کنید. به این ترتیب از نتیجهی کوئری، بجای اصل کوئری استفاده خواهد شد و در این حالت تنها یکبار رفت و برگشت به بانک اطلاعاتی را شاهد خواهید بود.
مشکل 7: فرق است بین IQueryable و IEnumerable
خروجی IEnumerable، یعنی این عبارت را محاسبه کن. خروجی IQueryable یعنی این عبارت را درنظر داشته باش. اگر نیاز است نتایج کوئریها با هم ترکیب شوند، مثلا بر اساس رابط کاربری برنامه، کاربر بتواند شرطهای مختلف را با هم ترکیب کند، باید از ترکیب IQueryableها استفاده کرد تا سبب رفت و برگشت اضافی به بانک اطلاعاتی نشویم.
مشکل 8: استفاده از کوئریهای Like دار
var list = context.BlogPosts.Where(x => x.Content.Contains("test"))
مشکل 9: استفاده از Count بجای Any
اگر نیاز است بررسی کنید مجموعهای دارای مقداری است یا خیر، از Count>0 استفاده نکنید. کارآیی Any و کوئری SQL ایی که تولید میکند، به مراتب بیشتر و بهینهتر است از Count>0.
مشکل 10: سرعت insert پایین است
ردیابی تغییرات را خاموش کرده و از متد جدید AddRange استفاده کنید. همچنین افزونههایی برای Bulk insert نیز موجود هستند.
مشکل 11: شروع برنامه کند است
میتوان تمام مباحث نگاشتهای پویای کلاسهای برنامه به جداول و روابط بانک اطلاعاتی را به صورت کامپایل شده در برنامه ذخیره کرد. این مورد سبب بالا رفتن سرعت شروع برنامه خصوصا در حالتیکه تعداد جداول بالا است میشود.