In System.Linq.Dynamic, there are a few methods to form Select, Where and other Linq statements dynamically. But there is no for SelectMany.
The method for Select is as the following:
public static IQueryable Select(this IQueryable source, string selector, params object[] values)
{
if (source == null) throw new ArgumentNullException("source");
if (selector == null) throw new ArgumentNullException("selector");
LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, null, selector, values);
IQueryable result = source.Provider.CreateQuery(
Expression.Call(
typeof(Queryable), "Select",
new Type[] { source.ElementType, lambda.Body.Type },
source.Expression, Expression.Quote(lambda)));
return result;
}
I tried to modify the above code, after hours working, I couldn't find a way out.
Any suggestions are welcome.
Ying
Already implemented this one for our project, let me know if it works for you!
public static IQueryable SelectMany(this IQueryable source, string selector, params object[] values)
{
if (source == null)
throw new ArgumentNullException("source");
if (selector == null)
throw new ArgumentNullException("selector");
// Parse the lambda
LambdaExpression lambda =
DynamicExpression.ParseLambda(source.ElementType, null, selector, values);
// Fix lambda by recreating to be of correct Func<> type in case
// the expression parsed to something other than IEnumerable<T>.
// For instance, a expression evaluating to List<T> would result
// in a lambda of type Func<T, List<T>> when we need one of type
// an Func<T, IEnumerable<T> in order to call SelectMany().
Type inputType = source.Expression.Type.GetGenericArguments()[0];
Type resultType = lambda.Body.Type.GetGenericArguments()[0];
Type enumerableType = typeof(IEnumerable<>).MakeGenericType(resultType);
Type delegateType = typeof(Func<,>).MakeGenericType(inputType, enumerableType);
lambda = Expression.Lambda(delegateType, lambda.Body, lambda.Parameters);
// Create the new query
return source.Provider.CreateQuery(
Expression.Call(
typeof(Queryable), "SelectMany",
new Type[] { source.ElementType, resultType },
source.Expression, Expression.Quote(lambda)));
}
I have added another SelectMany that retuns an AnonymousType.
public static IQueryable SelectMany(this IQueryable source, string selector, string resultsSelector, params object[] values)
{
if (source == null)
throw new ArgumentNullException("source");
if (selector == null)
throw new ArgumentNullException("selector");
// Parse the lambda
LambdaExpression lambda =
DynamicExpression.ParseLambda(source.ElementType, null, selector, values);
// Fix lambda by recreating to be of correct Func<> type in case
// the expression parsed to something other than IEnumerable<T>.
// For instance, a expression evaluating to List<T> would result
// in a lambda of type Func<T, List<T>> when we need one of type
// an Func<T, IEnumerable<T> in order to call SelectMany().
Type inputType = source.Expression.Type.GetGenericArguments()[0];
Type resultType = lambda.Body.Type.GetGenericArguments()[0];
Type enumerableType = typeof(IEnumerable<>).MakeGenericType(resultType);
Type delegateType = typeof(Func<,>).MakeGenericType(inputType, enumerableType);
lambda = Expression.Lambda(delegateType, lambda.Body, lambda.Parameters);
ParameterExpression[] parameters = new ParameterExpression[] {
Expression.Parameter(source.ElementType, "outer"), Expression.Parameter(resultType, "inner") };
LambdaExpression resultsSelectorLambda = DynamicExpression.ParseLambda(parameters, null, resultsSelector, values);
// Create the new query
return source.Provider.CreateQuery(
Expression.Call(
typeof(Queryable), "SelectMany",
new Type[] { source.ElementType /*TSource*/, /*,TCollection*/resultType /*TResult*/, resultsSelectorLambda.Body.Type},
source.Expression, Expression.Quote(lambda), Expression.Quote(resultsSelectorLambda)));
}
I still need to figure out how to do the following using Dynamic, the goal is to return a new result object.
var customerandorderflat = db.Customers
.SelectMany(c => c.Orders.SelectMany(o => o.Order_Details,
(ord, orddetail) => new
{
OrderID = ord.OrderID,
UnitPrice = orddetail.UnitPrice
}).DefaultIfEmpty(),
(cus, ord) => new
{
CustomerId = cus.CustomerID,
CompanyName = cus.CompanyName,
OrderId = ord.OrderID == null ? -1 : ord.OrderID,
UnitPrice = ord.UnitPrice
});
I am using the NWDB when I try:
var customerandorderquery = db.Customers .SelectMany(c => c.Orders.DefaultIfEmpty()).Select("new(CustomerId, CompanyName, OrderId)");
I get an error because CompanyName
is in Customers
not Orders
. So it is not seeing the combination of the two objects.
When I do:
.SelectMany(c => c.Orders.DefaultIfEmpty(), (cus, ord) => new { CustomerId = cus.CustomerID, OrderId = ord.OrderID == null ? -1 : ord.OrderID });
It returns the desired result.
来源:https://stackoverflow.com/questions/2983134/to-call-selectmany-dynamically-in-the-way-of-system-linq-dynamic