无法使用动态参数和泛型调用扩展方法

c# dapper dynamic extension-methods generics

我很想知道是否有其他人遇到过同样的问题...我正在ORM上使用Dapper作为项目,并且正在从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除了通过object继承的属性外,没有给出智能感知属性。

以为我只是在做一些智能感知不够聪明的事情,我继续我的快乐方式......直到我真的尝试运行代码。

当我尝试运行它时,它会在我调用.First()时出现以下错误:

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

现在这个错误,我觉得很有意思......在敲了一下头之后,我意识到第一个论点是抱怨动态打字......

我想这个错误正在发生,因为编译器无法构建通用模板,因为它不知道Query正在返回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;
    }
}

综上所述:

我是新手,通过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
许可下: CC-BY-SA with attribution
不隶属于 Stack Overflow