Not supported exception in LINQ Lambda - c#

I have this code and it gives me this following error "LINQ to Entities does not recognize the method"
var AuxiliarValue = _context.company.LastOrDefault(x => x.StartValue.HasValue && (x.StartValue.Value < InicialValue));
InicialValue is Double
But when I put a ToList(), it works
var AuxiliarValue = _context.company.ToList().LastOrDefault(x => x.StartValue.HasValue && (x.StartValue.Value < InicialValue));
Can anyone explain to me why it works with ToList()?

LastOrDefault is not supported with LINQ to Entities. You can use OrderByDescending and then use FirstOrDefault
var AuxiliarValue = _context.company
.OrderByDescending(r=> yourFieldtoOrder)
.FirstOrDefault(x => x.StartValue.HasValue && (x.StartValue.Value < InicialValue));
The reason it works with ToList is that ToList will iterate all the results and bring them in memory, so the LastOrDefault is executed on an in-memory collection, rather than at the database end.

It works when you add ToList because the query is no longer being translated into SQL and executed by the database. Instead, the entire table of data is returned from the database to your application, a List is built to hold that data, and then the operation is performed using LINQ to Objects.
You probably don't want to do that; you probably want to adjust the way that you query the data such that it can be translated into SQL and run against the database.

Related

LINQ to SQL to find values starting with characters from another table

I would like to retrieve a list of values from a SQL table where the records start with a prefix defined in another table.
This post gives an accurate answer, but it is for EF and not Linq to SQL.
With SQL I get an error:
Only arguments that can be evaluated on the client are supported for
the String.Contains method
Sample code:
var lookupList = dc.LookupTable.Select(p => p.Prefix);
var q = dc.Personnel
.Where(item => lookupList
.Any(p => item.Surname.StartsWith(p))).Select(x => x.PersonID);
This works with EF. Yes, I can ToList() my collections but the tables are big and the query becomes very slow. Any suggestions on how to make it work without enumerating my objects?
This part: .Any(p => item.Surname.StartsWith(p)) gives the error:
Only arguments that can be evaluated on the client are supported for the String.Contains method
It tells you Contains method does not work with the given parameter which can only be evaluated on the server. StartsWith basically uses the same mechanism.
So, instead of Contains or StartsWith you should use IndexOf to find out whether or not the containing parameter is occured at the beginning or not:
.Any(p => item.Surname.IndexOf(p) == 0)
According to MSDN:
IndexOf(T):
The index of item if found in the list; otherwise, -1.
This answer is partially taken from here.

How is "return (from tbl in context.[tablename] select tbl)" executed if calling method then does a .Where() on result?

A method returns the IQueryable<T> result of a from x in context.y select x query.
The calling method then calls .Where( a => ...) on that resultset.
Is the entirety of x going to be returned in IQueryable<T>, and then filtered? Or will LINQ load this lazily, and only once the resultset is iterated, will the actual SQL query be executed (using that where condition)?
This is using EntityFramework LINQ to Entities, against a MSSQL database.
If the method returns an IQueryable<T>, that's returning the query - not the results of the query. So calling Where will construct another query, applying a filter to the original query... still using expression trees.
So when you then actually start iterating over the results, the whole query will be converted to SQL, including the filter.
If the method had returned an IEnumerable<T> instead, then it would still represent the query (rather than the results), but the Where call would be using LINQ to Objects - so the filtering would happen in the client rather than in the database.

Filtering IQueryable using self defined function causes runtime error

I had a similar query to LINQ: How to remove element from IQueryable<T> and for the most part it answered my question.
My code is set up similarly:
var items = MyDataContext.Items.Where(x =>MyFunction(x.value1, x.value2, x.value3));
...
...
bool MyFunction(decimal val1, decimal val2, decimal val3)
{
//some calculation with the parameters
return true;
}
It compiles fine, but when I run it, it throws an error:
"An exception of type 'System.NotSupportedException' occurred in
System.Data.Entity.dll but was not handled in user code
Additional information: LINQ to Entities does not recognize the method
'Boolean MyFunction(System.Decimal, System.Decimal, System.Decimal)'
method, and this method cannot be translated into a store expression."
I'm new to linq, only started this week, so any help is appreciated. Thanks!
Your linq query is translated into SQL. In this case Linq to Entities doesn't know how to translate your method into SQL correctly. So you can't use custom methods on linq to sql queries. You can only use supported methods
If you want to do that, you have to fetch all data from database and do that in the memory.
if you want to run this code perfectly, you should use IEnumurable like this:
var items = MyDataContext.Items.toList().Where(x =>MyFunction(x.value1, x.value2, x.value3));
Try adding .ToList() before your where clause.
I am gussing you are querying a database of some sort, but the data provider can't translate your method Myfunction.
Get your data into memory before firing the where clause:
var items = MyDataContext.Items.ToList().Where(x =>MyFunction(x.value1, x.value2, x.value3)
From the cmoments I take it, there is some additional explanation needed. Your query is not executed until you really need it.
By calling .ToList() you materialize your data and every action afterwards is executed in memory on your machine instead on the database. This means you could very well split this into 2 calls:
//First call. Executed as SQL query on database. Materializes data into IEnumerable<Item>
var items = MyDataContext.Items.ToList();
//second call on client
var filteredItems = Items.Where(x => x.Id == MyFunction(value1, value2));
If you omit the ToList() call, Entity Framework trys to translate Myfunction() into SQL, which it can't and then throws an error.

Overhead of using linq queries on non-changing list

While debugging my code, I came to know that if you are using a Linq query for assigning any property, whenever that property is used, the query is going to be executed every time. So if you are doing something like:
Myprop= MyList.where(p=>p.Stauts=="ABKI BAAR ..");
So in your code, whenever you do foreach(var prop in Myprop)',Myprop.ToList().count()` or any use of Myprop, it will result in the execution of the linq query every time.
Isn't executing the linq query everytime it a bad idea, especially when I am not modifying MyList?
You can circumvent this by calling ToList after your expression:
Myprop = MyList.where(p=>p.Stauts=="ABKI BAAR ..").ToList(); // or .ToArray()
That way it will immediately execute the Linq query and you won't be executing the query every time, since now you have a list.
This obviously does not take into account changes of the source list later on.
Materialize you query result using .ToArray() or .ToList().
So, instead of
Myprop = MyList.Where(p => p.Status == "ABKI BAAR ..");
Use
Myprop = MyList.Where(p => p.Status == "ABKI BAAR ..").ToArray();
See the MSDN documentation for more information on ToList or ToArray.
You should certainly never write Myprop.ToList().Count(). That would materialize the query (or copy an already materialized result), count the results, and then throw away the materialized result of the query (or copy thereof). If you only need to count, just do Myprop.Count().

LINQ can't use string.contains?

This is my code:
string queryString = "Marco".ToLower();
utenti = db.User.Where(p =>
queryString.Contains(p.Nickname.ToLower()) ||
queryString.Contains(p.Nome.ToLower()) ||
queryString.Contains(p.Cognome.ToLower())).ToList();
but I get:
Only arguments that can be evaluated on the client are supported for the String.Contains method.
Why? Can't I use .Contains()?
Try .IndexOf. It is not LINQ that can't do Contains, it's LINQ to Entities and LINQ to SQL that can't.
string queryString = "Marco";
utenti = db.User.Where(p =>
queryString.IndexOf(p.Nickname, StringComparison.OrdinalIgnoreCase) >= 0 ||
queryString.IndexOf(p.Nome, StringComparison.OrdinalIgnoreCase) >= 0 ||
queryString.IndexOf(p.Cognom, StringComparison.OrdinalIgnoreCasee) >= 0)
.ToList();
Why?
LINQ uses deferred execution. This means it waits until you want to iterate over your query results before it does anything. There are 3 main types of LINQ:
LINQ to Objects - when your IEnumerable is already on the heap.
LINQ to Entities - when you want to query a database using Entity Framework.
LINQ to SQL - when you want to query a database using LINQ to SQL.
Deferred execution in the context of the second 2 means that your query is not executed on the database until you enumerate the results in a foreach block, or invoke an enumeration method like .ToList, .ToArray, etc. Until then, your query is just stored as expression trees in memory.
Your query would work just peachy if db.User was a collection in memory. However when the data is in a database, LINQ to Entities (or LINQ to SQL) must translate your expression trees to what it calls a "store expression" -- which is just fancy talk for "convert my LINQ expressions to SQL".
Now imagine you had a custom C# algorithm you wanted to use for your query, and you did something like this:
var result = db.User.Where(x => MyCustomMethod(x));
There is no way today that LINQ to Entities can convert your C# code into a SQL query (store expression). It is the same with a lot of other C# methods you rely on daily. It also does not support .ToLower, .ToUpper, .StartsWith, .EndsWith, etc. There is a limited number of C# methods that can be converted to store expressions, and .IndexOf just happens to be one of them.
However keep in mind that it is only the string object's Contains method that we are talking about here that is not supported for store expressions. LINQ to Entities does support .Contains on IEnumerables. The following is valid and will work with LINQ to Entities (not sure about LINQ to SQL):
var idsIWantToFind = new[] { 1, 2, 3 };
var users = db.Where(x => idsIWantToFind.Contains(x.UserId));
The above is the equivalent of doing a SQL WHERE UserId IN (1, 2, 3) predicate.

Categories