这里是什么导致了“扩展方法无法动态调度”的问题?

34

编译错误

'System.Data.SqlClient.SqlConnection' 没有适用的方法名为 'Query',但似乎有一个同名的扩展方法。扩展方法无法动态分派。考虑将动态参数转换或在不使用扩展方法语法的情况下调用扩展方法。

现在,我知道如何解决问题,但我正在试图更好地理解这个错误本身。我有一个类正在构建以利用 Dapper。最终,我将提供一些更自定义的功能,以使我们的数据访问类型更加流畅。特别是构建追踪和其他东西。然而,现在它就像这样简单:

public class Connection : IDisposable
{
    private SqlConnection _connection;

    public Connection()
    {
        var connectionString = Convert.ToString(ConfigurationManager.ConnectionStrings["ConnectionString"]);
        _connection = new SqlConnection(connectionString);
        _connection.Open();
    }

    public void Dispose()
    {
        _connection.Close();
        _connection.Dispose();
    }

    public IEnumerable<dynamic> Query(string sql, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, CommandType? commandType = null)
    {
        // this one works fine, without compile error, so I understand how to
        // workaround the error
        return Dapper.SqlMapper.Query(_connection, sql, param, transaction, buffered, commandTimeout, commandType);
    }

    public IEnumerable<T> Query<T>(string sql, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, CommandType? commandType = null)
    {
        // this one is failing with the error
        return (IEnumerable<T>)_connection.Query(sql, param, transaction, buffered, commandTimeout, commandType);
    }
}

有趣的是,如果我只是说出这样一句话:

_connection.Query("SELECT * FROM SomeTable");

它编译得很好。

那么,请有人帮助我理解为什么在那些其他方法中利用相同的重载会导致出现该错误?


@pst,很好,它在技术上并没有被抛出。 - Mike Perrenoud
2
为什么您需要动态参数而不是对象参数?您没有对其执行任何操作或方法调用。对吗? - Meirion Hughes
2个回答

48
所以,能有人解释一下为什么在那些其他方法内部利用同样的重载会失败并出现错误吗?
正是因为您将动态值(param)用作参数之一。这意味着它将使用动态调度...但是动态调度不支持扩展方法。
不过解决方法很简单:只需直接调用静态方法即可:
return SqlMapper.Query(_connection, sql, param, transaction,
                       buffered, commandTimeout, commandType);

(当然,这是在假设您真的需要paramdynamic类型时...正如评论中所指出的,您可能只需将其更改为object即可解决问题。)


2
这正是我一直在寻找的,非常感谢Jon!我没有问题来解决它,就像你在例子中所做的那样,以及我在第一个方法中所做的那样,我只是想知道为什么,如果这有意义的话。我认为我会将其更改为“object”,因为我不需要对其执行任何操作。 - Mike Perrenoud
1
@MichaelPerrenoud 你可能已经发现了,但在这种情况下 Foo 就是 SqlMapper;所以 SqlMapper.Query(...) - Marc Gravell
@MikePerrenoud 这里有一个更完整的答案 来回答,“为什么?” - jpaugh

1

解决同样问题的另一种方法是对动态值应用类型转换。

我遇到了相同的编译错误:

Url.Asset( "path/" + article.logo );

这个问题通过以下方式得以解决:
Url.Asset( "path/" + (string) article.logo );

注意:动态值在这种情况下被广泛认为是字符串,这一事实得到了存在的字符串连接的加强。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接