Lel*_*son 11 c# generics extension-methods dynamic dapper
我很好奇,看看是否有其他人遇到过同样的问题......我正在ORM上使用Dapper作为项目,并且正在创建一些我自己的扩展方法,IDbConnection以简化代码,我遇到了(我发现的是)令人费解的错误.
我将介绍我经历的过程.
首先,我在一个名为DbExtensionsso 的静态类中为我的项目添加了一个扩展方法:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
这会使用以下描述创建编译错误:
'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;
}
}
Run Code Online (Sandbox Code Playgroud)
它正确编译.虽然有些奇怪的事情发生了.在Visual Studio中,如果我带的返回值SqlMapper.Query<T>,其应该是IEnumerable<T>,我试图对它进行操作时,Visual Studio给我除了那些通过继承NO智能感知特性object.
以为我只是在做一些智能感知不够聪明的东西,我继续我的快乐方式......直到我真的尝试运行代码.
当我尝试运行它时,它会在我调用的地方绊倒.First()以下错误:
'System.Collections.Generic.List<MyNameSpace.MyClass>' does not contain a definition for 'First'
现在这个错误,我觉得很有意思......在敲了一下头之后,我意识到第一个论点是抱怨动态打字......
我想这个错误正在发生,因为编译器无法构建通用模板,因为它不知道Query正在返回, IEnumerable<T>因为它正在DLR中执行?我很想听到有人解释这个知识渊博的人.我基本上找到了两种方法来解决它:
dynamic参数投到一个objectIEnumerable<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;
}
}
Run Code Online (Sandbox Code Playgroud)
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;
}
}
Run Code Online (Sandbox Code Playgroud)
综上所述:
我是新手,通过DLR的qwerks,在搞乱动态+泛型时,似乎需要记住一些注意事项......?
我知道这不是一个问题本身,但是当我真正开始写这篇文章时,我不知道发生了什么,我在这个过程中想出来了!我认为它可能会帮助其他有类似问题的人...
Lel*_*son 10
正如所建议的那样,我会尝试在实际答案中回答我的问题......(现在已经8小时了)
我对这个问题的理解是这样的:
this关键字一样......例如:
dynamic list = someListObject;
var item = list.First(); //this will not compile
var item = Enumerable.First(list); //this will compile
Run Code Online (Sandbox Code Playgroud)
正如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()
Run Code Online (Sandbox Code Playgroud)
要使上述示例有效,您可以执行以下操作之一:
//cast dynamic as object
var returnedValue = x.ExtensionMethod(y as object).First();
//cast returned object
var returnedValue = ((IEnumerable<KnownType>)x.ExtensionMethod(y)).First();
Run Code Online (Sandbox Code Playgroud)