NHibernate / QueryOver: How to left join with parameter - c#

Is there a way in NHibernate / QueryOver API to pass a parameter already in the mapping (so it uses the parameter as a fixed value for all queries on this particular instance).
I need this (or a workaround) because I have a view like this in the Database:
CREATE VIEW ProblematicView
AS
SELECT
v.*,
-- lots of data
FROM someView v
LEFT JOIN someTable t ON v.ForeignKey = t.ForeignKey
Now additionally to the ForeignKey match I need an additional check for a property like this:
AND t.SomeOtherValue = #myParameter
which is not possible as there is no way to pass parameters to a view directly. With a table valued function this would be possible but then I don´t know how to map it to NHibernate / QueryOver.
Also the function approach would be hard to realize as a huge QueryOver statement is used to filter all the remaining properties (as the view is used for searching business entities)
Currently I am applying the SomeOtherValue / #myParameter filter to the the entire view as a part of my QueryOver.
This is my main problem:
Using for example:
SELECT
v.*,
-- lots of data
FROM someView v
LEFT JOIN someTable t ON v.ForeignKey = t.ForeignKey AND t.SomeOtherValue = 123
(followed by alot of other property checks...)
will return a different result (NULL entries for t.SomeOtherValue included on intent due to left join)
than using:
SELECT * FROM ProblematicView where SomeOtherValue = 123
(followed by alot of other property checks)
As now the left join happens inside the view without checking for SomeOtherValue and as the SomeOthervalue check is applied independent on the left join, all the NULL values will be excluded (which is wrong business logic).
Also using:
SELECT * FROM ProblematicView where SomeOtherValue = 123 OR SomeOtherValue = NULL
(followed by alot of other property checks)
does not seem to help, as NULL values are still ignored...
So the only way I can imagine to solve this problem would be to find a way to pass my SomeOtherValue property somehow to the view so that it can use it as a parameter in the view itself (instead of in the where clause) or maybe somehow use an sql table based function with parameter for the model...
EDIT:
After some more research I managed to hopefully simplify the problem:
I am trying to convert this SQL:
Select v.*, ... from (someView v LEFT JOIN someTable t ON v.ForeignKey = t.ForeignKey)
WHERE SomeOtherValue = 123
(where SomeOtherValue comes from someOtherTable)
to this:
Select v.*, ... from someView v LEFT JOIN someTable t on v.ForeignKey = t.ForeignKey
AND t.SomeOtherValue = 123
using NHibernate / QueryOver. Note that in the second version the property SomeOthervalue is checked against directly within the left join, where in the first version it is incorrectly applied only after the left join.
I need to find a way to write latter SQL statement in a way that I can put it inside a view while still being able to pass 123 as parameter for SomeOtherValue.

You can add a condition to a join clause using the overload of JoinQueryOver or JoinAlias that takes a withClause parameter. For example:
SomeTable stAlias = null;
session.QueryOver<ProblematicView>()
.Left.JoinAlias(
pv => pv.SomeTable, // Join path
() => stAlias, // alias assignment
st => st.SomeOtherValue == 123) // "with" clause
/* etc. */
The "with" portion of the QueryOver join is what will add a condition to the left outer join in SQL. The above should generate this:
SELECT /* select list */
FROM [ProblematicView] this_
left outer join [SomeTable] sometable1_
on this_.Id = sometable1_.ProblematicViewId
and (sometable1.SomeOtherValue = 123 /* #p0 */)
If I understand correctly, this should help solve your problem.
A few things to note about adding a "with" clause:
Interestingly, all of the overloads of JoinQueryOver and JoinAlias that allow you to specify a "with" clause require that you assign an alias when you do the join.
You cannot (as far as I know) generate SQL with an or in the join condition. the "with" clause is always anded with the mapped join condition (i.e., FK → PK)

Related

MSSQL Query Not Returning Null Columns/Results

I'm kind of new to SQL queries so this is probably something easily fixed, but I can't seem to find any answers.
I've got a bunch of related tables. E.g.
SPClients contains iClientID and sName, SPDocTypes contains iDocTypeID and sName, SPCreditor contains iCreditorID and sName
and then
SPDocIndex contains iIndexID, and then foreign keys to iClientID, iDocTypeID and iCreditorID listed above.
If I do a simple SELECT * FROM SPDocIndex I get all results, with just the IDs being displayed, which isn't much use when bound to a datagrid. So I use an inner join so the actual names appear rather than just their IDs, like so:
SELECT * FROM SPDocIndex
INNER JOIN SPClients ON SPDocIndex.iClientID=SPClients.iClientID
INNER JOIN SPDocType ON SPDocIndex.iDocTypeID=SPDocType.iDocTypeID
INNER JOIN SPCreditor ON SPDocIndex.iCreditorID=SPCreditor.iCreditorID
And the query "works" but it only returns rows that have data in all three columns. If the iCreditorID column of SPDocIndex is null, then that row is NOT returned...but I would like all rows to be returned irrespective of whether the columns are null or not.
Benny - some of the others responded in the comments, that you'll need to adjust your join to a left join instead of an inner join; indeed, that is the case here. Please check out this link for a quick tutorial on the differences between SQL joins.
https://www.w3schools.com/sql/sql_join.asp
Inner Join will always return the return the matching records within two table. In order to get the all the records from first table and matching record of second table you must use left join as shown below. Please try below and let me know if you have any further issues.
SELECT * FROM SPDocIndex
LEFT JOIN SPClients ON SPDocIndex.iClientID=SPClients.iClientID
LEFT JOIN SPDocType ON SPDocIndex.iDocTypeID=SPDocType.iDocTypeID
LEFT JOIN SPCreditor ON SPDocIndex.iCreditorID=SPCreditor.iCreditorID

EntityFramework Linq Left Join Parsing Error - DotNet Core 1.0

I'm attempting to explicitly join 3 tables using a left outer join in a linq query and am running into linq parsing issues. Performing an inner join parses correctly and returns data but using the left outer fails.
Example:
var query = from p in DatabaseContext.Products
where p.ClientID == clientID
join l in DatabaseContext.Licenses on p.ProductID equals l.ProductID into pl
from pli in pl.DefaultIfEmpty()
join a in DatabaseContext.Articles on p.ArticleID equals a.ArticleID into pa
from pai in pa.DefaultIfEmpty()
select new SomeEntityDTO
{
SomethingFromP = p.Something,
SomethingFromL = pli.Something,
SomethingFromA = pai.Something
};
As both joined tables key off of the first table, I can test each individually by removing the other join, e.g., test the query for p to l and then for p to a. These test queries function perfectly. It's also possible to remove the left outer rule and receive a proper result.
var query = from p in DatabaseContext.Products
where p.ClientID == clientID
join l in DatabaseContext.Licenses on p.ProductID equals l.ProductID
join a in DatabaseContext.Articles on p.ArticleID equals a.ArticleID
select new SomeEntityDTO
... the rest ...
Viewing the offending query in SQL Profiler (top code example) I see that the first two tables are successfully joined, e.g.:
SELECT p.Something, l.Something
FROM Products AS p
LEFT JOIN Licenses AS l ON p.ProductID = l.ProductID
WHERE p.ClientID = 5
ORDER BY p.ProductID
And, then right after this successful query, are another 2 queries (identical to each other):
SELECT a.ArticleID, a.Something, <all fields, even when not specified in query>
FROM Articles AS a
ORDER BY a.ArticleID
The outer joined 3 tables will successfully return an object, as long as I don't attempt to access a field from the "a" table. When doing that, I recieve a Null Exception error, as that table was never really joined.
As stated, removing the outer join rule brings back a successfully joined query.
I have attempted to adjust the linq query figuring that the Linq parser had an issue, but to no avail:
var query = from p in DatabaseContext.Products
from l in DatabaseContext.Licenses.Where(g => g.ProduktID == p.ProduktID).DefaultIfEmpty()
from a in DatabaseContext.Articles.Where(g => g.ArticleID == p.ArticleID).DefaultIfEmpty()
where ....
This parses to a set of CROSS APPLYs that doesn't function at all and the profiled query, when copied into a query editor window, doesn't run at all (as opposed to the 3 individual queries seen in the profiler for the first code example). I have also attempted the more complicated lambdas, which also doesn't work.
Is this an error in the Linq parser? Am I doing this completely wrong? (According to the multiple answered questions here on explicit left outer joins (as opposed to natural associations), I'm doing it correctly. But, it doesn't parse correctly. I've avoided creating the associations so I can join them without explicitly defining the join. Is that potentially required here and won't work properly without it?
Note: Each table has complex keys but I only really need to join based on single key values (the DB is part of a product I can't change).
Using. DotNet Core, EntityFramework, EntityFrameworkCore.SqlServer, etc., all version 1.0.1.
Help?
The short answer is to use EF6 instead of EFCore if you absolutely have to have complex Linq queries on your entites, even after the 1.1 release. There are still too many things missing in EFCore compared to EF6.
Roadmap here.
In my case, I kept EFCore and used the Context.Entity.FromSql(query) method in order to get the results. This allowed me to utilize EFCore for most of the EF Entities, and thereby keeping a forward-looking approach to the application, while allowing for special exceptions for complicated queries not based on an actual entity. The plan is to replace those FromSql queries as EF Core matures.
Prior to deciding on .FromSql, I also tested a query on a View and on a stored procedure. In both instances, I failed. For stored procedures, named parameters is not yet implemented, and views are not currently supported unless you attempt to trick EF into thinking the view is actually a table (which brings its own issues).
In order to access EF Core .FromSql, you need to install the following package:
Microsoft.EntityFrameworkCore.Relational

How do I do a right join and return only null values in EntityFramework?

I have two tables, NAttrValues and NAttrTitles. The NAttrTitles have an ID, which is referenced in the NAttrValues table by the column 'TitleID'. I'm trying to return the elements in NAttrTitles that do not have any associated NAttrValues, using LINQ in EntityFramework.
This SQL query returns exactly what I want
SELECT * FROM NAttrValues nav RIGHT JOIN NAttrTitles nat ON nav.TitleID = nat.ID WHERE nav.TitleID IS NULL
How do I write this in EntityFramework? I've tried various different uses of .DefaultIfEmpty() but they all end up returning either the wrong thing, or nothing at all.
Right Outer Join is the same as Left Outer Join with left and right sides swapped. So the LINQ To Entities equivalent of your SQL query is:
var query =
from nat in db.NAttrTitles
join nav in db.NAttrValues on nat.ID equals nav.TitleID into nat_nav
from nav in nat_nav.DefaultIfEmpty()
where nav == null
select nat;

Why is LinqToEntities Skip/Take oracle implementation so slow

Context is an Oracle Database, Entity Framework 5, LinqToEntities, Database First.
I'm trying to implement pagination over some large tables, my linqToEntities query looks like this :
context.MyDbSet
.Include("T2")
.Include("T3")
.Include("T4")
.Include("T5")
.Include("T6")
.Where(o => o.T3 != null)
.OrderBy(o => o.Id)
.Skip(16300)
.Take(50);
Fact is, depending on how many records i wanna skip (0 or 16300) it goes from 0.08s to 10.7s.
It seems weird to me so i checked the generated SQL and here is how it looks like :
SELECT *
FROM (
SELECT
[...]
FROM ( SELECT [...] row_number() OVER (ORDER BY "Extent1"."Id" ASC) AS "row_number"
FROM T1 "Extent1"
LEFT OUTER JOIN T2 "Extent2" ON [...]
LEFT OUTER JOIN T3 "Extent3" ON [...]
LEFT OUTER JOIN T4 "Extent4" ON [...]
LEFT OUTER JOIN T5 "Extent5" ON [...]
LEFT OUTER JOIN T6 "Extent6" ON [...]
WHERE ("Extent1"."SomeId" IS NOT NULL)
) "Filter1"
WHERE ("Filter1"."row_number" > 16300)
ORDER BY "Filter1"."Id" ASC
)
WHERE (ROWNUM <= (50) )
I checked if it actually was Oracle who took time through SQL Developper, and it is.
I then checked the execution plan and it's what appears :
All i actually understand is that there's no STOPKEY for the first filter over row_number and that it probably fetch the whole sub query.
I don't really know where to look at, if i'm not mistaken, the request is generated by ODT/ODP/.. and thus, should be optimized for an Oracle DB.. (and i can't change it myself)
Maybe my database model is rotten and i could add whetever indexes or optimization for it to work better ?
You see all those UNIQUE SCAN subqueries? That's doing a UNIQUE on every single one of those tables.
You should have a one-to-many relationship with T1 being the parent, and any tables you need to be in the query to have a FOREIGN KEY relationship to an indexed ID primary key column on T1.
Then you can use Join instead of Include and the subqueries will be unnecessary.

linq to entity stored procedure return that needs extra joins after the fact

I have a stored procedure that returns the table type. However, the EF model that has the stored procedure, only returns the present table class, but no associated classes. So when I traverse my referenced (child/parent) tables, I get nulls everywhere for those referenced table values.
I know this is expected as the stored procedure returns only that table (select * from that table), but I now would like to create a join clause AFTER calling the store procedure to get access to the rest of the referenced tables in my EF model.
Here is my code , slightly modified to make it easy to understand what I need...
var coll = db.SearchContacts(keyword,param1,param2)
//returns Contact types,works great
...now what I would like to do....(although I know syntax is off)
var ExtColl = coll join db.Address on coll.Address
join db.Department on coll.Department <- something similar
...so that I may now have access to that department (department.desc),
or address (address.civic) info from this contact.
I am at a loss as to how to implement this style of formatting AFTER I have already received it from the stored proc in the db.
Ok, so here is an update of what I am doing...
var ps = from d in db.SearchContacts(param1,param2)
select d.Id;
var p = from e in db.Contacts
.Include("Table1Ref")
.Include("Table2Ref")
.Include("Table3Ref")
Where(BuildOrExpression<DBMOdel.Contact,long>(e=>e.Id,ps.ToList()))
select e;
Now I am getting the return I want except for one thing....
there are tables that reference further referenced tables
that I would like to add, and Include does not allow
me to add them unto Contacts directly as these tables are not
referencing Contact but another table that references Contact
one layer/level further...so contacts references address
and address references province...
and I need to get access to the province info (such as description)
but need to finish my join statement properly....
So I am thinking of using join statement, but do not know the syntax for assigning or joining of this kind...
Contact -> Address on AdressId, Address -> Province on ProvinceId
SO i am thinking my linq statement would look like this...
var p = from e in db.Contacts
.Include("Table1Ref")
.Include("Table2Ref")
.Include("Table3Ref")
Where(BuildOrExpression<DBMOdel.Contact,long>(e=>e.Id,ps.ToList()))
-> join o in db.Province on o.ProvinceId equals e.Address.ProvinceId
select e;
but am not getting the desired effect...can anyone help?
I think I might be also mixing linq to sql or object with linq to entity...
this is the error I am getting for it
"The key selector type for the call to the 'Join' method is not comparable in the underlying store provider."
There is no SQL syntax to join tables to stored procedures, so EF won't be able to do this either.
Alternatives:
Do the joins in memory (linq to objects).
Do the joins in a stored procedure and map it to a complex type.
The latter option is likely to perform (much) better, but is less flexible. It's up to you to weigh the pros and cons.

Categories