Conditionally adding .Take() - linq

Currently I have this that automatically takes 500 rows:
var orderQuery = subsetTable.Where(pred).OrderByDescending(o => o.CreationDate).Take(500);
I'd like to make the Take() conditional, something like this:
var orderQuery = subsetTable.Where(pred).OrderByDescending(o => o.CreationDate);
if (condition)
orderQuery = orderQuery.Take(500);
Is this possible?
Edit:
The compiler says
"Cannot implicitly convert type 'System.Linq.IQueryable' to
'System.Linq.IOrderedQueryable'."

Add "AsQueryable" to make the types line up:
var orderQuery = subsetTable.Where(pred).OrderByDescending(o => o.CreationDate).AsQueryable();
if (condition)
orderQuery = orderQuery.Take(500);

In Linq-to-Objects, the var will infer to IOrderedEnumerable<T>, where T is the type of your object. The Take() will yield an IEnumerable<T>, so your line of code there will not be allowed. (IOrderedEnumerable is more specified than IEnumerable, you need your query to typed in a less-specified manner.) And, as the comments point out, the same holds true for providers that deal in terms of IQueryable<T>, which itself can be expressed as a less specified IEnumerable<T>.
To make it work, explicitly type your query to the lesser specified type you need, IEnumerable<T> or IQueryable<T>, and then you can apply your conditional Take.
IEnumerable<YourType> orderedQuery = ...
if (condition)
orderedQuery = orderedQuery.Take(n);

Is this possible?
Yes. Your code should work nearly as written. You just have to eliminate the var. Assuming your type is Order. you'd use:
IQueryable<Order> orderQuery = subsetTable.Where(pred).OrderByDescending(o => o.CreationDate);
if (condition)
orderQuery = orderQuery.Take(500);

What I do is to add Sorting at the end, to avoid having to convert them
var orderQuery = subsetTable.Where(pred);
if (condition)
orderQuery = orderQuery.Take(500);
orderQuery = orderQuery.OrderByDescending(o => o.CreationDate);

Related

LINQ Breaking changes in EF Core 3.0. Using expressions

In my app I have some queries that use the same repeated logic:
var someThings = context.table1
.where(SomeLogic)
.ToList();
With EF Core 2.1 I could encapsulate this logic in a layer with all these expressions:
public static Expression<Func<MyObject, bool>> SomeLogic =>
myObject => myObject.CreationDate.Date == DateTime.Now.Date
&& (myObject.Whatever.HasValue || myObject.MoreWhatever);
Now I discovered this was being evaluated in memory, and that's bad.
If I do something like:
var someThings = context.table1
.where(myObject =>
myObject.CreationDate.Date == DateTime.Now.Date
&& (myObject.Whatever.HasValue || myObject.MoreWhatever))
.ToList();
then the query is evaluated in the DB, but I am putting some logic in the wrong layer.
I tried to subsitute Expression with a function or any other tool, but I don't find a way to do it.
Is there a way to encapsulate the logic of a query in a layer as I was doing before, but preserving EF rules so that this query can still be evaluated in the DB?
Thanks.
Why you need a "real" expression and not just a Lambda is explained in this answer. The created Expression can be created anywhere and passed as a parameter to the function that executes the query.
This answer should guide the way you need to go. You only have to replace the two dummy expressions with the whatever.hasvalue...stuff
var param = Expression.Parameter(typeof(MyObject), nameof(MyObject));
// myObject.CreationDate.Date == DateTime.Now.Date
Expression dateExpression = Expression.Equal(Expression.Constant(DateTime.Now),
Expression.PropertyOrField(param, "CreationDate"));
var dummyExpression1 = Expression.Equal(Expression.Constant(1), Expression.Constant(1));
var dummyExpression2 = Expression.Equal(Expression.Constant(1), Expression.Constant(1));
// && (myObject.Whatever.HasValue || myObject.MoreWhatever)
Expression orExpression = Expression.Or(dummyExpression1, dummyExpression2);
Expression allConditions = Expression.And(dateExpression, orExpression);
//myObject =>
Expression myExpression = Expression.Lambda<Func<MyObject, bool>>(allConditions, param);
var someThings = context.table1
.where(myExpression)
.ToList();
I had the most trouble with Expression.PropertyOrField. If you have nested structures you need to loop through the data structure and call Expression.PropertyOrField with the first parameter being the result from the previous call to Expression.PropertyOrField.

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 can QueryOver be used to filter for a specific class?

I am currently dynamically constructing queries like so:
QueryOver<Base, Base> q = QueryOver.Of<Base>();
if (foo != null) q = q.Where(b => b.Foo == foo);
// ...
Now there are multiple mapped subclasses of Base (e. g. Derived) which I want to filter on, basically something like:
if (bar) q = q.Where(b => b is Derived); // does not work
or:
if (bar) q = q.Where(b => b.DiscriminatorColumn == 'derived'); // dito
How do I best achieve that, preferably - but not neccessarily - in a type-safe way? Can this be done using LINQ, too?
This is not intuitive, but the following should work fine (QueryOver):
if (bar) q = q.Where(b => b.GetType() == typeof(Derived));
I'm not sure about a way to do this in LINQ-to-NH.
The general QueryOver, asking for a subtype, would look like this:
Base alias = null;
var query = session.QueryOver<Base>(() => alias);
// this statement would be converted to check of the discriminator
query.Where(o => o is Derived);
var list = query.List<Derived>();
But this would result in a statement, expecting that discirminator is "MyNamespace.Derived". If this si not the case, we can use this approach:
Base alias = null;
var query = session.QueryOver<Base>(() => alias);
// here we can compare whatever value, we've used as discriminator
query.Where(Restrictions.Eq("alias.class", "Derived"));
var list = query.List<Derived>();
Here we are using the feature of NHibernate: ".class" which does return the value of discriminator
More details could be found here:
17.1.4.1. Alias and property references

LINQ How to Average an internal list

What is the LINQ syntax for the Average need below. Both Select and SelectMany fail me.
I can Max() and Sum() my internal list, but Average() wont compile.
The JSON version of the simple list looks like
[{"Week":6, "Matches":[{"Game":189},{"Game":149},{"Game":132}]} ....
var hiGame = games.Max(g => g.Matches.Max(m => m.Game)); // ok
var hiSeries = games.Max(g => g.Matches.Sum(m => m.Game)); // ok
var ave = games.Average(g => g.Matches.SelectMany(m => m.Game)); // no go
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.
I think you want your SelectMany on the outside:
var ave = games.SelectMany(g => g.Matches) // Flatten to a sequence of matches
.Average(m => m.Game); // Average by value of Game

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.

Resources