در مقدمه به معرفی کتابخانه ی Entity Framework Extended و نحوه ی اضافه کردن آن به پروژه و در بخش اول به نحوه اعمال حذف و ویرایش دسته ای با امکانات این کتابخانه پرداختیم. در این مقاله به یکی از کاربردی ترین امکانات این کتابخانه با نام Future Query می پردازیم.
با استفاده از Future Query ها می توانید لیستی از Query ها را ساخته و تمام آنها را با یک مراجعه (Round Trip) به پایگاه داده اجرا و بازیابی کنید. با استفاده از این تکنیک می توانید تعداد دفعات مراجعه به پایگاه داده را کمتر کرده و بازدهی برنامه ی خود را افزایش دهید. استفاده از Future Query ها بسیار راحت و ساده است، تنها کافی است از متد ()Future در آخر Query های خود استفاده کنید تا لیستی از Query ها بسازید. این Query ها همراه با اولین Query به پایگاه داده فرستاده و اجرا می شود.
- استفاده از Future برای ارسال و بازیابی چند Query همزمان
فرض کنید برای انجام عملیاتی در سیستم نیاز به بازیابی اطلاعات از دو جدول Member و Course را دارید. در حالت معمول باید از دو Query جدا مانند زیر استفاده نمایید :
1 2 3 4 5 6 7 |
using (var dbContext = new MembershipDbContex()) { var activeMembers = dbContext.Members.Where(a => a.IsActive == true).ToList(); var availableCourses = dbContext.Courses.Where(a => a.Capacity > 0).ToList(); } |
دو Query فوق با دوبار مراجعه به پایگاه داده اجرا و بازیابی می شوند. حال از متد Future برای بازیابی داده ها استفاده میکنیم :
1 2 3 4 5 6 |
using (var dbContext = new MembershipDbContext()) { var activeMembers = dbContext.Members.Where(a => a.IsActive == true).Future(); var availableCourses = dbContext.Courses.Where(a => a.Capacity > 0).Future().ToList(); } |
به Query ارسال شده به پایگاه داده توجه نمایید :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
-- Query #1 SELECT [Extent1].[Id] AS [Id], [Extent1].[Firstname] AS [Firstname], [Extent1].[Lastname] AS [Lastname], [Extent1].[IsActive] AS [IsActive] FROM [dbo].[Member] AS [Extent1] WHERE 1 = [Extent1].[IsActive]; -- Query #2 SELECT [Extent1].[Id] AS [Id], [Extent1].[Name] AS [Name], [Extent1].[Capacity] AS [Capacity], [Extent1].[StartDateTime] AS [StartDateTime], [Extent1].[EndDateTime] AS [EndDateTime] FROM [dbo].[Course] AS [Extent1] WHERE [Extent1].[Capacity] > 0; |
همانطور که مشاهده میکنید با اولین دسترسی یکی از Future Query ها به پایگاه داده (صدا زدن ToList در دومین Query)، هر دو Query همزمان به پایگاه داده ارسال و بازیابی شدند.
- استفاده از Future Count هنگام پیاده سازی Paging در صفحات
تقریبا در تمام سناریوهای Paging نیاز به داشتن تعداد واقعی رکوردهای یک Query دارید. در حالت معمول باید از چنین روشی استفاده نمایید :
1 2 3 4 5 6 7 |
var activeMemberQuery = dbContext.Members.Where(a => a.IsActive == true); var activeMemberCount = activeMemberQuery.Count(); var activeMembers = activeMemberQuery.OrderBy(a=>a.Id) .Skip( (currentPage -1) * pageSize) .Take(pageSize) .ToList(); |
در مثال فوق نیز دو Query به صورت جداگانه اجرا می شوند. یکی جهت بازیابی داده ها برای یک صفحه و دیگری برای بازیابی تعداد مجموع رکورد ها. میتوانید با استفاده از متد FutureCount با یک مراجعه به پایگاه داده، هر دوی آنها را اجرا نمایید :
1 2 3 4 5 6 7 8 |
var activeMemberQuery = dbContext.Members.Where(a => a.IsActive == true); var activeMemberCount = activeMemberQuery.FutureCount(); var activeMembers = activeMemberQuery.OrderBy(a=>a.Id) .Skip( (currentPage -1) * pageSize) .Take(pageSize) .Future() .ToList(); |
همانطور که مشاهده میکنید Query های فوق نیز به صورت همزمان اجرا می شوند :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
-- Query #1 SELECT [GroupBy1].[A1] AS [C1] FROM ( SELECT COUNT(1) AS [A1] FROM [dbo].[Member] AS [Extent1] WHERE 1 = [Extent1].[IsActive] ) AS [GroupBy1]; -- Query #2 SELECT TOP (10) [Filter1].[Id] AS [Id], [Filter1].[Firstname] AS [Firstname], [Filter1].[Lastname] AS [Lastname], [Filter1].[IsActive] AS [IsActive] FROM ( SELECT [Extent1].[Id] AS [Id], [Extent1].[Firstname] AS [Firstname], [Extent1].[Lastname] AS [Lastname], [Extent1].[IsActive] AS [IsActive], row_number() OVER (ORDER BY [Extent1].[Id] ASC) AS [row_number] FROM [dbo].[Member] AS [Extent1] WHERE 1 = [Extent1].[IsActive] ) AS [Filter1] WHERE [Filter1].[row_number] > 0 ORDER BY [Filter1].[Id] ASC; |
در این مقاله Future Query ها و نحوه ی استفاده از آنها جهت افزایش بازدهی و سرعت نرم افزار را بررسی کردیم. در مقاله ی بعد نحوه ی پیاده سازی مکانیزم Caching برای نتایج Query ها را با استفاده از امکانات کتابخانه ی Entity Framework Extended بررسی خواهیم کرد.
خیلی مفید بود مهندس
خواهش میکنم
اقای مهندس سایتتون واقعا عالیه
خواهش میکنم، لطف داری امیر جان
سلامئ
خیلی قابلیت خوب و جذابیه
مهندس خوب بود که به Fixup هم اشاره می کردی
کوئری هایی که به این ترتیب زده می شوند در نهایت چون داخل یک کانتکس هستند روابطشون با هم بر قرار می شه ! و تو کد می شه از روابط بین Entity هایی در کوئری های جداگانه اجرا شدند ،استفاده کرد و لذت برد !
بله درست میفرمایید
در این حالت هم مثل Query های معمولی، رابطه های بین Entity ها FixUp میشن.
ممنونم
ممنون از معرفی این کتابخانه ، فرض کنید این کتابخانه در دسترس نیست.
نظرتون در مورد باز کردن کانکشن و فراخوانی کوئری ها و بستن کانکشن چیه؟
سلام
روشی که میفرمایید بهتر از باز و بسته کردن کانکشن به ازای هر Query است و هزینه ی کمتری دارد.
اما همچنان نیاز دارید که به ازای هر Query، یک Round Trip به دیتابیس داشته باشید.
ممنون.
جالب است که بنده در EF 6 با هاش مشکلی نداشتم ولی جدیدا با EF 6.1.3 کوئری های همزمان ، نتیجه ای رو برنمیگردانند. البته در بازخورد های موجود در مخزن پروژه نیز مطرح شده است.
مشکل حل شد . قبلا به شکل زیر استفاده میکردم.
// base query
var q = db.Tasks.Where(t => t.Priority == 2);
// get total count
var q1 = q.FutureCount();
// get page
var q2 = q.Skip(pageIndex).Take(pageSize).Future();
// triggers execute as a batch
int total = q1.Value;
var tasks = q2.ToList();
این چیزی بود که در داکیونت خودش هم ارائه شده. و بنده هم استفاده کرده بودم و نتیجه گرفته بودم .
با حذف فراخوانی پراپرتی Value این مشکل حل شد!!
عالی بود
ممنون