¿Por qué Select () no convierte un IEnumerable a IEnumerable ?

c# dapper linq

Pregunta

Intento usar Dapper simplemente para asignar las tablas de mi base de datos a tipos en C #, sin embargo, algunos de mis tipos necesitan elementos adicionales que no están en la tabla. Para hacer esto, estoy usando una fábrica que puede tomar valores de columna y establecer las propiedades apropiadas.

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

Actualmente, esto está dando como resultado la declaración de devolución que genera un error:

No se puede convertir el tipo de expresión 'System.Collections.Generic.IEnumerable<dynamic>' para devolver el tipo 'System.Collections.Generic.IEnumerable<IMyType>'

Mi clase de fábrica se ve así:

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));
  }
}

¿Por qué el método Select() devuelve IEnumerable<IMyType> ? ¿Qué debo hacer para que esto funcione? ¿Es este el enfoque equivocado y hay una mejor manera?

Respuesta aceptada

La solución más simple es usar el operador Cast<> LINQ:

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

Alternativamente, puedes lanzar cada elemento:

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

Actualmente no funciona porque simplemente no hay una conversión implícita disponible entre IEnumerable<dynamic> e IEnumerable<IMyType> . IEnumerable<dynamic> podría implementarse de muchas maneras, y dado que cada elemento se generará dinámicamente, no hay ninguna razón para suponer que el valor resultante implementará IEnumerable<IMyType> .

Estoy de acuerdo en que parece que la segunda forma no agrega nada en realidad, pero el compilador no verifica todos los posibles tipos de retorno de _myTypeFactory.Create(o) - trata esa expresión completa como un valor dinámico, es decir, la expresión es de tipo dynamic . Por lo tanto, el resultado Select sigue siendo del tipo IEnumerable<dynamic> .

Otra opción es especificar el argumento de tipo genérico para Select .

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

Eso está intentando forzar la expresión lambda a Func<dynamic, IMyType> - Creo que funcionará ...

EDITAR: Como se señala en los comentarios, forzar la invocación del método para que se resuelva en tiempo de compilación lo arreglará también. Básicamente depende de lo que le resulte más legible.


Respuesta popular

La mejor solución es, probablemente, eliminar la invocación dinámica de la instrucción select y luego obtendrá el tipo estático esperado 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
}

O

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)); 
}


Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué