Weird LINQ ToList(), IEnumerable vs AsQueryable() results - c#

So I have this query on MSSQL where BANKS is a Table View
SELECT t.*
FROM [DB].[dbo].[BANKS] t
where t.BCODE = 'xxxxxx '; <--- with spaces
which above query has 1 result BCODE : "xxxxxx" <--- no space
on converting it to entity framework using ToList() or as IEnumerable(); e.g.
var _BANKS = dbcontext.BANKS.IEnumerable();
//var _BANKS = dbcontext.BANKS.ToList();
Just note that I have to put this on a Memory because I constantly refer to this table as reference.
On simple execution
string bankcode = "xxxxxx ".Trim();
var test = _BANKS.Where(q => q.BCODE == bankcode ).ToList(); // <--- would return me null
var test2 = from t in _BANKS where t.BCODE == "xxxxxx" select new t; <--- still null
but when I change the _BANKS as AsQueryable(); using the same code snippet above, it would give the desired result the same on the native query (see first SQL snippet).
I'm avoiding the .AsQueryable() because it will give me a runtime error specifically
"The specified LINQ expression contains references to queries that are associated with different contexts."
because I'm using it to constantly refer in a different DBContext's.

The reason why in case of a simple SQL statement, you got one result is pretty obvious for me:
it is that in the database, you have a record which contains spaces, i.e. 'xxxxxx '.
Now why it is not working when using the below code?:
string bankcode = "xxxxx ".Trim();
var test = _BANKS.Where(q => q.BCODE == bankcode ).ToList(); // <--- would return me null
var test2 = from t in _BANKS where t.BCODE == "xxxxxx" select new t; <--- still null
Because you are trimming the bankcode which will lead to converting "xxxxx   " to "xxxxx" but in the database, you have an actual record matching this "xxxxx   ". That's why it will return null which means it will not find matching records.
To overcome this issue use LINQ to SQL Contains() method:
string bankcode = "xxxxx ".Trim();
var test = _BANKS.Where(q => q.BCODE.Contains(bankcode)).ToList();

The issue was not in the .ToList() or the .IEnumerable, but rather it is related to a memory leak when you are trying to save the data in the Memory from a Table view.
Workaround
var _BANKS = (from banks in dbcontext.BANKS
select new {
banks.BCODE,
// ... so on.
}).ToList();
re-selecting the LINQ from storing into your Memory weirdly corrects the inconsistency.

Related

LINQ query becomes invalid upon editing result data?

(Please see the answer I wrote for more understanding of the situation.)
Below is a query which works great, operating on selected rows of table STUDENTS. Then one edit destroys the query variable. What's wrong here?
students is rows selected from an import Datatable defined in part by:
importTable.Columns.Add("SECTION", typeof(string));
importTable.Columns.Add("NUMBER", typeof(string));
importTable.Columns.Add("ID", typeof(string));
(Because the DataTable is untyped, I need to cast the data into string to use the fields).
Then called by:
IEnumerable<DataRow> s = importTable.AsEnumerable();
IEnumerable<DataRow> t = s
.OrderBy(r => r["HALL"]);
IEnumerable<DataRow> sortedTable = t
.OrderBy(r =>
{ //if (r["ID"] is DBNull)
// return "";
//else
return r["ID"]; // ERROR
});
IEnumerable<DataRow> tue = sortedTable.Where(r => r["DAY"].Equals("TUE"));
IEnumerable<DataRow> wed = sortedTable.Where(r => r["DAY"].Equals("WED"));
AssignSections(tue);
AssignSections(wed);
Here is the query:
public void AssignSections(IEnumerable<DataRow> students)
{
IEnumerable<IEnumerable<DataRow>> query = from e in students.AsEnumerable()
orderby (e["SHORTSCHOOL"] as string).PadRight(30) + e["SEED"] as string
group e by new { DAY=e["DAY"], GRADE=e["GRADE"] } into g
orderby g.Key.GRADE as string
select g.AsEnumerable();
var queryList = query.ToList(); // ArgumentException during "WED" call
foreach (var grade in query)
foreach (var student in grade)
if (student["ID"] == DBNull.Value)
{
student["SECTION"] = "S";
student["ID"] = "ID1";
}
}
Assigning SECTION works, NO PROBLEM. Assigning ID causes query to look like:
query now appears invalid. Future uses of query also prove to be invalid (though the foreach finishes fine).
For what it's worth, grade is just fine, but students is also invalidated through the original table seems to be fine as well.
No magic here. It's a combination of LINQ query Deferred Execution and the usage of the DBNull, which cannot be compared to/from other types.
The deferred execution has been explained many times, so I'm not going to spend time on it. Shorty, the query is executed the only (but anytime) when enumerated. Enumerating means foreach, ToList etc. and technically speaking happens when the GetEnumerator of the enumerable (or the first MoveNext of the enumerator) is called.
All you need to remember from the above is that the IEnumerable<T> (or IQueryable<T>) returning LINQ queries are not executed (evaluated) at the time you define them, but every time you enumerate them (directly or indirectly). This should explain the "The answer surprisingly to me is that LINQ reorders code" part from your own answer. No, LINQ does not reorder the code, it's your code which is doing that by reevaluting the LINQ queries at certain points which are different from the place where you define your query variables. If you want to evaluate them just once at specific point, then do that by adding ToList, ToArray and similar methods which enumerate the query and store the result in some in memory collection and use that collection for further processing. It still be IEnumerable<T>, but further enumerations would enumerate the query result rather than reevaluate the query.
The main issue is the DBNull. From your explanations looks like initially all the ID values are DBNull, so the first query runs fine (DBNull knows how to compare to itself :). Once the source contains at least one value which is not DBNull, any further query that uses OrderBy that column with the default IComparer will fail.
It can easily be reproduced w/o data tables with the following simple code:
var data = new[]
{
new { Id = (object)DBNull.Value },
new { Id = (object)DBNull.Value }
};
var query = data.OrderBy(e => e.Id);
query.ToList(); // Success
data[1] = new { Id = (object)"whatever" };
query.ToList(); // Fail
showing the deferred query execution and reevaluation, or directly (to prove that the problem is not with editing):
new[]
{
new { Id = (object)DBNull.Value },
new { Id = (object)"whatever" }
}
.OrderBy(e => e.Id)
.ToList(); // Fail
The solution is to avoid DBNull at all. The easiest (and much better than as string or ToString()) with DataTable is to use DataRowExtensions.Field extension methods instead of object returning indexer, which besides providing strongly typed access to the columns also automatically handle DBNulls for you (converts them to null when you request string or nullable type), so you won't experience such issues.
It can be proved by changing your problematic code to
.OrderBy(r => r.Field<string>("ID"))
and the problem will be gone. I strongly recommend doing that for other column accessors as well.
The answer surprisingly to me is that LINQ reorders code. The context was this:
IEnumerable<DataRow> s = importTable.AsEnumerable();
IEnumerable<DataRow> t = s
.OrderBy(r => r["HALL"]);
IEnumerable<DataRow> sortedTable = t
.OrderBy(r =>
{ //if (r["ID"] is DBNull)
// return "";
//else
return r["ID"]; // ERROR
});
IEnumerable<DataRow> tue = sortedTable.Where(r => r["DAY"].Equals("TUE"));
IEnumerable<DataRow> wed = sortedTable.Where(r => r["DAY"].Equals("WED"));
AssignSections(tue);
AssignSections(wed);
The 3 commented lines indicate the fault. And what happened: sortedTable was partially initialized in order to feed the Where clause for initializing tue. But then the sortedTable was completed to initialize wed AFTER the call to assign wed appeared in the code, but just in time to use wed in the query constructed in AssignSections!
So the ERROR arose during AssignSections, when the code detoured to completing the initializing of sortedTable, and I could detect this by adding the 3 disabled lines and setting a breakpoint on the "return "";
Magic?
DBNull and null is not the same...
As your original error message says "Object must be of type string" (to be assigned to a string)
DBNull can't be cast to a string,it is a class...
You need to handle this case in your code.
See this link for a simple helper method:
Unable to cast object of type 'System.DBNull' to type 'System.String
using System;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
DBNull dbNull = DBNull.Value;
Console.WriteLine(typeof(string).IsAssignableFrom(typeof(DBNull)));//False
Console.WriteLine(dbNull is string); //False
//Console.WriteLine((string)dbNull); // compile time error
//Console.WriteLine(dbNull as string); // compile time error
Console.ReadLine();
}
}
}
Also, make sure you read how "Lazy Loading"/"Deferred Execution" works on LINQ/IEnumerable.
You don't have to use IEnumerable all the time,especially if you are not sure how it works.

PostgreSQL, Linq C# error 'could not determine data type of parameter $1'

I am getting this error "could not determine data type of parameter $1"
and my where clause where I am getting the error is like the following:
var result = from Table in model.Table
where (filter.XId.HasValue ? Table.XId == filter.XId: true)
select new TableEntity
{
ID = Table.XId
};
If my code was only like this 'Table.X == filter.X', it works ...
How can I fix this?
and I am getting this problem only with PostgreSQL database ....
First about what that error usually means. When making parametrized queries to PostgreSQL, all parameters should be referenced in query itself. When you add more parameters than used in query, usually the error above appears.
It seems that when whatever EF provider for PosgreSQL you use converted your statement to SQL, it created more parameters than needed.
In general, it might be hard for EF providers to analyze and correctly parse statements like you used, so good practice is to use statements that are "closer" to SQL in certain sense. In your case equivalent query which is "closer" to SQL would be:
where (filter.XId == null || Table.XId == filter.XId)
If you want to generate different queries based on the value of filter, you can do something like this:
var query = (IQueryable<Table>) model.Table;
if (filter.XId != null) {
query = query.Where(row => row.XId == filter.XId);
}
var result = query.Select(row => new TableEntity {
Id = row.XId
});
As it is the first SO answer in Google -
Marked answer didn't help me in similar case (EF6 + Postgre), so I had to use .AsEnumerable();
Yeah, it could be bad for perfomance but it fitted well in my case.
So this one would work:
var query = model.Table
.AsEnumerable()
.Where (p => filter.XId.HasValue ? p.XId == filter.XId: true);

Linq Where clause not returning what I expect when performing String.Contains(String) on a null string

I scratched my head for one hour on this yesterday with no results but sweat.
string SearchTag = "";
Extension.getDBService<MyClass>().FindAll(i => <true condition>);
This returned me all my MyClass DB records as I would expect.
string SearchTag = "";
Extension.getDBService<MyClass>().FindAll(i => <true condition> && i.TAG.ToLower().Trim().Contains(SearchTag.ToLower().Trim()));
This returned a 0 Count collection!! I do not understand this.
string SearchTag = "e";
Extension.getDBService<MyClass>().FindAll(i => <true condition> && i.TAG.ToLower().Trim().Contains(SearchTag.ToLower().Trim()));
This returns a collection containing all MyClass DB records again. This is normal as i.TAG always contains "e".
Why do I get a 0 members collection with the second expression?
"string".Contains("") should always be true right?
PS: Extension.getDBService() is a call to a DBContext by the way.
Thx for your assistance.
Interestingly, the way you wrote the LINQ query generates SQL CHARINDEX(...) > 0 criteria which returns false for empty string.
However, if you remove (move outside the query) the ToLower().Trim() part of the SearchTag variable
SearchTag = SearchTag.ToLower().Trim();
and use
i.TAG.ToLower().Trim().Contains(SearchTag)
inside the LINQ query, then the generated SQL criteria is LIKE operator and works as expected.
Just another example that LINQ to Entities is not like LINQ to Objects.

Query not executing and not getting the reults

const string keyword = "manoj";
rsp.DataSource = company.GetCompanySearch(keyword);
rsp.DataBind();
using (var context = huntableEntities.GetEntitiesWithNoLock())
{
IEnumerable<Company> query = context.Companies.ToList();
if (!string.IsNullOrEmpty(keyword))
{
keyword = keyword.ToLower();
query = query.Where(u=>u.CompanyName.Contains(keyword)
|| u.EmailAdress.Contains(keyword)
||u.MasterCountry.Description.Contains(keyword)
||u.MasterIndustry.Description.Contains(keyword)
||u.CompanyDescription.Contains(keyword)
||u.CompanyHeading.Contains(keyword));
}
return query.ToList();
}
Query not getting the results. The database has 27 records with name manoj.
The inner exception is null for this
It should work as expected if you change the line IEnumerable<Company> query = ... to
var query = context.Companies;
This turns query into an IQueryable<Company>, of which the expression will be extended by the line query = query.Where(.... This will be done in a way that the Contains expressions will be translated into SQL (LIKE).
In your current code you fetch all Companies into memory and the Contains is executed by linq-to-objects. This is case-sensitive, the reason why I think you did not get any matches. The SQL is probably case-insensitive. That depends on the collation of your database.

Problem accessing association from the result of a lambda query

Has anyone had problems gettting associations to load using LINQ to SQL when your child record was loaded via a lambda query? For example:
var orderLine = db.OrderLines.
Where(ol => ol.ID == orderLineID select ol).
First();
// navigate to order via the association
var order = orderLine.GetOrder();
What I get basically is a null result from GetOrder().
But if I do this instead:
var orderLine = (from ol in db.OrderLines where ol.ID == orderLineID).First();
var order = orderLine.GetOrder();
Works fine.
What can cause this? Is this a bug?
EDIT: Here's the actual code that WORKS with the Lambda expression commented out that DOESN'T WORK
var msg = db.Messages.Where(m => m.ID == msgID).First();
if (msg.SentTS.HasValue) return;
// Get the message recipients
// I don't get it.. why doesn't lambda expressions work here? returns 0 results!
// var testtos = msg.GetMessageTos.Where(mt => mt.Active);
var tos = from mt in db.MessagesTos
where mt.Active && mt.MessageID == msgID
select mt;
You can also try this, I think it's a little cleaner.
var orderLine = db.OrderLines.Single( ol => ol.ID == orderLineID );
var order = orderLine.GetOrder();
I beileive in your non-working example you want to use .First() instead of .Single().
It seems to me that the problem has more to do with the association than lambda expressions.
In your scenario, this should work:
var tos = db.MessagesTos.Where(mt=> mt.Active && mt.MessageID);
while this won't:
var tos = from mt in msg.SentTS
where mt.Active
select mt;
As to why it doesn't work, I suggest taking a look at the association in the designer and checking its matching the db model correctly (matching the correct columns). I also suggest to confirm that msg.SentTS is effectively coming empty, regardless of any further query you run on it.
See my EDIT for the code that works. I guess sometimes the "Answer" is to do what works, not necessarily what you understand.

Categories

Resources