Group by Dynamic Aggregate - linq

In the code below I want to replace x.BaseSalary with any other property whose name is stored in feildtoretrive:
var feildtoretrive = "BaseSalary"
var groupcal = db.Employees.GroupBy(x=>x.Region)
.Select(group => new {
Key = group.Key,
Avg = group.Average(x => x.BaseSalary)
})
.ToList();

You need to
create a MemberExpression that accesses that member of an instance
compile a lambda expression that returns that member for a passed instance parameter
(I assume that the elements in Employees are of type Employee)
// the parameter expression for the lambda (your 'x')
var parameter = Expression.Parameter(typeof(Employee));
// the property access expression (your 'x.BaseSalary' or 'x.<feildtoretrive')
var propAccess = Expression.PropertyOrField(parameter, feildtoretrive);
// the build-together and compiled lambda
var expression = (Expression<Func<Employee, int?>>)Expression.Lambda(propAccess, parameter);
You can now use lambda for the x.Average call:
new { Key = group.Key, Avg = group.Average(lambda) }
A caveat: This only works now for members of type int?. I'm lacking a little experience on how to do this more type independent, but what types can you calculate an average over? But if there are int or double members, another cast expression maybe necessary.
EDIT: (changed the return type to int?). According to Ivan Stoev's comment on my follow up question you could try this:
new { Key = group.Key, Avg = group.AsQueryable().Average(expression) }
EF6 should recognize the call to AsQueryable() and then use the correct Average method (note that I use the expression as argument instead of the lambda). EF Core and linq2Sql won't work with that.

Related

Using Linq to Determine if List of Uris is Base of another Uri

I would like to build a method that determines if a given URL is a child of one of a number of URL's in a List. I thought of approaching this using Linq but the syntax seems beyond my understanding. Here is what I have attempted and I would expect isChild == true.
List<Uri> ProductionUriList = new List<Uri>(){
new Uri(#"https://my.contoso.com/sites/Engineering",UriKind.Absolute),
new Uri(#"https://my.contoso.com/sites/APAC",UriKind.Absolute),
new Uri(#"https://my.contoso.com/sites/China",UriKind.Absolute),
new Uri(#"https://my.contoso.com/sites/EMEA",UriKind.Absolute),
new Uri(#"https://my.contoso.com/sites/India",UriKind.Absolute),
new Uri(#"https://my.contoso.com/sites/Mexico",UriKind.Absolute),
new Uri(#"https://my.contoso.com/sites/SamCam",UriKind.Absolute),
new Uri(#"https://my.contoso.com/sites/USA",UriKind.Absolute),
};
var isChild =
ProductionUriList.SelectMany (p => p.IsBaseOf(new Uri("https://my.contoso.com/sites/China/Site1",UriKind.Absolute)));
The runtime error says:
The type arguments for method
'System.Linq.Enumerable.SelectMany(System.Collections.Generic.IEnumerable,
System.Func>)'
cannot be inferred from the usage. Try specifying the type arguments
explicitly.
If you just want to check for a bool condition on a set you can use the any operator:
var isChild = ProductionUriList.Any(p => p.IsBaseOf(new Uri("https://my.contoso.com/sites/China/Site1", UriKind.Absolute)));
Concerning your error: The selectmany operator expects a delegate that returns IEnumerable which you do not supply. You are mixing up select and selectmany. If you choose select as the linq operator you could do a count > 0 on the result which would give the same result as using the any operator:
var isChild = ProductionUriList.Select(p => p.IsBaseOf(new Uri("https://my.contoso.com/sites/China/Site1", UriKind.Absolute)).Count > 0;
To determine if the uri is a child of one or more:
var isChild = ProductionUriList.Any(p => p.IsBaseOf(newUri("https://my.contoso.com/sites/China/Site1",UriKind.Absolute)));
To determine if the uri is a child of exactly one:
var isChild = ProductionUriList.Count(p => p.IsBaseOf(newUri("https://my.contoso.com/sites/China/Site1",UriKind.Absolute))) == 1;

How to bind a value to a variable in a programmatically generated Linq Expression

I am trying to programmatically generate an expression tree (that will eventually get used in Linq-to-entity framework).
I can get the query to work just fine with one exception - it does not parametrize the query - which I want for Sql Server query plan reuse.
I read that in order for the generated sql to be parametrized, the expression needs to compare based on a variable. However, I cannot figure out how to assign the value to the variable in the expression tree. If I just use Expression.Constantit works (but is not parametrized).
So basically:
public Expression<Func<T, bool>> FooEquals<T>(
Expression<Func<T, int>> propertyExpression, int value)
{
ParameterExpression param = propertyExpression.Parameters.Single();
int targetValueForEqualityComparison = 9;
//There's some "special sauce" here which is why I don't
//use propertyExpression directly
var body = Expression.Property(param, GetPropertyName(propertyExpression));
//If I just use Expression.Constant, it works, but doesn't parametrize.
//var equalExpression = ParameterExpression.Equal(body,
// Expression.Constant(targetValueForEqualityComparison, typeof(int)));
var variable = Expression
.Variable(typeof(int), "targetValueForEqualityComparison");
var assigned = Expression.Assign(variable,
Expression.Constant(targetValueForEqualityComparison, typeof(int)));
//throws InvalidOperaitonException: "The parameter was not bound in the
//specified Linq to Entities query expression
var equalExpression = ParameterExpression.Equal(body, variable);
//throws NotSupportedException: "Unknown LINQ expression of type 'Assign'.
var equalExpression = ParameterExpression.Equal(body, assigned);
return Expression.Lambda<Func<T, bool>>(equalExpression, param);
}
How do I properly bind a value to the variable expression so that Linq-to-EntityFramework will parametrize the query?
I went ahead and tried it out because I was curious. The following seem to result in identical SQL, at least when used with Linq-to-SQL (which LINQPad does more readily than EF). I'd imagine it should work just the same with EF though.
Seems like a pretty convoluted way to just pass an integer in, but because this is what the compiler generates for a plain lambda function, I guess this is what the SQL generator looks for.
// Given this class, which is equivalent to the compiler-generated class
class Holder {
public int AnInteger;
}
int id = 1;
// You get the same SQL with a plain lambda function
var query = db.Items.Where(i => i.Id == id);
// or with a handwritten expression:
var arg = Expression.Parameter(typeof(Item), "i");
var paramHolder = new Holder { AnInteger = id };
// essentially, (i) => i.Id == paramHolder.AnInteger
var lambda = Expression.Lambda<Func<Item, bool>>(
Expression.Equal(
Expression.Property(arg, "Id"),
Expression.Field(
Expression.Constant(paramHolder), "AnInteger")),
arg);
// the SQL this translates to is equivalent to that of the first query
var query2 = db.Items.Where(lambda);

Linq with DynamicObject

I have List where MyType : DynamicObject. The reason for MyType inheriting from DynamicObject, is that I need a type that can contain unknown number of properties.
It all works fine until I need to filter List. Is there a way I can do a linq that will do something like this:
return all items where any of the properties is empty string or white space?
(from the comment) can I do above linq query with List?
Yes, here is how you can do it with ExpandoObject:
var list = new List<ExpandoObject>();
dynamic e1 = new ExpandoObject();
e1.a = null;
e1.b = "";
dynamic e2 = new ExpandoObject();
e2.x = "xxx";
e2.y = 123;
list.Add(e1);
list.Add(e2);
var res = list.Where(
item => item.Any(p => p.Value == null || (p.Value is string && string.IsNullOrEmpty((string)p.Value)))
);
The ExpandoObject presents an interface that lets you enumerate its property-value pairs as if they were in a dictionary, making the process of checking them a lot simpler.
Well as long as each object's properties are not unknown internally to themselves you could do it.
There isn't a great generic way to test all the properties of a dynamic object, if you don't have control over the DynamicObject you hope the implementer implemented GetDynamicMemberNames() and you can use the nuget package ImpromptuInterface's methods for getting the property names and dynamically invoking those names.
return allItems.Where(x=> Impromptu.GetMemberNames(x, dynamicOnly:true)
.Any(y=>String.IsNullOrWhiteSpace(Impromptu.InvokeGet(x,y));
Otherwise, since it's your own type MyType you can add your own method that can see internal accounting for those member values.
return allItems.Where(x => x.MyValues.Any(y=>String.IsNullOrWhitespace(x));

Var vs IEnumerable when it comes to Entity Framework

If I were to use IEnumerable instead of var in the code example below, will the SQL be generated only during the execution of the foreach statement? Or will it execute as an when the Linq statements are evaluated?
var query = db.Customers.Where (c => c.Age > 18);
query = query.Where (c => c.State == "CO");
var result = query.Select (c => c.Name);
foreach (string name in result) // Only now is the query executed!
Console.WriteLine (name);
Another example:
IEnumerable<Order> query = db.Orders.Where(o => o.Amount > 1000);
int orderCount = query.Count();
Would it be better to use var (or IQueryable) as it would be executed a select count(*)... when .Count() is executed or would it be exactly same with the IEnumerable code shown above?
It would make no difference. var is just syntactic sugar. If you hover over var, you will see what type C# thinks your query is.
From http://msdn.microsoft.com/en-us/library/bb383973.aspx
Beginning in Visual C# 3.0, variables that are declared at method scope can have an implicit type var. An implicitly typed local variable is strongly typed just as if you had declared the type yourself, but the compiler determines the type. The following two declarations of i are functionally equivalent:
var i = 10; // implicitly typed
int i = 10; //explicitly typed
If you want to perform actions on your query that SQL wouldn't know what to do with, such as a method defined in your class, then you could use AsEnumerable().
For example:
var query = db.Customers
.Where(c => c.Age > 18)
.AsEnumerable()
.Select(c => GetCustomerViewModel());
//put definition of GetCustomerViewModel() somewhere in the same class
Update
As Omar mentioned, if you want your query to be executed immediately (rather than deferred), use ToList(). This will immediately enumerate the IQueryable/IEnumerable and fill the list with the actual data from the database.
In general, the SQL is generated when GetEnumerator is called on the IQueryable.
In your example, there is a subtle difference that you may want to consider. Let's take your original example:
var query = db.Customers.Where (c => c.Age > 18);
query = query.Where (c => c.State == "CO");
var result = query.Select (c => c.Name);
In this case if you change your first query to IEnumerable query = ..., then the second line would use the IEnumerable version of the Where extension (LINQ to Objects) rather than the IQueryable one (LINQ to SQL/EF). As a result, when we start iterating, the first where clause is passed to the database, but the second where clause is performed on the client side (because it has already been converted to an IEnumerable).
Another subtle item you want to be aware of is the following type of code:
var query = db.OrderBy(c => c.State);
query = query.Customers.Where(c => c.Age > 18); // Fails: Widening
In this case, since your original query returns IOrderedQueryable rather than IQueryable. If you try to then assign query to the result of the .Where operation, you're trying to widen the scope of the return type and the compiler will refuse to perform that widening. As a result, you have to explicitly specify the baseline type rather than using var:
IQueryable<Customer> query = db.OrderBy(c => c.State); // Is narrowing rather than widening.
query = query.Customers.Where(c => c.Age > 18);
Linq queries return IQueryable<> or IEnumerable<>, the execution of both is deferred.
As DanM stated, whether or not you use var or IEnumerable<> it all depends on the return value of the method you're calling: if it's an IEnumerable<> or IQuerable<> it'll be deferred, if you use .ToList(), it'll be executed right away.
When to use var comes down to personal choice/style. I generally use var when the return value is understood from the line of code and variable name or if I'm instantiating a generic with a long declartion, e.g. Dictionary<string, Func<Order, object>>.
From your code, it's clear that a collection of Customers/Orders is returned, so I would use the var keyword. Again, this is a matter of personal preference.

construct expression tree in run time

is it possible to construct MethodCallExpression (by using Expression.Call), when this call is NESTED? so, in design time, the actual sequnce (IQueryable or IEnumerable) which the call should be run against, is unknown?
Example:
let's say this is the query:
var result = data.GroupBy(x => x.Name).Select(grouping => grouping.OrderByDescending(x => x.Date).Take(1).FirstOrDefault()).ToList();
when data is a list of objects contains:Name,Date properties.
How do i constructs this query block:
grouping => grouping.OrderByDescending(x => x.Date)
using Expression.call?
thanks
My Answer:
I managed to sort this out.
Aducci gave me the direction, thnks!
the key was to use ParameterExpression and not an actual collection instance.
From here, the sulotion is just around the corner: complie the Expression>, and call the compiled result with the actual collection.
this was very helpful: MethodCallExpression -> compiled expression
and also : link
i'm adding my final code:
Expression<Func<Data,DateTime>> lmbd = x => x.Date;
ParameterExpression par = Expression.Parameter(typeof(IQueryable<Data>),"pname");
ParameterExpression[] parameters = new ParameterExpression[] {par};
MethodCallExpression method = Expression.Call(typeof(Queryable),"OrderBy",new Type[]{typeof(Data),typeof(DateTime)},par, lmbd);
var lambaExpression = Expression.Lambda<Func<IQueryable<Data>, IQueryable<Data>>>(method, par);
var compiled = lambaExpression.Compile();
Thank you all!
I asked a similar question which can be found here
ParameterExpression innerParameter = Expression.Parameter(typeof(GROUPING), "x");
Expression innerExpression = Expression.Lambda<Func<DATA, object>>(Expression.Property(innerParameter, "Date"),
innerParameter);
ParameterExpression parameter = Expression.Parameter(typeof(DATA), "grouping");
Expression call = Expression.Call(typeof(Enumerable),
"OrderByDescending",
new type[] { typeof(DATA) },
parameter,
innerExpression);

Resources