Linq to NHibernate Expression Tree with nested relation condition - linq

In a .Net project in which I'm using NHibernate, I have a piece of code that build a list of expression trees depending on the values set in the filter by the user in the UI.
The expression is build against a specific object of my domain model, let's say Customer.
When I wanna create a filter criteria for a property of Customes, everything's fine, like in the following example:
Expression<Func<Model.Customer, bool>> expr = c =>
c.Name == "My Company";
But now, I need to create an expression that let me filter che customer based on a condition involving a one to many relation... let's say Order. A customer can have many orders, so the relationship is one-to-many. I need to build an expression that I can apply to a Customer query, in order to exptract only the customers which have at least one order placed in 2010.I'd write something like this:
Expression<Func<Model.Cusotmer, bool>> expr = c =>
c.Orders.Where(o => o.year == 2010).Count() > 0;
Too bad this won't work. It seems that NHibernate is not able to parse this Expression.
Any idea on how to write an expression tree that implement that search criteria and is parsable by Linq 2 NHibernate?

because you use Count() > 0 you can use Any instead:
Expression<Func<Model.Cusotmer, bool>> expr = c =>
c.Orders.Any(o => o.year == 2010);

Related

LINQ - Is Where(Predicate).FirstOrDefault() the same as FirstOrDefault(Predicate)

I have always written my LINQ queries with the predicate in the Where clause followed by the FirstOrDefault clause. I started seeing examples with the predicate in the FirstOrDefault clause.
Is one better than the other? Would the answer be different with EF (SQL)?
A. Using Where Clause
List<Product> products = GetProductList();
Product productWhere = products.Where(p => p.ProductID == 789).FirstOrDefault();
B. No Where Clause
List<Product> products = GetProductList();
Product productNoWhere = products.FirstOrDefault(p => p.ProductID == 789);
https://code.msdn.microsoft.com/LINQ-Element-Operators-0f3f12ce
Because method chains in Linq are lazily evaluated, there shouldn't be any material difference between the two. Where.FirstOrDefault will stop executing when it obtains a value, just as FirstOrDefault(Predicate) will.
To put it another way, FirstOrDefault (or any other Linq operator downstream, for that matter) accepts items one at a time from Where for evaluation, not the entire list at once (The result of a Linq operator that returns an IEnumerable is essentially a yield return under the hood).
See Also
Where.FirstOrDefault vs FirstOrDefault

Linq to NHibernate and Dynamic LINQ - query caching not working

I have problem with the NHibernate's second level cache. When I use query:
var items1 = Session.Query<Row>()
.Cacheable();
.Fetch(x => x.Field)
.OrderBy(x => x.Field.Value)
.ToList();
Everything is fine - the query is cached. But when I want to use Dynamic Linq (a link):
var items2 = Session.Query<Row>()
.Cacheable();
.Fetch(x => x.Field)
.OrderBy("Field.Value")
.ToList();
The query is not cached. Interesting thing is that, when I delete code line:
.Fetch(x => x.Field)
caching works again. So the problem is with using Fetch and dynamic linq OrderBy methods together.
EDIT:
When I try do debug NH code (QueryKey class), debugger tells me that these two queries do not have the same ResultTransformer (and deeper: a listTransformation private instance).
Any ideas?
Chris
Ok, I know what is the reason.
Dynamic Linq doesn't use Parameter Names in Linq Expressions. E.g. if I want to sort using lambda statemant, I write:
query.OrderBy(item => item.Name)
Above we see an item lambda parameter name.
When I use Dynamic linq:
query.OrderBy("Name")
in the result Queryable the lambda parameter in OrderBy mehod has no name (like item written above). We can illustrate the result like this:
query.OrderBy( => .Name)
And now, when NHibernate is decoding that Queryable expression and finds there an expression parameter that has no name, NH gives it a random name using GUID class. So every ordering using dynamic linq produces a Queryable Expression that has inconstant lambda parameter. This is the reason why NHibernate thinks that: query.OrderBy("Name") and query.OrderBy("Name") are not the same - they have another lamda parameters generated every time from scratch.
SOLUTION
If you want to fix it, you have to modify Dynamic Linq library.
In method ExpressionParser.ProcessParameters change line:
if (parameters.Length == 1 && String.IsNullOrEmpty(parameters[0].Name))
to:
if (parameters.Length == 1 && (parameters[0].Name == "it" || String.IsNullOrEmpty(parameters[0].Name)))
In method DynamicQueryable.OrderBy change line:
Expression.Parameter(source.ElementType, "")
to:
Expression.Parameter(source.ElementType, "it")
Now, query.OrderBy("Name") will produce query.OrderBy(it => it.Name).
Cheers!

Entity Framework LINQ Query using Custom C# Class Method - Once yes, once no - because executing on the client or in SQL?

I have two Entity Framework 4 Linq queries I wrote that make use of a custom class method, one works and one does not:
The custom method is:
public static DateTime GetLastReadToDate(string fbaUsername, Discussion discussion)
{
return (discussion.DiscussionUserReads.Where(dur => dur.User.aspnet_User.UserName == fbaUsername).FirstOrDefault() ?? new DiscussionUserRead { ReadToDate = DateTime.Now.AddYears(-99) }).ReadToDate;
}
The linq query that works calls a from after a from, the equivalent of SelectMany():
from g in oc.Users.Where(u => u.aspnet_User.UserName == fbaUsername).First().Groups
from d in g.Discussions
select new
{
UnReadPostCount = d.Posts.Where(p => p.CreatedDate > DiscussionRepository.GetLastReadToDate(fbaUsername, p.Discussion)).Count()
};
The query that does not work is more like a regular select:
from d in oc.Discussions
where d.Group.Name == "Student"
select new
{
UnReadPostCount = d.Posts.Where(p => p.CreatedDate > DiscussionRepository.GetLastReadToDate(fbaUsername, p.Discussion)).Count(),
};
The error I get is:
LINQ to Entities does not recognize the method 'System.DateTime GetLastReadToDate(System.String, Discussion)' method, and this method cannot be translated into a store expression.
My question is, why am I able to use my custom GetLastReadToDate() method in the first query and not the second? I suppose this has something to do with what gets executed on the db server and what gets executed on the client? These queries seem to use the GetLastReadToDate() method so similarly though, I'm wondering why would work for the first and not the second, and most importantly if there's a way to factor common query syntax like what's in the GetLastReadToDate() method into a separate location to be reused in several different other LINQ queries.
Please note all these queries are sharing the same object context.
I think your better of using a Model Defined Function here.
Define a scalar function in your database which returns a DateTime, pass through whatever you need, map it on your model, then use it in your LINQ query:
from g in oc.Users.Where(u => u.aspnet_User.UserName == fbaUsername).First().Groups
from d in g.Discussions
select new
{
UnReadPostCount = d.Posts.Where(p => p.CreatedDate > myFunkyModelFunction(fbaUsername, p.Discussion)).Count()
};
and most importantly if there's a way to factor common query syntax like what's in the GetLastReadToDate() method into a separate location to be reused in several different places LINQ queries.
A stored procedure would probably be one way to store that 'common query syntax"...EF, at least 4.0, works very nicely with SP's.

LINQ Query to find all tags?

I have an application that manages documents called Notes. Like a blog, Notes can be searched for matches against one or more Tags, which are contained in a Note.Tags collection property. A Tag has Name and ID properties, and matches are made against the ID. A user can specify multiple tags to match against, in which case a Note must contain all Tags specified to match.
I have a very complex LINQ query to perform a Note search, with extension methods and looping. Quite frankly, it has a real code smell to it. I want to rewrite the query with something much simpler. I know that if I made the Tag a simple string, I could use something like this:
var matchingNotes = from n in myNotes
where n.Tags.All(tag => searchTags.Contains(tag))
Can I do something that simple if my model uses a Tag object with an ID? What would the query look like. Could it be written in fluent syntax? what would that look like?
I believe you can find notes that have the relevant tags in a single LINQ expression:
IQueryable<Note> query = ... // top part of query
query = query.Where(note => searchTags.All(st =>
note.Tags.Any(notetag => notetag.Id == st.Id)));
Unfortunately there is no “fluent syntax” equivalent for All and Any, so the best you can do there is
query = from note in query
where searchTags.All(st =>
note.Tags.Any(notetag => notetag.Id == st.Id))
select note;
which is not that much better either.
For starters see my comment; I suspect the query is wrong anyway! I would simplifiy it, by simply enforcing separately that each tag exists:
IQueryable<Note> query = ... // top part of query
foreach(var tagId in searchTagIds) {
var tmpId = tagId; // modified closures...
query = query.Where(note => note.Tags.Any(t => t.Id == tmpId));
}
This should have the net effect of enforcing all the tags specified are present and accounted for.
Timwi's solution works in most dialects of LINQ, but not in Linq to Entities. I did find a single-statement LINQ query that works, courtesy of ReSharper. Basically, I wrote a foreach block to do the search, and ReSharper offered to convert the block to a LINQ statement--I had no idea it could do this.
I let ReSharper perform the conversion, and here is what it gave me:
return searchTags.Aggregate<Tag, IQueryable<Note>>(DataStore.ObjectContext.Notes, (current, tag) => current.Where(n => n.Tags.Any(t => t.Id == tag.Id)).OrderBy(n => n.Title));
I read my Notes collection from a database, using Entity Framework 4. DataStore is the custom class I use to manage my EF4 connection; it holds the EF4 ObjectContext as a property.

DynamicObject LINQ query does't works with custom class!

DynamicObject LINQ query with the List compiles fine:
List<string> list = new List<string>();
var query = (from dynamic d in list where d.FirstName == "John" select d);
With our own custom class that we use for the "usual" LINQ compiler reports the error "An expression tree may not contain a dynamic
operation":
DBclass db = new DBclass();
var query = (from dynamic d in db where d.FirstName == "John" select d);
What shall we add to handle DynamicObject LINQ?
Does DBClass implement IEnumerable? Perhaps there is a method on it you should be calling to return an IEnumerable collection?
You could add a type, against which to write the query.
I believe your problem is, that in the first expression, where you are using the List<>, everything is done in memory using IEnumerable & Link-to-Objects.
Apparently, your DBClass is an IQueryable using Linq-to-SQL. IQueryables use an expression tree to build an SQL statement to send to the database.
In other words, despite looking much alike, the two statements are doing radically different things, one of which is allowed & one which isn't. (Much in the way var y = x * 5; will either succeed or fail depending on if x is an int or a string).
Further, your first example may compile, but as far as I can tell, it will fail when you run it. That's not a particular good benchmark for success.
The only way I see this working is if the query using dynamic is made on IEnumerables using Link-to-Objects. (Load the full table into a List, and then query on the list)

Resources