2017-05-04 7 views
0

Je suit la fonction d'extension:Comment pouvons-nous retourner une collection vide en utilisant le rendement?

public static IEnumerable<T> Select<T>(this IDataReader reader, 
           Func<IDataReader, T> selector) 
{ 
    while (reader.Read()) 
    { 
     yield return selector(reader); 
    } 
} 

qui est utilisé comme:

var readFields = dsReader.Select(r => 
{ 
    var serviceResponse = myService.Decrypt<DateTime>(r.GetString(DATE_VALUE), r.GetInt32(DEK_ID)); 

    if (serviceResponse.IsSuccessful) 
    { 
     return new DataField<DateFieldValue> 
     { 
      FieldValue = new DateFieldValue { Data = serviceResponse.Value } 
     }; 
    } 
    return null; 
}); 

if (!readFields.IsCollectionNullOrEmpty()) 
         returnFinalFields.AddRange(readFields); 

Le problème que je suis confronté ici est que même si serviceResponse.IsSuccessful est fausse la readFields n'est pas vide, il contient un énumérable avec un élément null. Existe-t-il un moyen de renvoyer une collection vide ici?

+3

'Enumerable.Empty ();' –

+0

Y at-il un moyen de vérifier avant la reader.Read() pour voir s'il y a des données, et renvoient null avant d'entrer dans le temps? – Neil

+2

@Neil renvoyant une valeur nulle d'un 'IEnumerable 'attendu est presque toujours un anti-pattern. –

Répondre

0

utilisation des Select intéressants (mal?). Votre problème est que vous renvoyez une valeur nulle du délégué Select lorsque IsSuccessful est false. Depuis ne pas retourner une valeur de délégué du Select est pas une option, le filtre après:

var readFields = dsReader.Select(r => { 
    var serviceResponse = myService.Decrypt<DateTime>(r.GetString(DATE_VALUE), r.GetInt32(DEK_ID)); 

    if (serviceResponse.IsSuccessful) 
     return new DataField<DateFieldValue> { 
      FieldValue = new DateFieldValue { Data = serviceResponse.Value } 
     }; 
    else 
     return null; 
}).Where(df => df != null); 
1

La méthode Select permet de vérifier le résultat renvoyé et de ne donner sa valeur que lorsqu'elle est valide. Par exemple ne pas null:

public static IEnumerable<T> Select<T>(this IDataReader reader, Func<IDataReader, T> selector) 
    where T:class 
{ 
    while (reader.Read()) 
    { 
     var res = selector(reader); 
     if(res!=null) 
      yield return res; 
    } 
} 

Bien que déclaré par Servy, qui normalement ne pas appartenir à un Select régulier. La méthode pourrait s'appeler quelque chose comme SelectValidValues pour éviter toute confusion.

Une autre façon serait que le paramètre lambda renvoie un Tuple contenant à la fois le résultat et s'il est valide. Une autre façon est d'avoir un paramètre optionnel (comme une valeur ou une fonction sous-jacente supplémentaire) qui vérifie quelles valeurs sont valables

+1

Ensuite, il ne sélectionne plus. Cela n'appartient pas à une méthode 'Select'. – Servy

+1

@Servy en effet, mais peut-être OP ont une idée différente comment 'Select' devrait se comporter? Renommer quelque chose 'SelectNotEmpty' serait bien pour le code public. –

+0

@AlexeiLevenkov C'est juste créer un nouveau problème. Il y a une raison pour laquelle LINQ n'a pas de méthode 'SelectNotEmpty'. Si vous voulez projeter, vous utilisez une méthode de projection, si vous voulez filtrer, vous utilisez une méthode de filtrage. Vous ne les combinez pas en une seule méthode qui fait deux choses sans rapport.De plus, il est encore sémantiquement faux d'utiliser ici des valeurs null comme une représentation des réponses échouées; * C'est * le problème à résoudre. – Servy

2

Le vrai problème ici est que vous ne voulez pas être sélectionner une valeur null lorsque la le service n'a pas de réponse satisfaisante. Vous voulez filtrer les réponses réussies en tant que partie de votre requête:

var readFields = from r in dsReader 
    let serviceResponse = myService.Decrypt<DateTime>(r.GetString(DATE_VALUE), r.GetInt32(DEK_ID)) 
    where serviceResponse.IsSuccessful 
    select new DataField<DateFieldValue> 
    { 
     FieldValue = new DateFieldValue { Data = serviceResponse.Value } 
    }; 
+0

Ou en syntaxe lambda 'var readFields = dsReader.Select (r => myService.Decrypt (r.GetString (DATE_VALUE), r.GetInt32 (DEK_ID))). Où (sr => sr.IsSuccessful) .Select (nouveau DataField {FieldValue = nouveau DateFieldValue {Data = serviceResponse.Value}}); ' – NetMage

+0

BTW, très belle inversion du problème pour le rendre plus élégant. – NetMage