Не удается вызвать методы расширения с динамическими параметрами и дженериками

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

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

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

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

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

  • бросил 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;
    }
}

В ИТОГЕ:

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

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

1 ответов


как было предложено, я постараюсь и ответ мой вопрос в ответ... (Теперь, когда прошло 8 часов)

мое понимание вопроса таково:

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

например:

dynamic list = someListObject;

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

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

как указал Джон Скит в ответ это все по дизайну и часть реализации 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();