This project is read-only.

Question about the full string query

Nov 26, 2013 at 5:01 PM
Edited Nov 26, 2013 at 5:02 PM
Hello,

Your solution is very powerfull, thanks a lot for sharing !

Here is my question:

I have seen in your code that you are able to write full queries in string, for example:
string query = "Products.Where(Product => (Product.CategoryID = 3 And Product.UnitPrice > 3)).OrderBy(Product=>(Product.SupplierID))";
var expression = System.Linq.Dynamic.DynamicExpression.Parse(typeof(IQueryable<Product>), query, new[] { externals });
I am wondering if it is possible to use other methods like "join" or "selectmany" that take as parameters a IEnumerable<>, simple lambdas and doubled parameters lambda for the selection:
(query1, query2) => new ( query1.Column as Column1, query2.Column as Column2 )
The lambda expression parser does not recognize the complex lambda expression (with two parameters) because it starts with a '('

Maybe I should add a new keyword 'somethingLambda' like this?:
somethingLambda(query1, query2) => new ( query1.Column as Column1, query2.Column as Column2 )
I can't manage to make it work for the "Join" or "SelectMany" methods.

I have added a another element in the dictionnary externals (my second list), but I can't manage to use the methods in my string query.

Here is the Join extension I have found, it works very well when called alone :
http://stackoverflow.com/questions/389094/how-to-create-a-dynamic-linq-join-extension-method

Is it possible to write it in full string query?
My problem is to pass the second IEnumerable to the externals Dictionary and to know if it is possible to call the Join method like this?
var externals = new Dictionary<string, object>();
externals.Add("Products", queryableData);
externals.Add("Companies", ret);

string query = "Products.Join(Companies, l1 => l1.a, l2 => l2.a, (l1, l2) => new (l1A = l1.a, l2A = l2.a, l2B = l2.b))";
var expression = System.Linq.Dynamic.DynamicExpression.Parse(typeof(IQueryable), query, new[] { externals });
var result = queryableData.Provider.CreateQuery(expression);
As you can see, the two parameters l1 and l2 in '(l1, l2) => new (l1A = l1.a, l2A = l2.a, l2B = l2.b))' are created statically in the Join method (called "inner" and "outer")

Because the DynamicExpression.Parse() method can't manage to read two parameters lambda expression?

Thank you for your help :)
Dec 9, 2013 at 9:33 AM
Hello,

I finally found how to write my SelectMany method.
It was a problem type.

(Parsing several parameters of a lambda expression seems to be very difficult).
I also added a "Select" method in the IEnumerableSignatures
(same logic as the 'Contains', but I constructed a new Expression.Call expression on the Select method)

It is working on my case but maybe it will not work for all cases
interface IEnumerableSignatures
        {
            void Select(object selector);
            void Contains(object selector);
And my SelectMany (taking one IEnumerable)
public static IQueryable SelectMany(this IQueryable source, IEnumerable collection, string collectionSelector, string resultSelector, params object[] values)
        {
            if (source == null) throw new ArgumentNullException("source");
            if (collection == null) throw new ArgumentNullException("collection");
            if (collectionSelector == null) throw new ArgumentNullException("collectionSelector");
            if (resultSelector == null) throw new ArgumentNullException("resultSelector");

            ParameterExpression[] parameters = new ParameterExpression[] { Expression.Parameter(source.ElementType, "") };

            // obtient l'expression lambda de la collection
            LambdaExpression selectorLambdaCollection = DynamicExpression.ParseLambda(parameters, null, collectionSelector, collection.AsQueryable());

            // Convertit le type collection en IEnumerable<TCollection>, dans les cas où celui-ci serait d'un autre type 
            // (en général il est IQueryable)
            Type iEnumerableOfCollectionElement = typeof(IEnumerable<>).MakeGenericType(selectorLambdaCollection.ReturnType.GetGenericArguments()[0]);
            Type funcOfIEnumerableOfCollectionElement = typeof(Func<,>).MakeGenericType(source.ElementType, iEnumerableOfCollectionElement);

            // Conversion en type attendu par la méthode SelectMany (Func<T,IEnumerable<TCollection>>)
            selectorLambdaCollection = Expression.Lambda(funcOfIEnumerableOfCollectionElement, selectorLambdaCollection.Body, selectorLambdaCollection.Parameters);

            parameters = new ParameterExpression[] { 
                                        Expression.Parameter(source.ElementType, "Ref"), 
                                        Expression.Parameter(selectorLambdaCollection.ReturnType.GetGenericArguments()[0], "Comp") };

            // obtient l'expression lambda de la partie select
            LambdaExpression lambda = DynamicExpression.ParseLambda(parameters, null, resultSelector, values);

            return source.AsQueryable().Provider.CreateQuery(
                Expression.Call(
                    typeof(Queryable), "SelectMany",
                new Type[] { source.ElementType, lambda.Parameters[1].Type, lambda.Body.Type },//TSource, TCollection, TResult
                  new Expression[] { source.Expression, Expression.Quote(selectorLambdaCollection), Expression.Quote(lambda) }));
        }
Marked as answer by tranmarco on 12/9/2013 at 1:35 AM