ダイナミックなパラメータとジェネリックで拡張メソッドを呼び出せません

c# dapper dynamic extension-methods generics

質問

他の誰かがこの同じ問題に遭遇したのかどうか不思議です... DapperをプロジェクトのORMとして使っていて、コードを単純化するために自分の拡張メソッドをIDbConnectionインターフェイスから作成していました。 (私が見つけたものに)困惑している誤り。

私はその過程を歩みます。

まず、拡張メソッドを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;
    }
}

これにより、次の記述でコンパイルエラーが発生します。

'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.

これは問題ありません。エラーを修正する方法を教えてくれるので、エラーは実際には役に立ちます。だから私は試してみましょう:

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

それは正しくコンパイルされます。しかし、何か変なことが起こっている。私はの戻り値取る場合Visual Studioで、 SqlMapper.Query<T>なければなりません IEnumerable<T> 、と私はそれを操作しようとすると、Visual Studioは、経由して継承されたものを除き、私にNOインテリセンスのプロパティを与えないobject

思考私はintellisenseが理解するのに十分なほどスマートではないことをやっているだけで、私は実際にコードを実行しようとするまで、私は気持ちがいい。

私はそれを実行しようとすると、次のエラーで.First()を呼び出している場所に.First()ます。

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

今このエラーは面白かったと思っていました...しばらく私の頭を叩いた後、最初の議論は動的なタイピングについて不平を言っていました。

私はこのエラーが発生していると思います。なぜなら、クエリがIEnumerable<T>をDLRで実行されていることを返すことを知らないので、コンパイラは汎用テンプレートを構築できないからです。私は誰かが知識豊富なこれを説明するのを聞くのが大好きです。私は本質的にそれを修正する2つの方法を見つけました:

  • dynamicパラメータをobjectキャストする
  • 返された値を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;
    }
}

要約すれば:

私はDLRのqwerksに取り組むのが初めてで、ダイナミック+ジェネリックスを使いこなすときに留意すべきいくつかの注意点があるようです...?

私はこれが単なる質問ではないことを知っていますが、実際にこれを書いてみると、何が起こっているのか分からず、その過程でわかりました!私はそれが同じような問題を持つ他の誰かを助けるかもしれないと思った...

受け入れられた回答

示唆したように、私がしようとすると(それが8時間をされていることを今)...実際の答えに私の質問に答えます

問題の私の理解はこれです:

  • 参照される質問に記載されているように、動的型には利用可能な拡張メソッドがありませんが、拡張メソッドはthisキーワードなしで通常どおり( インスタンスメソッドとして)使用できます...

例えば:

dynamic list = someListObject;

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

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

Jon Skeet氏がこの答えで指摘しているようにこれはすべて設計上、DLR実装の一部です。呼び出しに動的引数がある場合、戻り型は動的とみなされます。

  • 同様の理由から、拡張メソッドで動的変数を使うのは少しうんざりです...

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() 

上の例を動作させるには、次のいずれかを実行します。

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


ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow
このKBは合法ですか? はい、理由を学ぶ
ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow
このKBは合法ですか? はい、理由を学ぶ