c# - Entity Framework Queryable async




1 Answers

يبدو أن المشكلة تكمن في أنك قد أسيء فهمك لطريقة عمل / عدم انتظام العمل مع Entity Framework.

حول كيان الإطار

لذا ، دعنا ننظر إلى هذا الرمز:

public IQueryable<URL> GetAllUrls()
{
    return context.Urls.AsQueryable();
}

ومثال على ذلك الاستخدام:

repo.GetAllUrls().Where(u => <condition>).Take(10).ToList()

ماذا يحدث هناك؟

  1. نحن نحصل على كائن IQueryable (وليس الوصول إلى قاعدة البيانات بعد) باستخدام repo.GetAllUrls()
  2. نقوم بإنشاء كائن .Where(u => <condition> جديد مع حالة محددة باستخدام. .Where(u => <condition>
  3. نقوم بإنشاء كائن IQueryable جديد مع حد ترحيل محدد باستخدام .Take(10)
  4. نقوم باسترجاع النتائج من قاعدة البيانات باستخدام .ToList() . يتم تجميع الكائن IQueryable بنا إلى sql (مثل select top 10 * from Urls where <condition> ). ويمكن لقاعدة البيانات استخدام الفهارس ، خادم SQL يرسل لك 10 أشياء فقط من قاعدة البيانات الخاصة بك (وليس كل مليار url مخزنة في قاعدة البيانات)

حسنًا ، لنلق نظرة على الكود الأول:

public async Task<IQueryable<URL>> GetAllUrlsAsync()
{
    var urls = await context.Urls.ToListAsync();
    return urls.AsQueryable();
}

مع نفس مثال الاستخدام الذي حصلنا عليه:

  1. نحن نقوم بتحميل كل المليرات من عناوين url المخزنة في قاعدة البيانات الخاصة بك باستخدام await context.Urls.ToListAsync(); .
  2. لدينا تجاوز الذاكرة. الطريقة الصحيحة لقتل الخادم الخاص بك

حول async / في انتظار

لماذا يفضل استخدام async / في انتظار؟ دعونا ننظر في هذا الرمز:

var stuff1 = repo.GetStuff1ForUser(userId);
var stuff2 = repo.GetStuff2ForUser(userId);
return View(new Model(stuff1, stuff2));

ماذا يحدث هنا؟

  1. يبدأ من السطر 1 var stuff1 = ...
  2. نرسل طلب إلى خادم SQL أننا لا نريد الحصول على بعض stuff1 لـ userId
  3. نحن ننتظر (يتم حظر موضوع الحالي)
  4. نحن ننتظر (يتم حظر موضوع الحالي)
  5. ...
  6. خادم Sql ترسل لنا الرد
  7. ننتقل إلى السطر 2 var stuff2 = ...
  8. نرسل طلب إلى خادم SQL أننا لا نريد الحصول على بعض stuff2 لـ userId
  9. نحن ننتظر (يتم حظر موضوع الحالي)
  10. ومره اخرى
  11. ...
  12. خادم Sql ترسل لنا الرد
  13. نحن نقدم وجهة النظر

لذلك دعونا ننظر إلى إصدار متزامن من ذلك:

var stuff1Task = repo.GetStuff1ForUserAsync(userId);
var stuff2Task = repo.GetStuff2ForUserAsync(userId);
await Task.WhenAll(stuff1Task, stuff2Task);
return View(new Model(stuff1Task.Result, stuff2Task.Result));

ماذا يحدث هنا؟

  1. نرسل طلب خادم SQL للحصول على stuff1 (السطر 1)
  2. نرسل طلب خادم SQL للحصول على stuff2 (السطر 2)
  3. ننتظر الردود من خادم SQL ، ولكن لا يتم حظر الصفحات الحالية ، يمكنه التعامل مع طلبات البحث من مستخدمين آخرين
  4. نحن نقدم وجهة النظر

الطريق الصحيح للقيام بذلك

رمز جيد هنا:

using System.Data.Entity;

public IQueryable<URL> GetAllUrls()
{
   return context.Urls.AsQueryable();
}

public async Task<List<URL>> GetAllUrlsByUser(int userId) {
   return await GetAllUrls().Where(u => u.User.Id == userId).ToListAsync();
}

ملاحظة ، يجب إضافة using System.Data.Entity أجل استخدام الأسلوب ToListAsync() لـ ToListAsync() .

لاحظ أنه إذا لم تكن بحاجة إلى التصفية والترقيم والأشياء ، فلن تحتاج إلى العمل باستخدام IQueryable . يمكنك فقط استخدام await context.Urls.ToListAsync() والعمل مع List<Url> .

أنا أعمل على بعض بعض تطبيقات Web API باستخدام Entity Framework 6 وأحد أساليب التحكم الخاصة بي هو "Get All" التي تتوقع أن تتلقى محتويات جدول من قاعدة البيانات الخاصة بي كـ IQueryable<Entity> . في مستودع بلدي أتساءل عما إذا كان هناك أي سبب مفيد للقيام بذلك بشكل غير متزامن كما أنا جديد لاستخدام EF مع المتزامن.

أساسا يتلخص ل

 public async Task<IQueryable<URL>> GetAllUrlsAsync()
 {
    var urls = await context.Urls.ToListAsync();
    return urls.AsQueryable();
 }

ضد

 public IQueryable<URL> GetAllUrls()
 {
    return context.Urls.AsQueryable();
 }

هل سيحقق الإصدار المتزامن فعليًا فوائد أداء هنا أو هل أتحمل نفقات غير ضرورية عن طريق عرض قائمة لأول مرة (باستخدام عقل التزامن) ثم الذهاب إلى IQueryable؟




Related

c# entity-framework async-await