2012-06-13 21 views
11

Tengo curiosidad por ver si alguien más ha tenido este mismo problema ... Estoy usando Dapper como en ORM para un proyecto y estaba creando algunos de mis propios métodos de extensión de la interfaz IDbConnection con el fin de simplificar el código, donde me encontré con (lo que encontré) error de desconcierto.No puedo llamar a los métodos de extensión con parámetros dinámicos y genéricos

Voy a caminar a través del proceso que atravesé.

En primer lugar, he añadido un método de extensión de mi proyecto en una clase estática denominada DbExtensions así:

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; 
    } 
} 

Esto crea un error de compilación con la siguiente descripción:

'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.

Esto está bien, y el error es bastante útil, ya que incluso me dice cómo solucionarlo. Por lo tanto, a continuación, tratar:

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; 
    } 
} 

y compila correctamente. Algo extraño está sucediendo sin embargo. En Visual Studio, si tomo el valor devuelto de SqlMapper.Query<T> que debe ser IEnumerable<T>, y trato de operar en él, Visual Studio no me otorga NINGUNA propiedad intellisense excepto las heredadas a través de object.

Pensando que estoy haciendo algo que intellisense no es lo suficientemente inteligente como para descubrirlo, sigo mi camino feliz ... hasta que intento ejecutar el código.

Cuando trato de ejecutarlo, se dispara hasta donde yo estoy llamando .First() con el siguiente error:

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

Ahora este error, pensé que era interesante ... Después de golpearse la cabeza durante una Mientras, me di cuenta de que el primer argumento se quejaba del tipado dinámico ...

Supongo que este error ocurre porque el compilador no puede construir la Plantilla genérica porque no sabe que Query está devolviendo IEnumerable<T> mientras se está ejecutando en el DLR? Me encantaría escuchar a alguien explicar esto, que estaba bien informado. He encontrado básicamente dos maneras de solucionarlo:

  • emitir el parámetro dynamic a un object
  • Fundido el valor devuelto a una 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; 
    } 
} 

EN RESUMEN:

Soy nuevo en el trabajo a través de los qwerks del DLR y parece haber algunas advertencias a tener en cuenta cuando se juega con Dynamic + Generics ...?

Sé que esto no es una pregunta per se, pero cuando realmente empecé a escribir esto, no sabía lo que estaba pasando y lo he descubierto en el proceso! Pensé que podría ayudar a alguien con problemas similares aunque ...

+0

posible duplicado de [Método de extensión y objeto dinámico en C#] (http://stackoverflow.com/questions/5311465/extens ión-método-y-dinámico-objeto-en-do sostenido) –

+0

Esto es impresionante, pero tenga en cuenta que para las preguntas de auto-respondido a la Convención es lo ideal para publicar la pregunta y la respuesta por separado. – McGarnagle

+0

@dbaseman Gracias. Me escribió una respuesta por separado, pero al parecer no tengo suficientes puntos para "auto-respuesta" hasta que hayan transcurrido 8 horas: P –

Respuesta

11

Como se sugirió, voy a tratar de respuesta a mi pregunta en una respuesta real ... (Ahora que ya son 8 horas)

Mi comprensión de la cuestión es la siguiente:

  • como se describe en el referenced question, tipos dinámicos no tienen métodos de extensión disponibles para ellos, pero los métodos de extensión se puede utilizar normalmente (como ejemplo métodos), al igual que serían sin el this keywor d ...

por ejemplo:

dynamic list = someListObject; 

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

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

Como Jon Skeet ha señalado en this answer esto es todo por el diseño y la parte de la implementación DLR - donde si cualquier invocación tiene un argumento dinámico que lo hará tener un tipo de devolución considerado dinámico.

  • Por razones similares, el uso de variables dinámicas en los métodos de extensión es un poco torcidas ...

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() 

Para que el ejemplo anterior trabajo que puede hacer uno de los siguientes:

//cast dynamic as object 
var returnedValue = x.ExtensionMethod(y as object).First(); 
//cast returned object 
var returnedValue = ((IEnumerable<KnownType>)x.ExtensionMethod(y)).First(); 
+0

Sólo parece funcionar si lanzar al tipo real (como ExtendedObject). Casting to Object no parece funcionar para esto. –

Cuestiones relacionadas