Ich versuche, Dapper einfach zu verwenden, um meine Datenbanktabellen Typen in C # zuzuordnen, jedoch benötigen einige meiner Typen zusätzliche Elemente, die nicht in der Tabelle enthalten sind. Dazu verwende ich eine Factory, die Spaltenwerte annehmen und die entsprechenden Eigenschaften festlegen kann.
public IEnumerable<IMyType> All() {
var query = _connection.Query("SELECT * FROM [table]");
return query.Select(o => _myTypeFactory.Create(o));
}
Zurzeit führt dies dazu, dass die return-Anweisung einen Fehler generiert:
Der Ausdruckstyp 'System.Collections.Generic.IEnumerable<dynamic>'
in den Rückgabetyp 'System.Collections.Generic.IEnumerable<IMyType>'
Meine Fabrikklasse sieht ungefähr so aus:
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));
}
}
Warum gibt die Select()
-Methode IEnumerable<IMyType>
? Was muss ich tun, damit dies funktioniert? Ist das nur die falsche Herangehensweise und es gibt einen besseren Weg?
Die einfachste Lösung besteht darin, den Operator Cast<>
LINQ zu verwenden:
public IEnumerable<IMyType> All() {
var query = _connection.Query("SELECT * FROM [table]");
return query.Select(o => _myTypeFactory.Create(o))
.Cast<IMyType>();
}
Alternativ könntest du jedes Element umwandeln:
public IEnumerable<IMyType> All() {
var query = _connection.Query("SELECT * FROM [table]");
return query.Select(o => (IMyType) _myTypeFactory.Create(o));
}
Es funktioniert derzeit nicht, da zwischen IEnumerable<dynamic>
und IEnumerable<IMyType>
einfach keine implizite Konvertierung verfügbar ist. IEnumerable<dynamic>
kann auf verschiedene Arten implementiert werden, und da jedes Element dynamisch generiert wird, gibt es keinen Grund anzunehmen, dass der Ergebniswert IEnumerable<IMyType>
implementiert.
Ich stimme zu, dass es aussieht, als würde die zweite Form nichts hinzufügen, aber der Compiler überprüft nicht alle möglichen Rückgabetypen von _myTypeFactory.Create(o)
- er behandelt diesen ganzen Ausdruck als dynamischen Wert, dh der Ausdruck ist vom Typ dynamic
. Daher ist das Select
Ergebnis immer noch vom Typ IEnumerable<dynamic>
.
Eine weitere Option besteht darin, das generische Argument für Select
anzugeben.
public IEnumerable<IMyType> All() {
var query = _connection.Query("SELECT * FROM [table]");
return query.Select<IMyType>(o => _myTypeFactory.Create(o));
}
Das versucht den Lambda-Ausdruck zu einem Func<dynamic, IMyType>
zu Func<dynamic, IMyType>
- ich glaube , das wird funktionieren ...
BEARBEITEN: Wie in den Kommentaren erwähnt, wird das Erzwingen des Methodenaufrufs, der zur Kompilierungszeit aufgelöst werden soll, ebenfalls behoben. Grundsätzlich kommt es darauf an, was Sie am besten lesen.
Die beste Lösung ist wahrscheinlich, den dynamischen Aufruf aus der IEnumerable<IMyType>
Anweisung zu entfernen. Dann erhalten Sie den erwarteten statischen Typ 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
}
ODER
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));
}