How to produce a Subquery using non-generic Lambda2 - linq

i'm very new to LINQ? please help me
How would you translate the following generic Lambda function into a lambda expression :
objects.Where(objects=>values.Contains(objects.DistrictId) );
I'm trying to create a full lambda expression without any or direct call. Something like :
var innerItem = Expression.Parameter(typeof(Objects), "objects");
var innerProperty = Expression.Property(innerItem, "ID");
var innerMethodExpression = Expression.Call(innerProperty,null);
var innerLambda = Expression.Lambda<Func<Objects, bool>>(innerMethodExpression, innerItem);
var outerItem = Expression.Parameter(typeof(int[]), "item");
var containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(int) });
var containsMethodExpression = Expression.Call(innerMethodExpression, containsMethod, innerLambda);
var outerLambda = Expression.Lambda<Func<Objects, bool>>(containsMethodExpression, outerItem);
collection = collection.AsQueryable<Objects>().Where(outerLambda);
But i can't understand what is wrong

In your example,
The Lambda function is
objects.Where(objects=>values.Contains(objects.DistrictId) );
so your lambda expression should be
Expression<Func<Objects, int>> ObjectsExp = objects=>values.Contains(objects.DistrictId);
Assume DistrictId is integer type for your out Result.

Related

Reuse function results in LINQ

I have the following query in LINQ as an example. Is it possible to save the results of the GetCalendarResources function so I wouldn't have to call it more than once? Thanks.
var query = from T in query2.AsEnumerable()
select new Event
{
resource = GetCalendarResources(T.eventID),
text = GetCalendarResources(T.eventID) + T.eventName
};
You can use the let keyword, which gives you the liberty to use its value for the next level:
var query = from T in query2.AsEnumerable()
let res= GetCalendarResources(T.eventID)
select new Event
{
resource =res,
text = res + T.eventName
};

how to format a decimal value in linq

var data = (from objData in receiptData
select new
{
ITEM_NAME=objData.ITEM_NAME,
UNIT_NAME=objData.UNIT_NAME,
PACK = objData.PACK,
RECIEVED_QTY =objData.RECIEVED_QTY.Value.ToString("0"), // ***this statement invoke error***
LPRATE = objData.LPRATE,
AMT = objData.LPRATE.Value * objData.RECIEVED_QTY.Value,
ESL = objData.ESL,
REMARKS = objData.REMARKS,
CHALLAN_NO=objData.CHALLAN_NO,
VEH_NO=objData.VEH_NO
});
Try like this
var data = (from objData in receiptData
select new
{
ITEM_NAME=objData.ITEM_NAME,
UNIT_NAME=objData.UNIT_NAME,
PACK = objData.PACK,
RECIEVED_QTY =objData.RECIEVED_QTY.Value.ToString("0.00", System.Globalization.CultureInfo.InvariantCulture),
LPRATE = objData.LPRATE,
AMT = objData.LPRATE.Value * objData.RECIEVED_QTY.Value,
ESL = objData.ESL,
REMARKS = objData.REMARKS,
CHALLAN_NO=objData.CHALLAN_NO,
VEH_NO=objData.VEH_NO
});
You should refer Decimal.ToString Method for formatting decimals.
Math.Round(objData.RECIEVED_QTY.Value,2)

Combined Lambda dynamically created

I want to build lambda expression :
Expression<Func<MyObject, bool>> predicate = PredicateBuilder.False<MyObject>();
var param = Expression.Parameter(typeof(MyObject), "f");
if (myOperator == OperateurEnum.EG)
{
var body = Expression.Equal(
Expression.PropertyOrField(param, myProperty),
Expression.Constant(myFilterValue)
);
var lambda = Expression.Lambda<Func<MyObject, bool>>(body, param);
predicate = predicate.Or(lambda);
}
else if (myOperator == OperateurEnum.CT)
{
var body = Expression.Call(
Expression.PropertyOrField(param, myProperty),
"StartsWith",
null,
Expression.Constant(myFilterValue)
);
var lambda = Expression.Lambda<Func<MyObject, bool>>(body, param);
predicate = predicate.Or(lambda);
}
else if ()
{
...
}
var myEx = Expression.Lambda<Func<MyObject, bool>>(predicate.Body, param);
Func<MyObject, bool> lambdaDelegate = myEx.Compile();
I use PredicateBuilder, but I got this error :
variable 'f' of type 'ProactisMvc.Web.ProactisWsServiceReference.Offre' referenced from scope '', but it is not defined
What's wrong ? Is there another solution ?
This last bit is wrong:
var myEx = Expression.Lambda<Func<MyObject, bool>>(predicate.Body, param);
Func<MyObject, bool> lambdaDelegate = myEx.Compile();
You are creating a new expression using predicate's body but with some other parameter. This will mean that that the parameter-expressions used inside the expression- body will not be the ones accepted by the top-level expression!
Perhaps you are confused by PredicateBuilder's magic, but all .Or is doing is combining the two predicates using an || expression, invoking the second predicate using the first predicate's parameter.
I think you just want:
var myDelegate = predicate.Compile();

Lambda Expressions: How to analyze how a delegate is stored as an Expression?

This code:
var lambda = Products.Where( p => p.name == "chair");
can be written like this code:
var propertyName = "name";
var value = "chair";
var arg = Expression.Parameter(typeof(Product), "p");
var property = typeof(Product).GetProperty(propertyName);
var comparison = Expression.Equal(
Expression.MakeMemberAccess(arg, property),
Expression.Constant(value));
var lambda = Expression.Lambda<Func<Product, bool>>(comparison, arg).Compile();
If I have any Lambda expression like this:
Products.Where( p => p.name.Contains("chair") );
How could I determine how to write the Expression like above? Is there a way to "debug" the expression tree so that I can program it manually?
EDIT:
I saw promising answers here but it didn't end up with working code. Here's their version (the error is the StartsWith method is given a non-string value):
ParameterExpression p = Expression.Parameter(typeof(Product), "p");
MethodInfo method = typeof(string).GetMethod("StartsWith",
new[] { typeof(string) });
var containsMethodExp = Expression.Call(p, method,
Expression.Constant("root"), p);
Just let the compiler do the work.
If you instead of
func<string,bool> MyLambda = p => p.name.Contains("chair");
write
Expression<func<string,bool>> MyExpression = p => p.name.Contains("chair");
Then you get a nice "MyExpression" that you can inspect in a debugger.

Dynamically Adding a GroupBy to a Lambda Expression

Ok, I'll admit that I don't entirely "get" lambda expressions and LINQ expression trees yet; a lot of what I'm doing is cutting and pasting and seeing what works. I've looked over lots of documentation, but I still haven't found the my "aha" moment yet.
With that being said...
I'm attempting to dynamically add a GroupBy expression to my Linq expression. I followed the question here:
Need help creating Linq.Expression to Enumerable.GroupBy
and tried to implement what I saw there.
First off, I've got entity classes for my database, and a table calledObjCurLocViewNormalized
I've got an method that does the initial call,
public IQueryable<ObjCurLocViewNormalized> getLocations()
{
IQueryable<ObjCurLocViewNormalized> res = (from loc in tms.ObjCurLocViewNormalized
select loc);
return res;
}
so I can call:
IQueryable<MetAmericanLinqDataModel.ObjCurLocViewNormalized> locations = american.getLocations();
No problem so far.
Now, I want to group by an arbitrary column, with a call like this:
var grouped = locations.addGroupBy(childLocationFieldName);
Right now, I have a method :
static public System.Linq.IQueryable<System.Linq.IGrouping<string, TResult>> addGroupBy<TResult>(this IQueryable<TResult> query, string columnName)
{
var providerType = query.Provider.GetType();
// Find the specific type parameter (the T in IQueryable<T>)
var iqueryableT = providerType.FindInterfaces((ty, obj) => ty.IsGenericType && ty.GetGenericTypeDefinition() == typeof(IQueryable<>), null).FirstOrDefault();
var tableType = iqueryableT.GetGenericArguments()[0];
var tableName = tableType.Name;
var data = Expression.Parameter(iqueryableT, "query");
var arg = Expression.Parameter(tableType, tableName);
var nameProperty = Expression.PropertyOrField(arg, columnName);
var lambda = Expression.Lambda<Func<TResult, string>>(nameProperty, arg);
var expression = Expression.Call(typeof(Enumerable),
"GroupBy",
new Type[] { tableType, typeof(string) },
data,
lambda);
var predicate = Expression.Lambda<Func<TResult, String>>(expression, arg); // this is the line that produces the error I describe below
var result = query.GroupBy(predicate).AsQueryable();
return result;
}
All this compiles ok, but when I run it, I get the error:
System.ArgumentException: Expression of type 'System.Collections.Generic.IEnumerable`1[System.Linq.IGrouping`2[System.String,MetAmericanLinqDataModel.ObjCurLocViewNormalized]]' cannot be used for return type 'System.String'
and the error comes from this line:
var predicate = Expression.Lambda<Func<TResult, String>>(expression, arg);
I'm copying and adapting this code from successful work I did in dynamically added Where clauses to an expression. So I'm sort of stabbing in the dark here.
If anyone out there can help to shed some light on this, Obviously posting complete working code and doing all my thinking for me would be great :), but if you could just lay out just why this is wrong, or how to wrap my head around these concepts, that would be great. If you can point to documentation that can really help be bridge the gap between the basics of lambda expressions, and building dynamic expression trees, that would be great. There's obviously big holes in my knowledge, but I think this information could be useful to others.
thanks everyone for your time, and of course if I find the answer elsewhere, I'll post it here.
Thanks again.
Don
The solution should be pretty simple:
public static IQueryable<IGrouping<TColumn, T>> DynamicGroupBy<T, TColumn>(
IQueryable<T> source, string column)
{
PropertyInfo columnProperty = typeof(T).GetProperty(column);
var sourceParm = Expression.Parameter(typeof(T), "x");
var propertyReference = Expression.Property(sourceParm, columnProperty);
var groupBySelector = Expression.Lambda<Func<T, TColumn>>(propertyReference, sourceParm);
return source.GroupBy(groupBySelector);
}
Assuming a sample class like this:
public class TestClass
{
public string TestProperty { get; set; }
}
You invoke it like this:
var list = new List<TestClass>();
var queryable = list.AsQueryable();
DynamicGroupBy<TestClass, string>(queryable, "TestProperty");
All that you need to do to make it work is the following:
static public IQueryable<IGrouping<TValue, TResult>> addGroupBy<TValue, TResult>(
this IQueryable<TResult> query, string columnName)
{
var providerType = query.Provider.GetType();
// Find the specific type parameter (the T in IQueryable<T>)
const object EmptyfilterCriteria = null;
var iqueryableT = providerType
.FindInterfaces((ty, obj) => ty.IsGenericType && ty.GetGenericTypeDefinition() == typeof(IQueryable<>), EmptyfilterCriteria)
.FirstOrDefault();
Type tableType = iqueryableT.GetGenericArguments()[0];
string tableName = tableType.Name;
ParameterExpression data = Expression.Parameter(iqueryableT, "query");
ParameterExpression arg = Expression.Parameter(tableType, tableName);
MemberExpression nameProperty = Expression.PropertyOrField(arg, columnName);
Expression<Func<TResult, TValue>> lambda = Expression.Lambda<Func<TResult, TValue>>(nameProperty, arg);
//here you already have delegate in the form of "TResult => TResult.columnName"
return query.GroupBy(lambda);
/*var expression = Expression.Call(typeof(Enumerable),
"GroupBy",
new Type[] { tableType, typeof(string) },
data,
lambda);
var predicate = Expression.Lambda<Func<TResult, String>>(expression, arg); // this is the line that produces the error I describe below
var result = query.GroupBy(predicate).AsQueryable();
return result;*/
}
And you will call you expression in the following manner:
var grouped = locations.addGroupBy<string, ObjCurLocViewNormalized>(childLocationFieldName);
First generic parameter "string" us used for saying explicilty what type of elements you a grouping on. For example you can group by "int" field and method call will be like following:
var grouped = locations.addGroupBy<int, ObjCurLocViewNormalized>(someFieldNameWithTheTypeOfInt);
Edit
Just to finish this solution your way:
//return query.GroupBy(lambda);
MethodCallExpression expression = Expression.Call(typeof (Enumerable),
"GroupBy",
new[] { typeof(TResult), typeof(TValue) },
data,
lambda);
var result = Expression.Lambda(expression, data).Compile().DynamicInvoke(query);
return ((IEnumerable<IGrouping<TValue, TResult>>)result).AsQueryable();

Resources