2011-01-18 17 views
7

Tengo una colección de objetos CLR. La definición de clase para el objeto tiene tres propiedades: FirstName, LastName, BirthDate.Ordenando dinámicamente con LINQ

Tengo una cadena que refleja el nombre de la propiedad por la que debe ordenarse la colección. Además, tengo una dirección de clasificación. ¿Cómo aplico dinámicamente esta información de clasificación a mi colección? Tenga en cuenta que la clasificación podría ser de varias capas, por lo que, por ejemplo, podría ordenar por Apellido, y luego por Nombre.

Actualmente, estoy intentando lo siguiente sin ninguna suerte:

var results = myCollection.OrderBy(sortProperty); 

Sin embargo, estoy recibiendo un mensaje que dice:

... no contiene una Defintion para 'OrdenarPor 'y la mejor sobrecarga del método de extensión ... tiene algunos argumentos inválidos.

Respuesta

0

se puede hacer esto con LINQ

var results = from c in myCollection 
    orderby c.SortProperty 
    select c; 
+0

El problema es que mi sortProperty es una cadena. ¿Cómo puedo hacer un tipo con una cadena como esa? – user564042

+2

ah, ya veo. Encontré otra publicación aquí que puede ayudar ... http://stackoverflow.com/questions/4275878/linq-how-to-dynamically-use-an-order-by-in-linq-but-only-if-a- variable-is-not-st – WraithNath

7

Es necesario construir una Expression Tree y pasarlo a OrderBy.
se vería algo como esto:

var param = Expression.Parameter(typeof(MyClass)); 
var expression = Expression.Lambda<Func<MyClass, PropertyType>>(
    Expression.Property(param, sortProperty), 
    param 
); 

Como alternativa, puede utilizar Dynamic LINQ, que permitirá que su código funcione como está.

+0

¿Cómo construyo un árbol de expresiones? – user564042

+0

Dado que el OP tiene "una colección de objetos CLR", sospecho que no está buscando una solución IQueryable. –

+0

@Jon: ¿Y qué? Aún necesita un árbol de expresiones para ordenar dinámicamente. Eso solo significa que deberá llamar a 'Compilar'. – SLaks

0

Para dinámico de clasificación se podría evaluar la cadena es decir, algo así como

List<MyObject> foo = new List<MyObject>(); 
string sortProperty = "LastName"; 
var result = foo.OrderBy(x => 
       { 
       if (sortProperty == "LastName") 
        return x.LastName; 
       else 
        return x.FirstName; 
       }); 

Para una solución más genérica, vea este SO: Strongly typed dynamic Linq sorting

+0

Eso es muy feo, y no funcionará para 'IQueryable'. – SLaks

+0

acordado, que no es una solución muy genérico, pero resuelve este problema particular – BrokenGlass

0

Deberá usar el reflejo para obtener el PropertyInfo y luego usarlo para crear un árbol de expresiones. Algo como esto:

var entityType = typeof(TEntity); 
var prop = entityType.GetProperty(sortProperty); 
var param = Expression.Parameter(entityType, "x"); 
var access = Expression.Lambda(Expression.MakeMemberAccess(param, prop), param); 

var ordered = (IOrderedQueryable<TEntity>) Queryable.OrderBy(
    myCollection, 
    (dynamic) access); 
10

bien, mi discusión con SLaks en sus comentarios me ha obligado a subir con una respuesta :)

Estoy asumiendo que sólo tiene que apoyar LINQ a Objetos.Aquí hay un código que necesita cantidades significativas de validación de la adición, pero hace el trabajo:

// We want the overload which doesn't take an EqualityComparer. 
private static MethodInfo OrderByMethod = typeof(Enumerable) 
    .GetMethods(BindingFlags.Public | BindingFlags.Static) 
    .Where(method => method.Name == "OrderBy" 
      && method.GetParameters().Length == 2) 
    .Single(); 

public static IOrderedEnumerable<TSource> OrderByProperty<TSource>(
    this IEnumerable<TSource> source, 
    string propertyName) 
{ 
    // TODO: Lots of validation :) 
    PropertyInfo property = typeof(TSource).GetProperty(propertyName); 
    MethodInfo getter = property.GetGetMethod(); 
    Type propType = property.PropertyType; 
    Type funcType = typeof(Func<,>).MakeGenericType(typeof(TSource), propType); 
    Delegate func = Delegate.CreateDelegate(funcType, getter); 
    MethodInfo constructedMethod = OrderByMethod.MakeGenericMethod(
     typeof(TSource), propType); 
    return (IOrderedEnumerable<TSource>) constructedMethod.Invoke(null, 
     new object[] { source, func }); 
} 

Código de ensayo:

string[] foo = new string[] { "Jon", "Holly", "Tom", "William", "Robin" }; 

foreach (string x in foo.OrderByProperty("Length")) 
{ 
    Console.WriteLine(x); 
} 

Salida:

Jon 
Tom 
Holly 
Robin 
William 

Incluso devuelve un IOrderedEnumerable<TSource> por lo que puede encadenar ThenBy cláusulas en forma normal :)

+0

¿Qué 'customers.OrderByProperty ("Apellido"). ThenByProperty ("Nombre")' 'o customers.OrderByProperty ("Apellido", "Nombre")' (donde estos son 'params string []'), ya que parece ser parte de su requerimiento. ¿Sería eso viable en esta solución? –

+0

@Anthony: Tendría que escribir más código para hacer eso, por supuesto, pero sería posible, y se parecería mucho al código anterior. –

+0

OK. Y como soy alguien que sabe lo que es Jack Squat sobre el tema, ¿hay alguna razón por la que prefiera la reflexión sobre el uso de árboles de expresión por parte de @SLaks? (En caso de que me presentan con tal requisito en el futuro.) –

1
protected void sort_grd(object sender, GridViewSortEventArgs e) 
    { 
     if (Convert.ToBoolean(ViewState["order"]) == true) 
     { 
      ViewState["order"] = false; 

     } 
     else 
     { 
      ViewState["order"] = true; 
     } 
     ViewState["SortExp"] = e.SortExpression; 
     dataBind(Convert.ToBoolean(ViewState["order"]), e.SortExpression); 
    } 

public void dataBind(bool ord, string SortExp) 
    { 
     var db = new DataClasses1DataContext(); //linq to sql class 
     var Name = from Ban in db.tbl_Names.AsEnumerable() 
         select new 
         { 
          First_Name = Ban.Banner_Name, 
          Last_Name = Ban.Banner_Project 
         }; 
     if (ord) 
     { 
      Name = BannerName.OrderBy(q => q.GetType().GetProperty(SortExp).GetValue(q, null)); 
     } 
     else 
     { 
      Name = BannerName.OrderByDescending(q => q.GetType().GetProperty(SortExp).GetValue(q, null)); 
     } 
     grdSelectColumn.DataSource = Name ; 
     grdSelectColumn.DataBind(); 
    } 
0

en realidad se puede utilizar su línea original de código

var results = myCollection.OrderBy(sortProperty); 

simplemente mediante el uso de la biblioteca System.Linq.Dynamic.

Si obtiene un error de compilación (algo así como no se puede convertir de o que no contiene una definición ...) puede que tenga que hacerlo de esta manera:

var results = myCollection.AsQueryable().OrderBy(sortProperty); 

No hay necesidad de ningún árbol de expresión o de datos Unión.