J'ai une extension dans mon projet qui me permet de trier un IEnumerable
avec une chaîne, de sorte que le tri peut être fait plus dynamiquement.Construire dynamiquement une arborescence d'expression pour le tri
Donc, si j'ai ces modèles:
public MyModel
{
public int Id {get; set;}
public string RecordName {get; set;}
public ChildModel MyChild {get; set;}
}
public ChildModel
{
public int ChildModelId {get; set;}
public DateTime SavedDate {get; set;}
}
je peux trier deux façons:
myList.OrderByField("RecordName ");
myList.OrderByField("MyChild.SavedDate");
Cependant, si mon objet a une propriété ICollection
, comme ICollection<ChildModel> MyChildren
je peux coder en dur mon genre comme ceci:
myList
.OrderBy(m => m.MyChildren
.OrderByDescending(c => c.SavedDate).FirstOrDefault().SavedDate);
Et obtenez ce que je veux.
Ma question est, comment puis-je mettre à jour ma méthode d'extension pour permettre d'obtenir les mêmes résultats avec ce:
myList.OrderByField("MyChildren.SavedDate");
Voici mon extension actuelle:
public static class MkpExtensions
{
public static IEnumerable<T> OrderByField<T>(this IEnumerable<T> list, string sortExpression)
{
sortExpression += "";
string[] parts = sortExpression.Split(' ');
bool descending = false;
string fullProperty = "";
if (parts.Length > 0 && parts[0] != "")
{
fullProperty = parts[0];
if (parts.Length > 1)
{
descending = parts[1].ToLower().Contains("esc");
}
ParameterExpression inputParameter = Expression.Parameter(typeof(T), "p");
Expression propertyGetter = inputParameter;
foreach (string propertyPart in fullProperty.Split('.'))
{
PropertyInfo prop = propertyGetter.Type.GetProperty(propertyPart);
if (prop == null)
throw new Exception("No property '" + fullProperty + "' in + " + propertyGetter.Type.Name + "'");
propertyGetter = Expression.Property(propertyGetter, prop);
}
Expression conversion = Expression.Convert(propertyGetter, typeof(object));
var getter = Expression.Lambda<Func<T, object>>(conversion, inputParameter).Compile();
if (descending)
return list.OrderByDescending(getter);
else
return list.OrderBy(getter);
}
return list;
}
}
je pensais de vérifier le type de prop
et faire une déclaration if... else
, mais je ne suis pas sûr.
Peut-être quelque chose comme ceci:
foreach (string propertyPart in fullProperty.Split('.'))
{
var checkIfCollection = propertyGetter.Type.GetInterfaces()//(typeof (ICollection<>).FullName);
.Any(x => x.IsGenericType &&
(x.GetGenericTypeDefinition() == typeof(ICollection<>) || x.GetGenericTypeDefinition() == typeof(IEnumerable<>)));
if (checkIfCollection)
{
// Can I get this to do something like
// myList.OrderBy(m => m.MyChildren.Max(c => c.SavedDate));
// So far, I can get the propertyGetter type, and the type of the elements:
var pgType = propertyGetter.Type;
var childType = pgType.GetGenericArguments().Single();
// Now I want to build the expression tree to get the max
Expression left =
Expression.Call(propertyGetter, pgType.GetMethod("Max", System.Type.EmptyTypes));
// But pgType.GetMethod isn't working
}
else
{
PropertyInfo prop = propertyGetter.Type.GetProperty(propertyPart);
if (prop == null)
throw new Exception("No property '" + fullProperty + "' in + " + propertyGetter.Type.Name + "'");
propertyGetter = Expression.Property(propertyGetter, prop);
}
}
Pour votre information, vous pouvez simplifier la commande imbriquée codées en dur comme ceci: 'myList.OrderBy (m => m.MyChildren.Max (c => c.SavedDate)); En parlant de cela, quand votre programme voit '' MyChildren.SavedDate "', comment est-il supposé savoir trier ascendant par MyChildren, mais descendant par SavedDate? – StriplingWarrior
Max travaillera pour les enregistrements enfants. Dans ma chaîne, j'utilise en fait 'MyChildren.SavedDate asc' ou' MyChildren.SavedDate desc' et je le change en conséquence. '.SavedDate' suppose le plus récent ... pour l'instant. ;) –