LINQ - Remove Parts of Expression Tree - c#

I have the following LINQ query:
var query = session.Query<Event>()
.Fetch(e => e.Venue); // Fetch is an IQueryable extension which does a join
// Code needed here to remove the fetch part
var num = query.Count(); // This then hits the database
Unfortunately this throws an error as fetch is not supported for a count method. At this stage i'm sure you're thinking why don't i remove the fetch part. However i have simplified my example and this is not possible. What i'd ideally like to be able to do is navigate the expression tree for the LINQ query and remove any calls to Fetch before i call Count.
I'd appreciate it if someone could show how this is possible. Thanks

It is possible to change expression trees at runtime (by building a new one out of the existing one), since O/RM tools such as LINQ to SQL and Entity Framework do this constantly, but it's not really easy. It has become easier with the introduction of the ExpressionVisitor class of .NET 4.0, but still don't expect it to be simple.
Here is an article that shows an example of this.

Would it help to convert the query to an Enumerable and call Count() on this like this:
var num = query.AsEnumerable().Count();
This would execute the query and afterwards makes a simple Count() on the result instead of letting the Count() flow into the ExpressionTree.

Related

How to filter and order collection in C#?

Here is the code:
public ActionResult Index()
{
var crView = db.CRCases.ToList();
return View(crView);
}
Would like to filter like we have in sql statement using WHERE and ORDER BY.
Is there any good reference on how to apply it?
linq is your friend here.
you can do
crView = crView.Where(x => x.yourPredicate).OrderBy( y => y.OrderClause);
CRCases should be a DBSet<T>. You can use LinQ like this :
db.CRCases.Where(x => x.TheProperty =xxx).ToList()
You can use LINQ = Language Integrated Query.
This have two different notations:
Functional: you can change several function calls beginning on an IEnumerable.
db.CrCases.Where(predicate).OrderBy(sortExpression)
"SQL like": it really looks like an "unordered SQL".
from crCase in db.CrCases where condition orderby sortExpression select crCase
The condition and sort expression in the first notation require lambda expressions (you can see them in action here). In the second case the condition is expressed.
Most of the queries can be written either way.
You'll usually use var to store the query, and materialize it later.
var query = from crCase in db.CrCases
where condition orderby sortExpression
select crCase
CrCase firstCase = query.Firts();
List<CrCase> cases = query.ToList();
There are several flavors of LINQ. They all look equal on the surface, but are translated into another thing. For example:
LINQ to objects: it's the more general, an allows to make queries on collections. Tis is what you'll use for this particular case.
LINQ to SQL: this allows to write queries in C# which are transalated to SQL queries in the underlying database
LINQ to Entities: similar to the previous, but you make your queries to an Entity Framework model, and the queries are finally transalated to the DB
There are some others flavors of LINQ. And, in fact, you can create your own, but that's not easy.
Finally, you have to take into account that a LINQ query isn't executed until you materialize it. I.e. it's not executed until you try to get data from it (converting it ToList(), ToArray() or whatever, enumerating it, or accesing any of it's data First(), Sum(), Count()). Depending on the flavor, the executing can be running C# code (LINQ to objects) or run a SQL Query in a DB (LINQ to SQL) or whatever.
Here you have a great place to learn how to use LINQ:
LINQ 101

Entity framework and linq string comparsion issue

I have a problem with linq..
lets see the code
I have an article class:
public calss Article
{
public string Tag{get; set; }
}
I save each tag for article splitted by , comma.
for example : first,second,third
and when I want to get an article I want to get articles that has any common tag.
I use this query but:
var relatedArticles =
_db.Articles.Where(a => a.Tag
.Split('،')
.Any(t => article.Tag
.Split('،')
.Any(ac => ac == t)));
and I am getting this exception:
LINQ to Entities does not recognize the method 'System.String[]
Split(Char[])' method
Any other way?
Update:
i cant keep tags in different table because i must let user to create tags as many as he wants when he/she is inserting article.
like 50 and it will be overhead to check if that tag exists or not when saving article to db.
Set your class as follows:
public class Article
{
public List<string> Tag{get; set; }
}
I'm not 100% sure what your 2nd statement does, but you can use a.Tag.Contains() to check your values.
I think my comment could be worth as answer to your problem, so i write it down as one :)
You should think about your table / class design.
You need to normalize it because that looks like n:m reference. Keep the article in one table with a reference to a mapping table, where you reference the arcticleId and the tagId and than one table with that tags with a primary key tagId.
If one Tag will change in future, you don't need to update every article, you just update that particular tag and it changes for every article.
The "a.Tag.Split('،')" is a node in the expression tree that your IQueryable is, not an actual operation. When you materialize results by calling something like ToList, the whole expression tree is translated into SQL expression before execution - this is the point of error because translator doesnt have an idea how to convert Split() method into sql statement.
As an alternative, you can retrieve all results into app, materialize them by ToList() and then do what you want with them as IEnumerables. Also, you can write a stored procedure and pass search tags array into there.
Also, maybe it will work - try to pass a simple values array (not as methods) into query so resulting sql looks like
"WHERE ... IN ...".
it means that Linq to entities failed to find translation of split method that can be written as SQL query. if you want to perform split functions you have to bring the record in memory by calling ToList(), AsEnumerable() etc.
But better approach would be to create separate table for tags in your db.
Linq query would look something like this(supposing many-to-many relationship between articles and tags):
var relatedArticles =
_db.Articles.Where(a => a.Tags.Any(t => t.Articles.Count() > 1));
// if Article has Tag which is assigned more then to one Article then it suits us
You can't call regular methods like string.Split() directly in Linq when working with EF, since it can't be translated to SQL.
You could append AsEnumerable() to your Where() clause to cause Linq to fetch the data, allowing you to perform operations like that on it later. Unfortunately, that will not permit you to do what you want with the list without fetching the whole list, which I'm sure you would rather avoid.
Perhaps you could do something like this instead?:
List<string> tagsForCurrentArticle = ... // fetch this first somehow
_db.Articles.Where(a =>
tagsForCurrentArticle.Any(tag =>
a.Tag.Contains(tag)))
Note, just to be clear: This should work, but the better option, if possible, would be to move your tags out into a separate table, as others have suggested.

Passing query data from LINQ to method in same query

I was able to create a LINQ statement that I thought was strange and wanted to see if anyone else had experience with it.
I've simplified it to this:
var x = db.Test
.Where(a => a.Field1 == Utils.CreateHash(Preferences.getValue(a.Field2)))
.FirstOrDefault();
Now how does this translate to database code? Wouldn't LINQ need to do a double query for every single row, i.e. for row a:
1) Query a.Field2
2) Return value to run Utils.CreateHash(Preferences.getValue(a.Field2))
3) Take that value from step 2 and compare it against a.Field1
4) Repeat 1-3 until I've gone through all the rows or returned a matching row
Wouldn't this be extremely inefficient? Or is LINQ smart enough to run this in a better way? Note, I haven't actually run this code so another possibility is a runtime error. Why wouldn't LINQ be smart enough to detect a conflict then and not let me compile it?
The query as is will not work since have a call to Utils.CreateHash in your lambda that you are trying to execute on the DB - in that context you cannot execute that method since there simply is no equivalent on the DB side hence the query will fail.
In general the ability of 3rd party Linq IQuerable providers (e.g. Linq to SQL, Linq to Entities) to access in memory constructs such as methods or classes is very limited, as a rule of thumb at most accessing primitive values or collections of primitives will work.
Just to add fast...
A good example to know how this works would be to write (extreme case I agree, but best :) or go through the source code for a custom (open source) LINQ provider (e.g. http://relinq.codeplex.com/ has one etc.).
Basically (I'm simplifying things here a bit), a LINQ provider can only 'map' to Db (supported SQL, functions) what he 'knows' about.
i.e. it has a standard set it can work with, other than that, and with your custom methods (that do not translate to constants etc.) in the frame, there is no way to resolve that on the 'Db/SQL side'.
E.g. with your 'custom' linq provider (not the case here) you could add a specific extension call e.g. .MyCalc() - which would be properly resolved and translated into SQL equivalent - and then you'd be able to use it.
Other than that, I think if I recall correct, provider will leave that as an expression, to resolve when it returns from the Db 'fetch', query operation. Or complain about it in certain cases.
Linq is based on IQueryable - and you can take a look at extension methods provided there for SQL equivalents supported.
hope this helps
EDIT: whether things 'work' or not doesn't matter - it still doesn't mean it'd execute on the Db context - i.e. it'd be unacceptable performance wise in most cases. IQueryable works with expressions (and if you look at the interface) - and linq is executed when you invoke or enumerate usually. At that point some of the expressions may evaluate to a const value that can be worked into a SQL, but not in your case.
Best way to test is to test back the SQL generated by query (possibly this one I think Translate LINQ to sql statement).
No.
The LINQ provider will run a single SELECT query that selects both fields, then execute your lambda expression with the two values for each returned row.

is my linq query pulling just what it needs or everything?

I do something like this:
var src = dbContext.Set<Person>().Where(o => o.LastName.StartsWith(search));
var page = src.OrderBy(u => u.Id).Skip((page - 1) * pageSize).Take(pageSize);
var count = src.Count();
is ef pulling from the database everything and after does the query or not ? how do I know this? what are the ways finding out this?
(using ef4 ctp5 code first)
Try downloading LinqPad, it will show you the SQL that gets executed so you can see exactly what's happening.
Here's a Linq query and the results:
Here's the same Linq query along with the SQL that gets executed:
It's a very good tool for writing and optimising Linq to EF and Linq to SQL queries. It's also great for writing and testing .Net code snippets.
This tool has saved me so much time simply because you don't need to fire up the debugger! It's the most useful .Net tool that I've found in years.
None of the calls you are using is a To* operator (like ToList), so all method calls will be translated into SQL and executed in the database. However, the Count operator is immediately evaluated, you might should delay that assignment to the place you actually need the value. The other variables will be evaluated once iterated over.

Linq2Entities, many to many and dynamic where clause

I'm fairly new to Linq and struggling using dynamic where over a many to many relationship.
Database tables are like so:
Products <-> Products_SubCategories <-> SubCategories
with Products_SubCategories being a link table.
My full linq statement is
db.Products.Where("it.SubCategories.SubCategoryID = 2")
.Include("SubCategories")
.OrderBy(searchOrderBy)
.Skip(currentPage * pageSize)
.Take(pageSize)
.ToList()
.ForEach(p => AddResultItem(items, p));
So ignoring everything bar the Where() I'm just trying to pull out all products which are linked to sub category ID 2, this fails with
To extract properties out of collections, you must use a sub-query to iterate over the collection., near multipart identifier, line 8, column 1.
I think using the SQL-esque syntax I can do a subquery as per this link. However I'm not sure how to do that in the lambda / chaining syntax.
This is the start of a search function and I would like to build up the where string dynamically, as I have with the searchOrderBy string to avoid a large SELECT CASE. Products is linked to another table via a link table that I will need to include once I understand how to do this example.
Any help would be much appreciated!
Thanks
This is wrong:
db.Products.Where("it.SubCategories.SubCategoryID = 2")
SubCategories is a list. It does not have a property called SubCategoryID. Rather, it contains a group of entities which each have a property called SubCategoryID. That's a critical distinction.
When you run into a situation where you don't know how to proceed in there are multiple problems, it is good to break the problem down into several, smaller problems.
Let's start by removing the dynamic query. It will be easier to solve the problem with a non-dynamic query. Once you've done that, you can go back and make it dynamic again.
So start by using the non-dynamic syntax. Type something like this in Visual Studio, and see what IntelliSense does for you:
db.Products.Where(p => p.SubCategories.
You will quickly see that there is no SubCategoryID property. Instead, you will see a bunch of LINQ API methods for working with lists. If you know LINQ well, you will recognize that the Any() method is what you want here:
db.Products.Where(p => p.SubCategories.Any(sc => sc.SubCategoryID == 2))
Go ahead and run that query. Does it work? If so, you can move ahead to making it dynamic. I'm no ESQL expert, but I'd start with something along the lines of:
db.Products.Where("EXISTS(SELECT SC FROM it.SubCategories AS SC WHERE SC.SubCategoryID = 2");
As an aside, I use MS Dynamic Query ("Dynamic LINQ") for this sort of thing rather than Query Builder, as it's more testable.
It worked for me.
db.Products.Where("SubCategories.Any(SubCategoryID = 2)")

Categories