2009-09-17 4 views
5

Importante La pregunta no es "¿Qué hacer Queryable.OfType , es '¿cómo el código veo que hay que cumplir'¿Cómo funciona Queryable.OfType?

Reflexionando sobre Queryable.OfType, veo (después de algo de limpieza)?:

public static IQueryable<TResult> OfType<TResult>(this IQueryable source) 
    { 
     return (IQueryable<TResult>)source.Provider.CreateQuery(
      Expression.Call(
       null, 
       ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(
        new Type[] { typeof(TResult) }) , 
        new Expression[] { source.Expression })); 
    } 

Así que vamos a ver si lo he entendido bien:.

  1. utilizar la reflexión para tomar una referencia al método actual (OfType)
  2. Haz un nuevo método, que es exactamente el mismo, usando MakeGenericMethod para cambiar el parámetro de tipo del método actual a, er, exactamente la misma cosa.
  3. El argumento para ese nuevo método no será source, sino source.Expression. Lo cual no es un IQueryable, pero le pasaremos todo a Expression. Llama, así que está bien.
  4. llamada Expression.Call, pasando null como método (raro?)ejemplo y el método clonado como sus argumentos.
  5. Pase ese resultado a CreateQuery y emita el resultado, que parece ser la parte más sana de todo el asunto.

Ahora el efecto de este método es devolver una expresión que indica al proveedor omitir devolver cualquier valor en el que el tipo no es igual a TResult o uno de sus subtipos. Pero no puedo ver cómo los pasos anteriores realmente logran esto. Parece que está creando una expresión que representa un método que devuelve IQueryable <TResult>, y convierte el cuerpo de ese método simplemente en la expresión de origen completa, sin siquiera mirar el tipo. ¿Se espera simplemente que un proveedor de IQueryable silenciosamente no devuelva ningún registro que no sea del tipo seleccionado?

¿Los pasos anteriores son incorrectos de alguna manera o simplemente no veo cómo resultan en el comportamiento observado en el tiempo de ejecución?

Respuesta

5

No está pasando en null como método - lo está pasando como la "expresión de destino", es decir, lo que está llamando al método. Esto es nulo porque OfType es un método estático, por lo que no necesita un objetivo.

El punto de llamada MakeGenericMethod es que GetCurrentMethod() devuelve la versión abierta, es decir, OfType<> en lugar de OfType<YourType>.

Queryable.OfType en sí no es significa para contener cualquiera de las lógicas para omitir la devolución de los valores. Eso depende del proveedor de LINQ. El objetivo de Queryable.OfType es crear el árbol de expresiones para incluir la llamada al OfType, de modo que cuando el proveedor de LINQ finalmente tenga que convertirlo a su formato nativo (por ejemplo, SQL) sepa que se llamó al OfType.

Así es como funciona Queryable en general, básicamente le permite al proveedor ver toda la expresión de consulta como un árbol de expresiones. Eso es todo lo que tiene que hacer, cuando se le pide al proveedor que traduzca esto en código real, que es donde ocurre la magia.

Queryable no podría hacer el trabajo en sí mismo, no tiene idea de qué tipo de almacenamiento de datos representa el proveedor. ¿Cómo podría surgir la semántica de OfType sin saber si el almacén de datos era SQL, LDAP u otra cosa? Estoy de acuerdo en que toma un tiempo darme la vuelta :)

+0

OK, ¿lo tengo claro? 1. La llamada a MakeGenericMethod es necesaria porque puede pasar una versión abierta de OfType, con un argumento de YourType (por separado), pero no puede pasar OfType como un argumento. Esa parte al menos tiene sentido. 2. El objetivo de todo esto es simplemente decirle al proveedor "ir a implementar OfType" en lugar de producir una expresión de una implementación de OfType. Continúa en el siguiente comentario ... –

+0

Sin embargo, no puede simplemente devolver algo similar a Expression.OfType (lo que esperaría, según su explicación) porque eso realmente no existe. Entonces, en su lugar, devuelve algo que equivale a lo mismo, solo que se implementa de manera diferente por alguna razón. –

+1

No, devuelve algo que representa una llamada a 'Queryable.OfType', al igual que' Select' devuelve un 'IQueryable ' que representa la consulta anterior con una llamada encadenada a 'Queryable.Select', etc. No es un * expresión * de 'OfType', es una expresión que representa una * llamada * a' OfType'. –