EF- two WHERE IN clauses in an INCLUDE table produce two EXISTS in SQL sent to server - entity-framework

I have an INCLUDE table that I want to check a couple of values, in the same row, using an IN clause. The below doesn't return the correct result set because it produces two EXISTS clauses with subqueries. This results in the 2 values being checked independently and not strictly in the same child row. (forgive any typos as I'm typing this in from printed code)
var db = new dbEntities();
IQueryable<dr> query = db.drs;
// filter the parent table
query = query.Where(p => DropDown1.KeyValue.ToString().Contains(p.system_id.ToString()));
// include the child table
query = query.Include(p => p.drs_versions);
// filter the child table using the other two dropdowns
query = query.Where(p => p.drs_versions.Any(c => DropDown2.KeyValue.ToString().Contains(c.version_id.ToString())) && c => DropDown3.KeyValue.ToString().Contains(c.status_id.ToString()));
// I tried removing the second c=> but received an error "'c' is inaccessible due to its protection level" error and couldn't find an clear answer to how this related to Entity Framework
// query = query.Where(p => p.drs_versions.Any(c => DropDown2.KeyValue.ToString().Contains(c.version_id.ToString())) && DropDown3.KeyValue.ToString().Contains(c.status_id.ToString()));
This is an example of the query the code above produces...
SELECT *
FROM drs d
LEFT OUTER JOIN drs_versions v ON d.dr_id = v.dr_id
WHERE d.system_id IN (9,8,3)
AND EXISTS (SELECT 1 AS C1
FROM drs_versions sub1
WHERE d.tr_id = sub1.tr_id
AND sub1.version_id IN (9, 4, 1))
AND EXISTS (SELECT 1 AS C1
FROM drs_versions sub2
WHERE d.tr_id = sub2.tr_id
AND sub2.status_id IN (12, 7))
This is the query I actually want:
SELECT *
FROM drs d
LEFT OUTER JOIN drs_versions v ON d.dr_id = v.dr_id
WHERE d.system_id IN (9, 8, 3)
AND v.version_id IN (9, 4, 1)
AND v.status_id IN (12, 7)
How do I get Entity Framework to create a query that will give me the desired result set?
Thank you for your help

I'd drop all of the .ToString() everywhere and format your values ahead of the query to make it a lot easier to follow.. If EF is generating SQL anything like what you transcribed, you are casting to String just to have EF revert it back to the appropriate type.
From that it just looks like your parenthesis are a bit out of place:
I'm also not sure how something like DropDown2.KeyBalue.ToString() resolves back to what I'd expect to be a collection of numbers based on your SQL examples... I've just substituted this with a method called getSelectedIds().
IEnumerable<int> versions = getSelectedIds(DropDown2);
IEnumerable<int> statuses = getSelectedIds(DropDown3);
query = query
.Where(p => p.drs_versions
.Any(c => versions.Contains(c.version_id)
&& statuses.Contains(c.status_id));
As a general bit of advice I suggest always looking to simplify the variables you want to use in a linq expression as much as possible ahead of time to keep the text inside the expression as simple to read as possible. (avoiding parenthesis as much as possible) Make liberal use of line breaks and indentation to organize what falls under what, and use the code highlighting to double-check your closing parenthesis that they are closing the opening you expect.
I don't think your first example actually was input correctly as it would result in a compile error as you cannot && c => ... within an Any() block. My guess would be that you have:
query = query.Where(p => p.drs_versions.Any(c => DropDown2.KeyValue.ToString().Contains(c.version_id.ToString())) && p.drs_versions.Any(c => DropDown3.KeyValue.ToString().Contains(c.status_id.ToString()));
Your issue is closing off the inner .Any()
query.Where(p => p.drs_versions.Any(c => DropDown2.KeyValue.Contains(c.version_id))
&& DropDown3.KeyValue.Contains(c.status_id)); //<-- "c" is still outside the single .Any() condition so invalid.
Even then I'm not sure this will fully explain the difference in queries or results. It sounds like you've tried typing across code rather than pasting the actual statements and captured EF queries. It may help to copy the exact statements from the code because it's pretty easy to mistype something when trying to simplify an example only to find out you've accidentally excluded the smoking gun for your issue.

Related

How to format a number in Entity Framework LINQ (without trailing zeroes)?

In a SQL Server database I have a column of decimal datatype defined something like this:
CREATE TABLE MyTable
(
Id INT,
Number DECIMAL(9, 4)
)
I use Entity Framework and I would like to return column Number converted to a string with only the digits right of the decimal separator that are actually needed. A strict constraint is that a result must be an IQueryable.
So my query is:
IQueryable queryable = (
from myTable in MyDatabase.NyTable
select new
{
Id = myTable.Id,
Number = SqlFunctions.StringConvert(myTable.Number,9,4)
}
);
The problem with is that it always convert number to string with 4 decimals, even if they are 0.
Examples:
3 is converted to "3.0000"
1.2 is converted to "1.2000"
If I use other parameters for StringConvert i.e.
SqlFunctions.StringConvert(myTable.Number, 9, 2)
the results are also not OK:
0.375 gets rounded to 0.38.
StringConvert() function is translated into SQL Server function STR.
https://learn.microsoft.com/en-us/sql/t-sql/functions/str-transact-sql?view=sql-server-2017
This explains the weird results.
In the realm of Entity Framework and LINQ I was not able to find a working solution.
What I look for is something like C# function
String.Format("0.####", number)
but this cannot be used in a LINQ query.
In plain simple SQL I could write my query like this
SELECT
Id,
Number = CAST(CAST(Number AS REAL) AS VARCHAR(15))
FROM
MyTable
I have not managed to massage LINQ to produce query like that.
A workaround would be to forget doing this in LINQ, which is quite inflexible and messy thing, borderline on useless and just return type DECIMAL from database and do my formatting on a client side before displaying. But this is additional, unnecessary code and I would hate to di it that way if there perhaps is a simpler way via LINQ.
Is it possible to format numbers in LINQ queries?
I would absolutely return a decimal from he database and format it when needed. Possible directly after the query. But usually this is done at display time to take into account culture specific formatting from the the client.
var q =
(from myTable in MyDatabase.NyTable
select new
{
Id = myTable.Id,
Number = myTable.Number
})
.AsEnumerable()
.Select(x => new { Id = x.Id, Number = x.Number.ToString("G29") });

Searching a column in Entity Framework with multiple values

I am trying to run a search on one particular field of a table with a list of values. Not able to find a solution so far. Any help is really appreciated.
Here is the scenario
var records = new PagedList<Required>();
var result = db.Required.Where(x => filter == null || (x.Title.Contains(filter)) || x.CID.Contains(filter));
foreach (string str in SelectedNetwork)
{
string tempStr = str;
result = result.Where(x => x.Network == tempStr);
records.TotalRecords = result.Count();
}
records.Content = result
.Where(x => filter == null ||
(x.Title.Contains(filter))
|| x.CID.Contains(filter)
)
.OrderBy(sort + " " + sortdir)
.Skip((page - 1) * Convert.ToInt32(records.PageSize))
.Take(Convert.ToInt32(records.PageSize))
.ToList();
highlighted code in the foreach loop fails to run as per expectation. Is there any way, I can fix it?
Thanks
Tutumon
You must take into account that LINQ expressions are queries, until you materialize them. To materialize them you need to either enumerate them, or convert them to a list, array, or whatever, i.e. enumerate their members in a foreach, or call a method like ToList(), or ToArray().
In your code the original query stored in result is not materialized, so everytime a foreach loop is executed, a new Where contidion is added to the original query. To vaoid this behavior you need to recreate the whole results query in each iteration, so that you get a fresh copy of the unfilterd expression.
There would be another solution which would be to materialize the result query and then run the foreach loop as is. The problem of this solution would be that you would get all the data from the database, keep it in memory and run the Where and the Count on the in-memory copy. Unless there is a very small number of rows in Required that would be a very bad idea.

Sub Query in select clause with Squeryl

I'm trying to replicate the following query usine Squeryl.
SELECT c.order_number,p.customer,p.base,(
SELECT sum(quantity) FROM "Stock" s where s.base = p.base
) as stock
FROM "Card" c, "Part" p WHERE c."partId" = p."idField";
I have the following code for selecting the Cards and Parts but I cannot see a way to add a sumation into the select clause.
from(cards, parts)((c,p) =>
where(c.partId === p.id)
select(c,p)
Any help is much appreciated!
In Squeryl, you can use any Queryable object in the from clause of your query. So, to create a subquery, something like the following should work for you:
def subQuery = from(stock)(s => groupBy(s.base) compute(sum(s.quantity)))
from(cards, parts, subquery)((c, p, sq) =>
where(c.partId === p.idField and sq.key === p.base)
select(c.orderNumber, p.customer, sq.measures))
Of course the field names may vary slightly, just guessing at the class definitions. If you want the whole object for cards and parts instead of the single fields from the original query - just change the select clause to: select(c, p, sq.measures)

Linq-to-Entities match ID in list of integers in a compiled query

I'm trying to find a way to implement a Linq-to-Entities compiled query of the form:
Func<MyEntities, List<int>, IQueryable<MyClass>> query = System.Data.Objects.CompiledQuery.Compile(
(MyEntities entities, List<int> IDs) => (
(from au in entities.Mine where IDs.Any(x => x == au.ID) select au)
));
Because only scalar parameters can be passed to CompiledQuery.Compile the above fails. I'm trying to find some clever way to pass a comma delimited list of integers as a string and use that in the L2E query along the lines of:
Func<MyEntities, string, IQueryable<MyClass>> query = System.Data.Objects.CompiledQuery.Compile(
(MyEntities entities, string IDs) => (
(from au in entities.Mine where IDs.Split(',').Any(x => Convert.ToInt32(x) == au.ID) select au)
));
But that doesn't work due to the unsupported Split function.
Any clever ideas about how this could be implemented?
I'm not sure if this could possibly work, but maybe try to use join of List<int> IDs and MyEntities entities?
I ended up finding a way to do it but it's way too slow at around 3s.
string ids = "|" + String.Join("|", new List<int> { 4, 5, 6, 7, 8, 9, 10, 20, 23, 34 }) + "|";
Func<MyEntities, string, IQueryable<MyClass>> query = System.Data.Objects.CompiledQuery.Compile(
(MyEntities entities, string IDs) => (
(from au in entities.Mine where IDs.Contains("|" + SqlFunctions.StringConvert((decimal)au.ID).Trim() + "|")select au)
));
Back to the drawing board.
You can't.
What does CompiledQuery do? It pre-converts the query to a canonical command tree -- an intermediate representation used to generate SQL by the provider.
The SQL for a command with a scalar param is the same structure regardless of the param value. But the SQL generated for, say, a list of 2 items will be structurally different from the SQL afor a list of 3 items, because it takes one fewer OR predicate in its WHERE clause. Most DB servers won't take lists as a param value, after all.
Your string/| kludge works because you're now passing only one param, not a list, to the DB server. But as you've seen, the server can't index such a query, so it will be slow.

Zend Framework: Re-use WHERE clause returned from Zend_Db_Select::getPart()

I have a SELECT object which contains a WHERE.
I can return the WHERE using getPart(Zend_Db_Select::WHERE), this returns something like this:
array
0 => string "(clienttype = 'agent')"
1 => string "AND (nextpayment < (NOW() - INTERVAL 1 DAY))"
This array seems pretty useless as I cannot do this with it
$client->update(array("paymentstatus" => "lapsed"), $where);
Or even put it into another SELECT object. Is there a way of getting a more useful representation of the WHERE statement from the SELECT object?
Thanks
EDIT
The best I have come up with so far is
$where = new Zend_Db_Expr(implode(" ", $select->getPart(Zend_Db_Select::WHERE)));
Your first choice, $client->update(...) would work, if getParts omitted the 'AND' from the second part of the where clause.
I'm pretty sure your only choice is to:
use your second option (probably safest depending on how complex the where clauses are) -or-
iterate through the $where returned and remove the leading 'AND', 'OR' that may exist.