2015-11-03 16 views
10

मैं निम्न त्रुटि जब IEnumerable.SelectMany के भीतर एक async लैम्ब्डा उपयोग करने की कोशिश हो रही है:SelectMany के साथ async lambda का उपयोग कैसे करें?

var result = myEnumerable.SelectMany(async (c) => await Functions.GetDataAsync(c.Id)); 

विधि के लिए प्रकार तर्क 'IEnumerable System.Linq.Enumerable.SelectMany (इस IEnumerable, समारोह>) ' उपयोग से अनुमानित नहीं हो सकता है।

public interface IFunctions { 
    Task<IEnumerable<DataItem>> GetDataAsync(string itemId); 
} 

public class Functions : IFunctions { 
    public async Task<IEnumerable<DataItem>> GetDataAsync(string itemId) { 
     // return await httpCall(); 
    } 
} 

मुझे लगता है कि क्योंकि मेरी GetDataAsync विधि वास्तव में एक Task<IEnumerable<T>> रिटर्न: जहां GetDataAsync के रूप में परिभाषित किया गया है स्पष्ट रूप से

प्रकार तर्क निर्दिष्ट करने का प्रयास। लेकिन Select काम क्यों करता है, निश्चित रूप से इसे एक ही त्रुटि फेंकनी चाहिए?

var result = myEnumerable.Select(async (c) => await Functions.GetDataAsync(c.Id)); 

क्या इसके आसपास कोई रास्ता है?

+1

आप 'Functions.GetDataAsync' के लिए घोषणा प्रदान कर सकते हैं? – Grundy

+0

@ ग्रुंडी 'कार्य >', लेकिन मैंने प्रश्न में पूरी घोषणा को जोड़ा है। जहां 'टी'' myEnumerable' – CodingIntrigue

+1

@HimBromBeere के प्रकार से भिन्न है, संग्रह_ के वापसी _collection का चयन करें, लेकिन मुझे लगता है कि ओपी को सरल संग्रह – Grundy

उत्तर

10

async lambda अभिव्यक्ति को सरल Func<TSource, TResult> में परिवर्तित नहीं किया जा सकता है।

तो, कई का चयन नहीं किया जा सकता है। आप सिंक्रनाइज़ संदर्भ में चला सकते हैं:

myEnumerable.Select(c => Functions.GetDataAsync(c.Id)).SelectMany(task => task.Result); 

या

List<DataItem> result = new List<DataItem>(); 

foreach (var ele in myEnumerable) 
{ 
    result.AddRange(await Functions.GetDataAsyncDo(ele.Id)); 
} 

आप कर सकते हैं नहीं न उपयोग yield return - यह डिजाइन कर रहा है।

public static async Task<IEnumerable<T1>> SelectManyAsync<T, T1>(this IEnumerable<T> enumeration, Func<T, Task<IEnumerable<T1>>> func) 
{ 
    return (await Task.WhenAll(enumeration.Select(func))).SelectMany(s => s); 
} 

आप को चलाने के लिए अनुमति देता है कि:

var result = await myEnumerable.SelectManyAsync(c => Functions.GetDataAsync(c.Id)); 

स्पष्टीकरण: प्रत्येक रिटर्न Task<IEnumerable<T>> आप कार्यों की एक सूची है, f.e .:

public async Task<IEnuemrable<DataItem>> Do() 
{ 
    ... 
    foreach (var ele in await Functions.GetDataAsyncDo(ele.Id)) 
    { 
     yield return ele; // compile time error, async method 
          // cannot be used with yield return 
    } 

} 
+0

बहुत बहुत धन्यवाद, मैंने कॉल सिंक्रोनस बनाने के लिए 'await' के बजाय' .Result' का उपयोग किया और सभी अच्छी तरह से – CodingIntrigue

+0

डाउनवोट पर कोई टिप्पणी है? – pwas

+1

डाउनवोट संभवत: 'Reses' के उपयोग के कारण था जो [deadlocks का कारण बन सकता है] (https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html)। –

14

यह एक विस्तार है। तो आपको उन सभी को आग लगाना होगा, फिर सभी का इंतजार करें, और फिर SelectMany के माध्यम से परिणाम को स्क्वैश करें।

+0

इसे आपके कोड में इनलाइन करने की बजाय किसी विधि को निकालने का एक लाभ यह है कि एक्सटेंशन 'कार्य ' देता है, इसलिए कोड में बाद में यह प्रतीक्षा की जा सकती है। – Tim

+0

ध्यान रखें कि यह ओपी के 'GetDataAsync (...)' के सभी कॉल समानांतर करता है। आमतौर पर यह एक अच्छी बात है, लेकिन कुछ परिदृश्यों में वांछित नहीं हो सकता है। –

2

Select काम करता है क्योंकि यह IEnumerable<Task<T>> लौटाएगा, जिसे बाद में प्रतीक्षा किया जा सकता है। Task.WhenAll

तो, यह समस्या के लिए एक आसान समाधान नहीं है:

IEnumerable<Task<IEnumerable<T>>> tasks = source.Select(GetNestedEnumerableTask); 
IEnumerable<T>[] nestedResults = await Task.WhenAll(tasks); 
IEnumerable<T> results = nestedResults.SelectMany(nr => nr); 
संबंधित मुद्दे