0

I have spent almost my whole day trying to sort PARENT based on CHILD collection property using reflection.

PS: My child collection property has lazy loading enabled.

But now, I'm stuck. Here is what I have achieved till now:

var query = myIQueryAbleVariable;

var collectionName = "MyCollectionName";
var collectionType = typeof (TEntity).GetProperty(collectionName).PropertyType;
var collectionItemType = collectionType.GetGenericArguments()[0];

var propertyName = "CollectionPropertyName";
var propertyInfo = collectionItemType.GetProperty(propertyName);

// Create a parameter to use for both of the expression bodies.
var parameter = Expression.Parameter(collectionItemType, "x");
Expression columnExpr = Expression.Property(parameter, propertyInfo);
var lambda = Expression.Lambda(columnExpr, parameter);

var selectMethod = typeof(Enumerable)
   .GetMethods(BindingFlags.Static | BindingFlags.Public)
   .FirstOrDefault(
      mi =>
         mi.Name == "Select" &&
         mi.GetParameters()[1].ParameterType.GetGenericArguments().Count() == 2);

// we need to specialize it 
selectMethod = selectMethod.MakeGenericMethod(collectionItemType, lambda.GetType());

query = query.OrderBy(p =>
   selectMethod.Invoke(null,
      new[] { p.GetType().GetProperty(collectionName).GetValue(p), lambda }));

It fetches data from db. But when it seems to process de order by (which is processed in memory), I get:

System.ArgumentException: Object of type 'System.Linq.Expressions.Expression`1[System.Func`2[Measure,System.Int32]]' cannot be converted to type 'System.Func`2[Measure,System.Linq.Expressions.Expression`1[System.Func`2[Measure,System.Int32]]]'.
   at System.RuntimeType.TryChangeType(Object value, Binder binder, CultureInfo culture, Boolean needsSpecialCast)
   at System.RuntimeType.CheckValue(Object value, Binder binder, CultureInfo culture, BindingFlags invokeAttr)
   at System.Reflection.MethodBase.CheckArguments(Object[] parameters, Binder binder, BindingFlags invokeAttr, CultureInfo culture, Signature sig)
   at System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
   at lambda_method(Closure , Measure )
   at System.Linq.EnumerableSorter`2.ComputeKeys(TElement[] elements, Int32 count)
   at System.Linq.EnumerableSorter`1.Sort(TElement[] elements, Int32 count)
   at System.Linq.OrderedEnumerable`1.<GetEnumerator>d__0.MoveNext()
   at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
Marco Alves
  • 2,645
  • 3
  • 21
  • 33
  • Can you provide an example of a static query that resembles what your dynamic query is trying to build? – Servy May 22 '14 at 19:57
  • Its completely dynamic. But let's try it: var myIQueryAbleVariable= DbContext.Measure.Where(p => p.Market == "MarketGuid") .It's useful to think about an IQueryable or IEnumerable object. – Marco Alves May 22 '14 at 20:00
  • Your query contains an `OrderBy` call and a `Select`. I see no `Where` in there at all. What I mean is that dymaically you're trying to construct a call to `OrderBy` that also uses a projection. If you were to statically hard code that one operation, what would it look like, realizing of course that for your project you need to be able to construct that query dynamically. – Servy May 22 '14 at 20:04
  • I assume `query` is an IQueryable from LINQ to Entities. Which means you can't use `Invoke`. You may want to use [this](http://stackoverflow.com/a/233505/861716) in stead. – Gert Arnold May 22 '14 at 20:04
  • @GertArnold In that example, I haven't seen anywhere how to sort based on child collection property. Example: MyInstance has a collection property of type T. T has a property SortOrder. I want to get all myinstances, ordered by T.SortOrder. – Marco Alves May 22 '14 at 20:08
  • @MarcoAlves So you have two objects, A and B. A has a collection of children with values {2,5}, B has a collection of children with values {7,1}, which comes first, A or B? How do you determine which of the two sequences is larger? The one with the largest sum, the one with the largest max value, the one with the smallest min value? – Servy May 22 '14 at 20:10
  • @Servy This is how the query would be non-dynamic: var qry = new Service().GetAllMeasures() .SelectResponse.ListOfItems.OrderBy(o => o.MarketMeasures.Select(x => x.Priority)); – Marco Alves May 22 '14 at 20:18
  • @MarcoAlves Okay, now to my last comment of how the heck you plan to sort your measures based on a list of priorities. Is the first measure the measure that has a collection with the highest sum of priorities, the one with the highest max priorities, or what? – Servy May 22 '14 at 20:20
  • @Servy Every measure returned from DB has a collection of type MarketMeasure.Every measure returned has that collection populated (via Lazy Loading). MarketMeasure has a property name called Priority, which states the priority for that measure in that MarketMeasure. – Marco Alves May 22 '14 at 20:23
  • @MarcoAlves Yes, I got that. That's very clear. You want to order your measures based on the priorities of the values inside MarketMeasure, according to your query. That doesn't really make sense. How do you determine whether a given measure is greater than or less than another measure just based on their collection of priorities? – Servy May 22 '14 at 20:24
  • @Servy BTW: I have just found out that var qry = new Service().GetAllMeasures() .SelectResponse.ListOfItems.OrderBy(o => o.MarketMeasures.Select(x => x.Priority)); ISN'T working. Is that really possible? I mean, what I am trying to achieve. – Marco Alves May 22 '14 at 20:26
  • @Servy I don't know, actually. But this is what I am trying to achieve. Is that possible? – Marco Alves May 22 '14 at 20:28
  • 2
    @MarcoAlves I know it's not working. I'm explaining to you exactly why it won't work (and indicentally this is why I suggest creating all of your query's statically first, before trying to create them dynamically). You're trying to sort based on a sequence of integers. Which is greater, the sequence {1, 7} or the sequence {2,5}? I have no idea. SQL has no idea. Your query provider has no idea. Do you want to sort based on the *sum* of all of the priorities, so that {1,7} is larger, for example? – Servy May 22 '14 at 20:28
  • @MarcoAlves If *you* don't know which should be greater, than how do you expect the query provider to know? There is no "natural" way to determine whether two sequences of values is greater, even if you know how to compare any two of the values in the sequence. – Servy May 22 '14 at 20:29
  • @Servy So, as I understand, that isn't possible and I need some "hand programming". Thank you. At least I hope this question help others no waste time as I have wasted. – Marco Alves May 22 '14 at 20:32

1 Answers1

0

Based on @Servy comments, I end up using IComparer.

Marco Alves
  • 2,645
  • 3
  • 21
  • 33