2016-09-15 19 views
5

का चयन करें मैं Asp.Net कोर पहचान का उपयोग कर रहा हूं और कुछ कोड को सरल बनाने की कोशिश कर रहा हूं जो उपयोगकर्ताओं की सूची और उनकी भूमिका को व्यूमोडेल में प्रोजेक्ट करता है। यह कोड काम करता है, लेकिन इसे सरल बनाने की कोशिश में मैं त्रुटियों और जिज्ञासा की पागल सर्पिल में गया हूं।async/await का उपयोग करना। Lambda

यहाँ मेरे कार्य कोड है:

 var allUsers = _userManager.Users.OrderBy(x => x.FirstName); 
     var usersViewModel = new List<UsersViewModel>(); 

     foreach (var user in allUsers) 
     { 
      var tempVm = new UsersViewModel() 
      { 
       Id = user.Id, 
       UserName = user.UserName, 
       FirstName = user.FirstName, 
       LastName = user.LastName, 
       DisplayName = user.DisplayName, 
       Email = user.Email, 
       Enabled = user.Enabled, 
       Roles = String.Join(", ", await _userManager.GetRolesAsync(user)) 
      }; 

      usersViewModel.Add(tempVm); 
     } 

कोड को आसान बनाने की कोशिश कर रहा में, मैं समझ मैं इस (टूटी हुई कोड) की तरह कुछ कर सकता है:

 var usersViewModel = allUsers.Select(user => new UsersViewModel 
     { 
      Id = user.Id, 
      UserName = user.UserName, 
      FirstName = user.FirstName, 
      LastName = user.LastName, 
      DisplayName = user.DisplayName, 
      Email = user.Email, 
      Enabled = user.Enabled, 
      Roles = string.Join(", ", await _userManager.GetRolesAsync(user)) 
     }).ToList(); 

यह ब्रेक क्योंकि मैं उपयोगकर्ता से पहले लैम्ब्डा अभिव्यक्ति में async कीवर्ड का उपयोग नहीं कर रहा हूं। हालांकि, जब मैं उपयोगकर्ता से पहले async जोड़ने करते हैं, मैं अभी तक एक और त्रुटि का कहना है कि "Async लैम्ब्डा भाव अभिव्यक्ति के पेड़ में परिवर्तित नहीं किया जा सकता है"

मेरा अनुमान है कि GetRolesAsync() प्रणाली टास्क और लौट जाता है मिल उस कार्य के वास्तविक परिणामों की बजाय भूमिकाओं को आवंटित करना। मेरे जीवन के लिए मुझे क्या लगता है कि यह काम नहीं कर रहा है।

मैंने शोध किया है और पिछले दिन कई किस्मत के साथ कई तरीकों का प्रयास किया है।

Is it possible to call an awaitable method in a non async method?

https://blogs.msdn.microsoft.com/pfxteam/2012/04/12/asyncawait-faq/

Calling async method in IEnumerable.Select

How to await a list of tasks asynchronously using LINQ?

how to user async/await inside a lambda

How to use async within a lambda which returns a collection

: यहाँ कुछ है कि मैं को देखा हैं

मान्य है, मैं पूरी तरह से समझ नहीं पा रहा हूं कि कैसे async/काम का इंतजार है, तो शायद यह समस्या का हिस्सा है। मेरा foreach कोड काम करता है, लेकिन मैं समझने में सक्षम होना चाहता हूं कि जिस तरह से मैं कोशिश कर रहा हूं उसे कैसे काम करना है। चूंकि मैंने इस पर इतना समय बिताया है, मुझे लगा कि यह एक अच्छा पहला सवाल होगा।

धन्यवाद!

संपादित

मैं मैं क्या मैं लेख मैं एक नकली प्रश्न के रूप में चिह्नित नहीं किया जा करने के लिए इस क्रम में शोध के प्रत्येक मामले में किया था समझाना पड़ेगा लगता है - और मुझे लगता है कि से बचने के लिए वास्तव में कड़ी मेहनत करने की कोशिश की: - /। जबकि सवाल समान लगता है, परिणाम नहीं हैं।

public async Task<ActionResult> Users() 
    { 
     var allUsers = _userManager.Users.OrderBy(x => x.FirstName); 
     var tasks = allUsers.Select(GetUserViewModelAsync).ToList(); 
     return View(await Task.WhenAll(tasks)); 
    } 

    public async Task<UsersViewModel> GetUserViewModelAsync(ApplicationUser user) 
    { 
     return new UsersViewModel 
     { 
      Id = user.Id, 
      UserName = user.UserName, 
      FirstName = user.FirstName, 
      LastName = user.LastName, 
      DisplayName = user.DisplayName, 
      Email = user.Email, 
      Enabled = user.Enabled, 
      Roles = String.Join(", ", await _userManager.GetRolesAsync(user)) 
     }; 
    } 

मैं भी करने की कोशिश की तो जैसे AsEnumerable का उपयोग कर: लेख है कि एक जवाब के रूप में चिह्नित किया गया था के मामले में मैं निम्नलिखित कोड की कोशिश की

var usersViewModel = allUsers.AsEnumerable().Select(async user => new UsersViewModel 
     { 
      Id = user.Id, 
      UserName = user.UserName, 
      FirstName = user.FirstName, 
      LastName = user.LastName, 
      DisplayName = user.DisplayName, 
      Email = user.Email, 
      Enabled = user.Enabled, 
      Roles = string.Join(", ", await _userManager.GetRolesAsync(user)) 
     }).ToList(); 

इन दोनों त्रुटि संदेश का उत्पादन: " अमान्य ऑपरेशन अपवाद: पिछले ऑपरेशन पूर्ण होने से पहले इस संदर्भ पर एक दूसरा ऑपरेशन शुरू हुआ।किसी भी इंस्टेंस सदस्यों को थ्रेड सुरक्षित होने की गारंटी नहीं है। "

इस बिंदु पर ऐसा लगता है कि मेरा मूल ForEach सबसे अच्छा शर्त हो सकता है, लेकिन मैं अभी भी सोच रहा हूं कि ऐसा करने का सही तरीका क्या होगा थे async तरीकों का उपयोग कर यह करने के लिए

संपादित करें 2 - उत्तर के साथ त्सेंग की टिप्पणी के लिए धन्यवाद (और कुछ अन्य अनुसंधान) मैं निम्नलिखित कोड का उपयोग कर काम बातें कर रहा था:।

 var userViewModels = allUsers.Result.Select(async user => new UsersViewModel 
     { 
      Id = user.Id, 
      UserName = user.UserName, 
      FirstName = user.FirstName, 
      LastName = user.LastName, 
      DisplayName = user.DisplayName, 
      Email = user.Email, 
      Enabled = user.Enabled, 
      Roles = string.Join(", ", await _userManager.GetRolesAsync(user)) 
     }); 
     var vms = await Task.WhenAll(userViewModels); 
     return View(vms.ToList()); 

हालांकि अब मैंने हर किसी को लिया है ध्यान में रखते हुए, मैंने एसक्यूएल प्रोफाइलर के करीब देखना शुरू कर दिया कि यह देखने के लिए कि डीबी वास्तव में कितनी हिट कर रही है - मैट जॉनसन ने उल्लेख किया है, यह बहुत कुछ है (एन + 1)।

तो जब यह मेरे प्रश्न का उत्तर देता है, तो अब मैं क्वेरी को चलाने के तरीके पर पुनर्विचार कर रहा हूं और मुख्य दृश्य में भूमिकाओं को छोड़ सकता हूं और केवल उन्हें प्रत्येक उपयोगकर्ता के रूप में चुन सकता हूं। हालांकि मैंने निश्चित रूप से इस सवाल के माध्यम से बहुत कुछ सीखा है (और जो कुछ मुझे नहीं पता है उससे ज्यादा सीखा है), इसलिए सभी को धन्यवाद।

+0

'मेरा अनुमान है कि GetRolesAsync() विधि कार्य को वापस कर रही है और उस कार्य के वास्तविक परिणामों की बजाय भूमिकाओं को असाइन कर रही है।'- शायद नहीं, क्योंकि 'प्रतीक्षा' कार्य के परिणाम को पकड़ लेगा। – mason

+2

'चयन करें' से पहले 'AsEnumerable' डालने का प्रयास करें ताकि यह EF के लिए अभिव्यक्ति वृक्ष में परिवर्तित करने की कोशिश करने के बजाय LIVq में ऑब्जेक्ट्स में चलाया जा सके या जो भी प्रदाता आप उपयोग करते हैं। – juharr

+0

वर usersViewModels = (इंतजार Task.WhenAll (allUsers.AsEnumerable()। का चयन करें (async उपयोगकर्ता => नई UsersViewModel { आईडी = user.Id, उपयोगकर्ता नाम = user.UserName, प्रथम = user.FirstName, अंतिम नाम = user.LastName, DisplayName = user.DisplayName, ईमेल = user.Email, सक्षम = user.Enabled, भूमिकाओं = string.Join (",", का इंतजार _userManager.GetRolesAsync (उपयोगकर्ता)) })))। सूची बनाने के लिए(); –

उत्तर

7

मुझे लगता है कि आप यहां दो चीजें मिश्रित कर रहे हैं। अभिव्यक्ति पेड़ और प्रतिनिधियों। लैम्ब्डा का उपयोग दोनों को व्यक्त करने के लिए किया जा सकता है, लेकिन यह उस पैरामीटर के प्रकार पर निर्भर करता है जिस विधि को स्वीकार किया जाता है जिसमें इसे चालू किया जाएगा।

एक लैम्ब्डा एक विधि से गुजरता है जो Action<T> या Func<T, TResult> को एक प्रतिनिधि (मूल रूप से एक अज्ञात फ़ंक्शन/विधि) में परिवर्तित कर दिया जाएगा।

जब आप Expression<T> को स्वीकार करने वाली विधि में लैम्ब्डा अभिव्यक्ति को पास करते हैं, तो आप लैम्ब्डा से एक अभिव्यक्ति वृक्ष बनाते हैं। अभिव्यक्ति पेड़ सिर्फ कोड हैं जो कोड का वर्णन करते हैं, लेकिन कोड स्वयं नहीं हैं।

कहा जा रहा है कि एक अभिव्यक्ति वृक्ष निष्पादित नहीं किया जा सकता क्योंकि यह निष्पादन योग्य कोड में परिवर्तित हो गया है। आप रनटाइम पर एक अभिव्यक्ति वृक्ष संकलित कर सकते हैं और फिर इसे एक प्रतिनिधि की तरह निष्पादित कर सकते हैं।

ओआरएम फ्रेमवर्क आपको "कोड" लिखने की अनुमति देने के लिए अभिव्यक्ति पेड़ का उपयोग करते हैं, जिसका अनुवाद कुछ अलग (उदाहरण के लिए डेटाबेस क्वेरी) में किया जा सकता है या रनटाइम पर गतिशील रूप से कोड उत्पन्न करने के लिए किया जा सकता है।

इसी कारण से आप Expression<T> स्वीकार करने वाले तरीकों में async का उपयोग नहीं कर सकते हैं। जब आप इसे AsEnumerable() में परिवर्तित करते हैं तो यह क्यों काम कर सकता है क्योंकि यह IEnumerable<T> देता है और उस पर LINQ विधियां Func<T, TResult> स्वीकार करती हैं। लेकिन यह अनिवार्य रूप से पूरी क्वेरी प्राप्त करता है और पूरी सामग्री को स्मृति में करता है, इसलिए आप अनुमानों का उपयोग नहीं कर सकते हैं (या आपको केवल अभिव्यक्तियों और अनुमानों का उपयोग करने से पहले डेटा प्राप्त करना होगा), फ़िल्टर किए गए परिणाम को सूची में बदलें और फिर इसे फ़िल्टर करें।

आप कुछ इस तरह की कोशिश कर सकते:

// Filter, sort, project it into the view model type and get the data as a list 
var users = await allUsers.OrderBy(user => user.FirstName) 
          .Select(user => new UsersViewModel 
    { 
     Id = user.Id, 
     UserName = user.UserName, 
     FirstName = user.FirstName, 
     LastName = user.LastName, 
     DisplayName = user.DisplayName, 
     Email = user.Email, 
     Enabled = user.Enabled 
    }).ToListAsync(); 

// now that we have the data, we iterate though it and 
// fetch the roles 
var userViewModels = users.Select(async user => 
{ 
    user.Roles = string.Join(", ", await _userManager.GetRolesAsync(user)) 
}); 

पहले भाग डेटाबेस पर पूरी तरह से किया जाएगा और आप अपने सभी फायदे रखने (यानी आदेश डेटाबेस पर होता है, तो आप ऐसा करने के लिए नहीं है परिणामों को लाने के बाद इन-मेमोरी सॉर्टिंग और सीमा कॉल डीबी आदि से प्राप्त डेटा को सीमित करता है)।

दूसरा भाग स्मृति में परिणाम के माध्यम से पुनरावृत्त करता है और प्रत्येक अस्थायी मॉडल के लिए डेटा प्राप्त करता है और अंत में इसे दृश्य मॉडल में मानचित्रित करता है।

संबंधित मुद्दे