2016-05-03 4 views
0

J'ai regardé les autres versions de SO de cette question mais il semble que la sortie d'une méthode fonctionne pour d'autres. Je ne suis pas sûr de ce que je fais mal ici. Je suis nouveau à la partie du bâtiment d'expression de Linq.LambdaExpression à l'expression via les extensions Méthode

Ma méthode extensions est la suivante:

void Main() 
{ 
    var people = LoadData().AsQueryable(); 

    var expression = people.PropertySelector<Person>("LastName"); 
    expression.Should().BeOfType(typeof(Expression<Func<Person, object>>)); 

    var result = people.OrderBy(expression); 
} 

public static class Extensions 
{ 
    public static Expression<Func<T, object>> PropertySelector<T>(this IEnumerable<T> collection, string propertyName) 
    { 
     if (string.IsNullOrWhiteSpace(propertyName)) 
     { 
      throw new ArgumentException(nameof(propertyName)); 
     } 

     var properties = typeof(T).GetProperties(); 
     if (!properties.Any(p => p.Name == propertyName)) 
     { 
      throw new ObjectNotFoundException($"Property: {propertyName} not found for type [{typeof(T).Name}]"); 
     } 

     var propertyInfo = properties.Single(p => p.Name == propertyName); 

     var alias = Expression.Parameter(typeof(T), "_"); 
     var property = Expression.Property(alias, propertyInfo); 
     var funcType = typeof(Func<,>).MakeGenericType(typeof(T), propertyInfo.PropertyType); 
     var lambda = Expression.Lambda(funcType, property, alias); 

     return (Expression<Func<T, object>>)lambda; 
    } 
} 


#region 

private Random rand = new Random(); 

// Define other methods and classes here 
public class Person 
{ 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    public int Age { get; set; } 
} 

public IEnumerable<Person> LoadData() 
{ 
    IList<Person> people = new List<Person>(); 
    for (var i = 0; i < 15; i++) 
    { 
     people.Add(new Person 
     { 
      FirstName = $"FirstName {i}", 
      LastName = $"LastName {i}", 
      Age = rand.Next(1, 100) 
     }); 
    } 
    return people; 
} 

#endregion 

Je reçois une exception sur le rendement au cours de la distribution. À ce stade T est de type Person et object est un string. Mon lambda.GetType() rapporte que c'est de type Expression<Func<Person, string>> L'exception est:

Unable to cast object of type 'System.Linq.Expressions.Expression`1[System.Func`2[UserQuery+Person,System.String]]' to type 'System.Linq.Expressions.Expression`1[System.Func`2[UserQuery+Person,System.Object]]'. 

Et mon casting est incorrect? Merci.

EDIT: J'ai mis à jour avec mon code complet avec lequel je joue en LinqPad. Je suis juste en train d'essayer de comprendre s'il existe un moyen facile de générer une expression lambda en transmettant le nom de la propriété. Je faisais quelque chose comme ça auparavant mais je faisais juste un changement de nom de propriété en utilisant ensuite la syntaxe lambda pour créer dynamiquement la requête OrderBy.

Ceci est strictement moi essayant d'apprendre comment les méthodes statiques d'expression peuvent être utilisées pour obtenir le même résultat que l'exemple ci-dessous. J'essaie d'imiter le ci-dessous via la méthode d'extension. Mais ça ne doit pas être comme ça. C'était plus facile d'essayer en plongeant dans LinqPad.

Expression<Func<Loan, object>> sortExpression; 

    switch (propertyFilter) 
    { 
     case "Age": 
      sortExpression = (l => l.Age); 
      break; 
     case "LastName": 
      sortExpression = (l => l.LastName); 
      break; 
     default: 
      sortExpression = (l => l.FirstName); 
      break; 
    } 

    var sortedLoans = loans.AsQueryable().OrderBy(sortExpression); 
    sortedLoans.Dump("Filtered Property Result"); 
+1

Les classes sont invariantes. Donc, vous ne pouvez pas lancer l'expression à l'expression même si la conversion de TFunc1 en TFunc2 est autorisée. – PetSerAl

+0

Qu'essayez-vous d'accomplir avec cette méthode? J'essaie de comprendre pourquoi vous auriez besoin du Func . Essayez-vous de filtrer en fonction d'une propriété spécifique? – bhmahler

+0

Vous pouvez convertir un objet en objet via 'Expression.Convert (propriété, typeof (objet))'. –

Répondre

1

Votre code crée un Func<UserQuery, String> parce que vous geting son type intrinsèque avec

var propertyInfo = properties.Single(p => p.Name == propertyName); 
var funcType = typeof(Func<,>).MakeGenericType(typeof(T), propertyInfo.PropertyType); 

Si vous voulez retourner un Func<T, object> puis créer un Func<T, object>, pas Func<T, (reflected property type)>, sinon la meilleure solution est pour utiliser un Func<TOut, TIn> et créer une fonction totalement générique.

1

Pour garder la signature de la méthode actuelle, vous pouvez le faire:

 var funcType = typeof(Func<,>).MakeGenericType(typeof(T), typeof(object)); 
     var typeAs = Expression.TypeAs(property, typeof(object)); 
     var lambda = Expression.Lambda(funcType, typeAs, alias); 

et une meilleure façon est de changer votre méthode pour

public static Expression<Func<T, Tout>> PropertySelector<T, Tout>(this IEnumerable<T> collection, string propertyName) 
    { 
     if (string.IsNullOrWhiteSpace(propertyName)) 
     { 
      throw new ArgumentException(nameof(propertyName)); 
     } 

     var properties = typeof(T).GetProperties(); 
     if (!properties.Any(p => p.Name == propertyName)) 
     { 
      throw new ObjectNotFoundException($"Property: {propertyName} not found for type [{typeof(T).Name}]"); 
     } 

     var propertyInfo = properties.Single(p => p.Name == propertyName); 

     var alias = Expression.Parameter(typeof(T), "_"); 
     var property = Expression.Property(alias, propertyInfo); 
     var funcType = typeof(Func<,>).MakeGenericType(typeof(T), propertyInfo.PropertyType); 
     var lambda = Expression.Lambda(funcType, property, alias); 

     return (Expression<Func<T, Tout>>)lambda; 
    } 

et l'appeler avec

 var expression = people.PropertySelector<Person, string>("LastName"); 
0

Je suis le résultat que je voulais. Après les commentaires de @Gusman, @IvanStoev et @PetSerAl je l'ai eu pour fonctionner. Je peux continuer à explorer et à apprendre à nouveau. Merci beaucoup. Le résultat final était de modéliser le Propertytype.

public static Expression<Func<T, TPropertyType>> PropertySelector<T, TPropertyType>(this IEnumerable<T> collection, string propertyName) 
    { 
     if (string.IsNullOrWhiteSpace(propertyName)) 
     { 
      throw new ArgumentException(nameof(propertyName)); 
     } 

     var properties = typeof(T).GetProperties(); 
     if (!properties.Any(p => p.Name == propertyName)) 
     { 
      throw new ObjectNotFoundException($"Property: {propertyName} not found for type [{typeof(T).Name}]"); 
     } 

     var propertyInfo = properties.Single(p => p.Name == propertyName); 

     var alias = Expression.Parameter(typeof(T), "_"); 
     var property = Expression.Property(alias, propertyInfo); 
     var funcType = typeof(Func<,>).MakeGenericType(typeof(T), typeof(TPropertyType)); 
     var lambda = Expression.Lambda(funcType, property, alias); 

     return (Expression<Func<T, TPropertyType>>)lambda; 
    }