2011-12-14 18 views
20

¿Hay alguna manera de saber si se ha pedido un IQueryable<T> (usando OrderBy o OrderbyDescending)?Determine si se ha pedido o no un <T> de IQ

Entonces sé si llamar OrderBy o ThenBy en la colección.

IQueryable<Contact> contacts = Database.GetContacts(); 

me trataron contacts is IOrderedQueryable<Contact>, pero siempre es cierto.

Editar: Acabo de cambiar mi ejemplo, el anterior no mostraba realmente mi punto. Supongamos que GetContacts usa Entity Framework y simplemente devuelve todos los registros de una tabla.

Más tarde, aplico varias funciones al contacts, no tengo conocimiento de lo que hacen esas funciones. Pueden clasificar o filtrar el IQueryable<Contact>.

Cuando recupere la colección, debo ordenarla una vez más. Para hacerlo, necesito saber si necesito llamar al OrderBy, o al ThenBy. Por lo tanto, no reorganizo toda la colección si ya se ha ordenado.

+0

Si se aloja en el ámbito de 'IQueryable', es lo que tiene que preocuparse? Lo que quiero decir es que un '.OrderBy' adicional podría dar como resultado que no haya ningún cambio en lo que realmente está ejecutando el proveedor en la fuente de datos subyacente. – AakashM

+0

@AakashM, si llamo a 'OrderBy' en un' IQueryable' ya ordenado, pierdo ese tipo. Solo quiero agregarle –

+0

En realidad estaba pensando que estabas preocupado por perf. ¿Qué tal si siempre usas 'ThenBy'? – AakashM

Respuesta

2

La respuesta breve es no, la clase Queryable no mantiene un indicador o si la colección está ordenada ni qué método se usó para realizar dicho tipo.

http://msdn.microsoft.com/en-us/library/system.linq.queryable.aspx

+7

Entonces, ¿cómo es ['.Skip'] (http://msdn.microsoft.com/en-us/library/bb357513.aspx) capaz de [lanzar una excepción] (http://stackoverflow.com/questions/ 225481/how-to-check-for-the-presence-of-an-orderby-in-a-objectqueryt-expression-tree) cuando lo usa en una colección EF no ordenada? –

+1

No creo que .Skip arroje una excepción. El proveedor subyacente arroja una excepción cuando intenta generar una consulta desde * todo * IQueryable. –

+1

Es cierto que no existe una bandera como tal, pero es posible saber si se ha llamado a un método de pedido. Ver [mi respuesta] (http://stackoverflow.com/a/31252271/964514). –

1

Nunca sabrá si los objetos se ordenaron correctamente, a menos que usted mismo compruebe el pedido. Su ejemplo es fácil de ver, no están ordenados, porque los números tienen un orden natural, pero IQueryable es genérico, lo que significa que puede manejar diferentes tipos de objetos. El orden de los objetos de usuario de decir (FirstName, LastName, DateStart y LastPayDate) tiene un orden arbitrario, por lo que el orden en el que se devuelven no es necesariamente el pedido que está buscando. (¿Cuál es considerado el campo primario para el género? Depende de su necesidad.) Entonces, en teoría, la pregunta "¿Están ordenados?" Podría ser siempre "¡Sí!" El orden que está buscando puede ser muy diferente de lo que devuelve el sistema.

+0

Mi ejemplo fue solo para señalar que la prueba con 'IOrderedQueryable' siempre devuelve verdadero. Voy a aclarar mi pregunta. –

1

En realidad, se puede.

El primer problema que identifico en su código es que está lanzando la colección al IQueryable sin ningún motivo para hacerlo.

El siguiente fragmento:

var numbers = new[] {1, 5, 6, 87, 3}; 
Console.Write(numbers is IOrderedEnumerable<int>); 
var ordered = numbers.OrderBy(c => c); 
Console.Write(ordered is IOrderedEnumerable<int>); 

ni siquiera necesita que se ejecute: el primer cheque que consigue un tiempo de diseño de advertencia diciendo que esta expresión nunca será verdadera.

De todos modos, si lo ejecuta, le dará False para el primer control, y True para el segundo control.

Puede hacer lo mismo con IQueryable<T> y IOrderedQueryable<T> siempre que esté utilizando realmente ese tipo y no le haga una recopilación.

+0

¿Qué sucede si los elementos enumerables ya están en orden? –

+3

Esto no parece funcionar en una colección como 'dbSet.AsQueryable()', ya que esto siempre será verdadero en la verificación 'is IOrderedQueryable'. – Juri

1

Puede examinar el ToString() de su consulta para averiguar si se utiliza Order By.

Cuando se produce una unión, ToString de IQueryable pone parantheses al principio y al final de la consulta interna.Entonces, si encuentra las últimas parantheses de cierre, puede verificar si su consulta externa tiene una cláusula Order By.

private bool isOrdered(IQueryable Data) 
    { 
     string query = Data.ToString(); 

     int pIndex = query.LastIndexOf(')'); 

     if (pIndex == -1) 
      pIndex = 0; 

     if (query.IndexOf("ORDER BY", pIndex) != -1) 
     { 
      return true; 
     } 

     return false; 
    } 

Sé que es extremadamente sucio pero funciona en todos mis casos y no puedo pensar en un caso excepcional.

+0

Me gusta su solución, sucia pero funciona como un encanto! –

16

Es posible. He aquí un método de extensión:

public static bool IsOrdered<T>(this IQueryable<T> queryable) 
{ 
    if (queryable == null) 
    { 
     throw new ArgumentNullException("queryable"); 
    } 

    return queryable.Expression.Type == typeof(IOrderedQueryable<T>); 
} 
+0

Gracias, eso es lo que he buscado. Simplemente cambiaría la comprobación de la implementación de la interfaz en lugar de la igualdad de tipo ... typeof (IOrderedQueryable ) .IsAssingableFrom (queryable.Expression.Type) – Ondrej

+0

¿Cuáles son los beneficios de ese cambio @Ondrej? También noté que tampoco funcionaba si OrderBy no era la operación más reciente :( –

2

Sí se puede inspeccionar el árbol IQueryable.Expression para ver si se llama a cualquiera de los métodos OrderBy/ThenBy. Los árboles de expresión se pueden examinar derivando una clase de ExpressionVisitor.

Hay un OrderingMethodFinder interno en System.Web - que puede adaptar. Esto es lo que ocurrió:

// Adapted from internal System.Web.Util.OrderingMethodFinder http://referencesource.microsoft.com/#System.Web/Util/OrderingMethodFinder.cs 
class OrderingMethodFinder : ExpressionVisitor 
{ 
    bool _orderingMethodFound = false; 

    protected override Expression VisitMethodCall(MethodCallExpression node) 
    { 
     var name = node.Method.Name; 

     if (node.Method.DeclaringType == typeof(Queryable) && (
      name.StartsWith("OrderBy", StringComparison.Ordinal) || 
      name.StartsWith("ThenBy", StringComparison.Ordinal))) 
     { 
      _orderingMethodFound = true; 
     } 

     return base.VisitMethodCall(node); 
    } 

    public static bool OrderMethodExists(Expression expression) 
    { 
     var visitor = new OrderingMethodFinder(); 
     visitor.Visit(expression); 
     return visitor._orderingMethodFound; 
    } 
} 

usarlo como así:

bool isOrdered = OrderingMethodFinder.OrderMethodExists(myQuery.Expression); 
Cuestiones relacionadas