Pourquoi Select () ne convertit-il pas un IEnumerable <dynamic> à IEnumerable <StrongType> ?

c# dapper linq

Question

J'essaie d'utiliser Dapper simplement pour mapper mes tables de base de données avec des types en C #. Cependant, certains de mes types ont besoin d'éléments supplémentaires qui ne figurent pas dans la table. Pour ce faire, j'utilise une fabrique capable de prendre des valeurs de colonne et de définir les propriétés appropriées.

public IEnumerable<IMyType> All() {
  var query = _connection.Query("SELECT * FROM [table]");
  return query.Select(o => _myTypeFactory.Create(o));
}

Actuellement, cela entraîne la déclaration de retour générant une erreur:

Impossible de convertir le type d'expression 'System.Collections.Generic.IEnumerable<dynamic>' pour renvoyer le type 'System.Collections.Generic.IEnumerable<IMyType>'

Ma classe d'usine ressemble à ceci:

public class MyTypeFactory {
  public IMyType Create(dynamic o) {
    return Create((String) o.Code, (Int32) o.KeyID);
  }
  public IMyType Create(String code, Int32 keyID) {
    return new MyType(code, Cache.Lookup(keyID));
  }
}

Pourquoi la méthode Select() renvoie-t-elle pas IEnumerable<IMyType> ? Que dois-je faire pour que cela fonctionne? Est-ce que c'est juste la mauvaise approche et qu'il y a une meilleure façon?

Réponse acceptée

La solution la plus simple consiste à utiliser l’opérateur Cast<> LINQ:

public IEnumerable<IMyType> All() {
  var query = _connection.Query("SELECT * FROM [table]");
  return query.Select(o => _myTypeFactory.Create(o))
              .Cast<IMyType>();
}

Alternativement, vous pouvez lancer chaque élément:

public IEnumerable<IMyType> All() {
  var query = _connection.Query("SELECT * FROM [table]");
  return query.Select(o => (IMyType) _myTypeFactory.Create(o));
}

Il ne fonctionne pas actuellement car il n'y a simplement pas de conversion implicite disponible entre IEnumerable<dynamic> et IEnumerable<IMyType> . IEnumerable<dynamic> pourrait être implémenté de différentes manières, et comme chaque élément sera généré dynamiquement, il n'y a aucune raison de supposer que la valeur du résultat implémentera IEnumerable<IMyType> .

Je suis d' accord qu'il ressemble à la deuxième forme n'ajoute quoi que ce soit, mais le compilateur ne vérifie pas tous les types de retour possibles de _myTypeFactory.Create(o) - il traite cette expression tout comme une valeur dynamique, à savoir l'expression est de type dynamic . Par conséquent, le résultat de la Select est toujours de type IEnumerable<dynamic> .

Une autre option consiste à spécifier l'argument de type générique à Select .

public IEnumerable<IMyType> All() {
  var query = _connection.Query("SELECT * FROM [table]");
  return query.Select<IMyType>(o => _myTypeFactory.Create(o));
}

Cela tente de forcer l'expression lambda à un Func<dynamic, IMyType> - je crois que ça va marcher ...

EDIT: comme indiqué dans les commentaires, forcer l'invocation de la méthode à être résolue au moment de la compilation le corrigera également. Fondamentalement, cela dépend de ce que vous trouvez le plus lisible.


Réponse populaire

La meilleure solution consiste probablement à supprimer l'invocation dynamique de l'instruction select, puis vous obtiendrez votre type statique attendu IEnumerable<IMyType> .

public IEnumerable<IMyType> All() {
  var query = _connection.Query("SELECT * FROM [table]");
  return query.Select(o => _myTypeFactory.Create((Object)o)); //cast dynamic type to Object
}

OU

public IEnumerable<IMyType> All() {
      IEnumerable<object> query = _connection.Query("SELECT * FROM [table]"); //IEnumerable<dynamic> is the same as IEnumerable<object>
      return query.Select(o => _myTypeFactory.Create(o)); 
}



Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi