2012-06-13 2 views
11

Мне любопытно узнать, столкнулся ли кто-то еще с этой проблемой ... Я использую 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 не дает мне никаких свойств intellisense, кроме тех, которые унаследованы через object.

Думаю, что я просто делаю то, что intellisense недостаточно умен, чтобы понять, я продолжаю свой веселый путь ... пока я на самом деле не попытаюсь запустить код.

Когда я пытаюсь запустить его, он срабатывает там, где я звоню .First() со следующей ошибкой:

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

Теперь эта ошибка, я думал, было интересно ... После того, как стучал головой для в то время как я понял, что первый аргумент жаловался на динамическую типизацию ...

Я предполагаю, что эта ошибка возникает из-за того, что компилятор не может построить общий шаблон, поскольку он не знает, что Query возвращает IEnumerable<T>, поскольку он выполняется в DLR? Я хотел бы услышать, как кто-то объяснил это, кто был осведомлен. Я по существу нашел два способа исправить:

  • закиньте dynamic Param к object
  • Cast возвращенное значение в 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; 
    } 
} 

В РЕЗЮМЕ:

Я новичок в работе с qwerks из DLR, и, похоже, есть некоторые предостережения, которые следует учитывать при взаимодействии с динамическими + Generics ...?

Я знаю, что это не вопрос, но когда я начал писать это, я не знал, что происходит, и я понял это в этом процессе! Я думал, что это может помочь кому-то еще с подобными проблемами, хотя ...

+0

Возможный дубликат [Метод расширения и динамический объект в C#] (http://stackoverflow.com/questions/5311465/extens ion-method-and-dynamic-object-in-c-sharp) –

+0

Это потрясающе, но обратите внимание, что для ответов на собственные вопросы соглашение идеально подходит для отправки вопросов и ответов отдельно. – McGarnagle

+0

@dbaseman Спасибо. Я написал отдельный ответ, но, по-видимому, у меня недостаточно очков для «ответа на себя» до тех пор, пока не истечет 8 часов: P –

ответ

11

Как было предложено, я буду стараться и Ответ моего вопрос в реальном ответе ... (Теперь, когда это было 8 часов)

Моей понимание этого вопроса заключается в следующем:

  • Как описано в referenced question, динамические типы не имеют методы расширения, доступные для них, но методы расширения можно использовать как обычно (как например методы), так же, как они будут без ключа this d ...

, например:

dynamic list = someListObject; 

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

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

Как Джон Скит отметил в this answer все это по конструкции и части реализации 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(); 
+0

Кажется, что это работает, если вы отбрасывается на фактический тип (например, ExtendedObject). Кастинг для объекта, похоже, не работает для этого. –

Смежные вопросы