Erweiterungsmethoden können nicht mit dynamischen Parametern und Generika aufgerufen werden

c# dapper dynamic extension-methods generics

Frage

Ich bin gespannt, ob noch jemand auf dasselbe Problem IDbConnection ... Ich benutze Dapper als ORM für ein Projekt und erstelle einige meiner eigenen Erweiterungsmethoden von der IDbConnection Schnittstelle, um Code zu vereinfachen, wo ich lief in (was ich gefunden habe) rätselhafter Fehler.

Ich werde durch den Prozess gehen, den ich durchlaufen habe.

Zuerst habe ich meinem Projekt eine Erweiterungsmethode in einer statischen Klasse namens DbExtensions :

using System.Collections.Generic;
using System.Data;
using System.Linq;

public static class DbExtensions
{
    public static T Scalar<T>(
        this IDbConnection cnn, string sql, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, CommandType? commandType = null)
    {
        var ret = cnn.Query<T>(sql, param as object, transaction, buffered, commandTimeout, commandType).First();
        return ret;
    }
}

Dies erzeugt einen Kompilierungsfehler mit der folgenden Beschreibung:

'System.Data.IDbConnection' has no applicable method named 'Query' but appears to have an extension method by that name. Extension methods cannot be dynamically dispatched. Consider casting the dynamic arguments or calling the extension method without the extension method syntax.

Das ist in Ordnung, und der Fehler ist tatsächlich ziemlich hilfreich, da es mir sogar sagt, wie ich es beheben kann. Also ich versuche es dann:

using System.Collections.Generic;
using System.Data;
using System.Linq;

public static class DbExtensions
{
    public static T Scalar<T>(
        this IDbConnection cnn, string sql, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, CommandType? commandType = null)
    {
        var ret = SqlMapper.Query<T>(cnn, sql, param, transaction, buffered, commandTimeout, commandType).First();
        return ret;
    }
}

und es kompiliert korrekt. Etwas Merkwürdiges passiert jedoch. In Visual Studio, wenn ich den Rückgabewert nehmen SqlMapper.Query<T> , die sein sollte IEnumerable<T> , und ich versuche , darauf zu arbeiten, gibt mir Visual Studio KEINE intellisense Eigenschaften mit Ausnahme derjenigen über geerbt object .

Ich denke, ich mache nur etwas, das intellisense nicht schlau genug ist, um herauszufinden, ich gehe auf meinem fröhlichen Weg ... bis ich tatsächlich versuche, den Code zu starten.

Wenn ich versuche, es .First() , .First() es auf, wo ich rufe. .First() mit dem folgenden Fehler:

'System.Collections.Generic.List<MyNameSpace.MyClass>' does not contain a definition for 'First'

Nun, DIESER Fehler, dachte ich, war interessant ... Nachdem ich eine Weile mit dem Kopf geschlagen hatte, wurde mir klar, dass das erste Argument sich über das dynamische Tippen beschwerte ...

Ich nehme an, dass dieser Fehler auftritt, weil der Compiler die generische Vorlage nicht erstellen kann, weil es nicht weiß, dass Abfrage IEnumerable<T> wie es in der DLR ausgeführt wird? Ich würde gerne jemanden hören, der das kennt. Ich habe im Wesentlichen zwei Möglichkeiten gefunden, es zu beheben:

  • Übergeben Sie den dynamic Parameter an ein object
  • IEnumerable<T> den zurückgegebenen Wert an IEnumerable<T>

using System.Collections.Generic;
using System.Data;
using System.Linq;

public static class DbExtensions
{
    public static T Scalar<T>(
        this IDbConnection cnn, string sql, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, CommandType? commandType = null)
    {
        var ret = SqlMapper.Query<T>(cnn, sql, param as object, transaction, buffered, commandTimeout, commandType).First();
        return ret;
    }
}

using System.Collections.Generic;
using System.Data;
using System.Linq;

public static class DbExtensions
{
    public static T Scalar2<T>(
        this IDbConnection cnn, string sql, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, CommandType? commandType = null)
    {
        var ret = ((IEnumerable<T>)SqlMapper.Query<T>(cnn, sql, param, transaction, commandTimeout, commandType)).First();
        return ret;
    }
}

ZUSAMMENFASSEND:

Ich bin neu in den Qwerks des DLR zu arbeiten und es scheint ein paar Vorbehalte zu geben, wenn man sich mit Dynamic + Generics beschäftigt ...?

Ich weiß, dass das keine Frage per se ist, aber als ich eigentlich anfing zu schreiben, wusste ich nicht was vor sich ging und ich fand es heraus! Ich dachte, dass es jemand anderem mit ähnlichen Problemen helfen könnte, obwohl ...

Akzeptierte Antwort

Wie bereits angedeutet, werde ich meine Frage in einer tatsächlichen Antwort versuchen und Antwort ... (Jetzt, wo es 8 Stunden gewesen ist)

Mein Verständnis des Problems ist das:

  • Wie in der referenzierten Frage beschrieben , stehen dynamischen Typen keine Erweiterungsmethoden zur Verfügung, aber Erweiterungsmethoden können normal (als Instanzmethoden ) verwendet werden, genauso wie sie ohne das this Schlüsselwort ausgeführt würden.

zum Beispiel:

dynamic list = someListObject;

var item = list.First(); //this will not compile

var item = Enumerable.First(list);  //this will compile

Wie Jon Skeet in dieser Antwort darauf hingewiesen hat, ist dies alles durch Design und Teil der DLR-Implementierung - wobei, wenn irgendein Aufruf ein dynamisches Argument hat, ein Rückgabetyp als dynamisch angesehen wird.

  • Aus ähnlichen Gründen ist die Verwendung dynamischer Variablen in Erweiterungsmethoden ein bisschen wackelig ...

public static Enumerable<T> ExtensionMethod(this ExtendedObject p1, dynamic p2) {
    //Do Stuff
}

dynamic y = something;
var x = new ExtendedObject();

//this works
var returnedEnumerable = x.ExtensionMethod(y); 

//this doesn't work
var returnedValue = x.ExtensionMethod(y).SomeEnumerableExtensionMethodLikeFirst() 

Um das obige Beispiel zu verwenden, können Sie einen der folgenden Schritte ausführen:

//cast dynamic as object
var returnedValue = x.ExtensionMethod(y as object).First(); 
//cast returned object
var returnedValue = ((IEnumerable<KnownType>)x.ExtensionMethod(y)).First(); 


Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow