NH QueryOver - use properties of main query in subquery - c#

I am trying to convert following SQL to QueryOver:
Select 1
From myTable mt
Where mt.ForeignKey in (select ID from otherTable ot where ot.ID = R.ID)
I want to use this subquery inside an EXISTS / NOT EXISTS statement like:
select * from table R where .... AND EXISTS (query above)
Currently I have something like:
mainQuery.WithSubquery.WhereExists(QueryOver.Of<myTable>()
.Where(mt => mt.ForeignKey)
.WithSubquery.IsIn(QueryOver.Of<otherTable>().Where(c => c.Id == R.SomeId)));
I created this query as a subquery which I want to connect to the main query.
The problem is that the table aliased as R is the table called by the main query and I don´t know how to access columns of the table (NHibernate Model) R (which is not accesible in the query above), so my question is:
How can I get values from the main query and use them in a subquery. I think this is only possible by creating the subquery inline (as in mainQuery.WithSubquery.Where(..) or smth. similar) but I can´t see what would be the best possible way to do so. I appreciate any help!
Thanks in advance!

The trick is to use proper alias, for the parent query:
// the alias
myTable R = null;
mainQuery
.WithSubquery
.WhereExists(QueryOver
.Of<myTable>( () => R) // the Alias in place
.Where(mt => mt.ForeignKey)
.WithSubquery.IsIn(QueryOver.Of<otherTable>().Where(c => c.Id == R.SomeId)));
Note, not fully sure about the mainQuery part, but the solution in general here is like this:
// I. the outer query ALIAS
Employee emplyoee = null;
// II. the subquery - using the alias
var subQuery = QueryOver.Of<Contact>()
.Select(x => x.ID)
.Where(x => x.Related.ID == emplyoee.ID); // use alias
// III. declare the outer query and use the above alias
var query = session.QueryOver<Employee>(() => emplyoee) // declare alias
.WithSubquery
.WhereExists(subQuery); // put both together
Also check this for more ideas

Related

What is lambda equivalent an SQL "in" statement?

I can't figure out a lambda equivalent of this sql statement:
select * from Document
where Document.OrginalDocumentNumber
in (select documentAccess.DocumentId from documentAccess where userId='1')
The problem is that Document & documentaccess tables have no relation to each other.
Any help would be so much appreciated.
Replace IN with EXISTS and you get following:
from d in dbContext.Documents
where dbContext.documentAccesses.Any(
x=>x.DocumentId == d.OrginalDocumentNumber && x.userId == '1' )
select d
Normally, if you have sensible navigation properties, you can avoid join or sub-queries directly:
var documents = from documentAccess in contex.DocumentAccesses
where documentAccess.UserId == 1
select documentAccess.Document;
You may want to use .Distinct() on the results, depending on your data.
Similarly:
var documents = contex.DocumentAccesses
.Where(access => access.UserId == 1)
.Select(access => access.Document);
And even better, if you already have a User in context:
var documents = currentUser.DocumentAccesses.Select(access => access.Document);

Hierarchical Structures Database Select By Lambda Expression

Hierarchical Structures Database Select By Lambda Expression
Hi My Table Structure is recursive
ID
ParentID
UserGROUPNAME
How Can I select Subset an Id from my contex By Lambda Expression like this
I Try to select first
var keys = db.UsersGroups.Select(x => x).Where(u => u.GroupMasterID == 1).ToArray();
var UsersGroup = db.UsersGroups.Where(x => keys.Contains(x.GroupMasterID));
But I think it's return wrong or return just 2 level data
Please Help me
That´s something that you cannot do, without mess it up, with linq. What you have to do is to flatten the hierarchy using a probably a common table expression and then you will be able to write a simple linq query.
finally I create a sql function
like this
Create FUNCTION [dbo].[GET_USERGROUPLIST]
(
#GROUPID INT
)
RETURNS TABLE
AS
RETURN
(
WITH UsersGroups_CTE AS (
SELECT *
FROM [dbo].[UsersGroups]
WHERE ID = #GROUPID
UNION ALL
SELECT e.ID, e.GroupName, e.ParentID ,e.Status
FROM [dbo].[UsersGroups] e
INNER JOIN UsersGroups_CTE ucte ON ucte.ID = e.ParentID
)
SELECT *
FROM UsersGroups_CTE Where UsersGroups_CTE.Status = 1
);
and Next Use in my model like this
db.GET_USERGROUPLIST(1);
Many Thanks to lontivero for him suggestion

How to write an linq statement to get the last of a group of records

I have 2 SQL statements that basically do the same thing, that is, retrieve the last record from a table based on a datetime field for a group of records. I am using the data-first Entity Framework model. How would I write either of these SQL statements using LINQ Lambda functions?
ie,
var u = db.AccessCodeUsage.Where(...).GroupBy(...)
rather than
var u = from a in db.AccessCodeUsage
where ...
group by ...
SQL Statements:
SELECT *
FROM AccessCodeUsage a
WHERE NOT EXISTS (SELECT 1
FROM AccessCodeUsage
WHERE LocationId = a.LocationId
AND Timestamp > a.Timestamp)
SELECT a.*
FROM AccessCodeUsage a
WHERE a.Timestamp =
(SELECT MAX(Timestamp)
FROM AccessCodeUsage
WHERE a.LocationId = LocationId
AND a.AccessCode = AccessCode
GROUP By LocationId, AccessCode)
If you need to have the method-call form, but are finding it tricky to work out, then use the other syntax first:
from a in db.AccessCodeUsage
orderby a.TimeStamp descending
group a by a.LocationId into grp
from g in grp select g.First();
Then convert to method calls by taking each clause one at a time:
db.AccessCodeUsage
.OrderByDescending(a => a.TimeStamp)
.GroupBy(a => a.LocationId)
.Select(g => g.First());
From which I can workout the second without bothering to write out the linq-syntax form first:
db.AccessCodeUsage
.OrderByDescending(a => a.TimeStamp)
.GroupBy(a => new {a.LocationId, a.AccessCode})
.Select(g => g.First());
(Except it doesn't include what may be a bug, in that if timestamps aren't guaranteed unique, the SQL given in the question could include some extra inappropriate results).
I can't check on the SQL produced right now, but it should hopefully be equivalent in results (if not necessarily matching). There's cases where grouping doesn't translate to SQL well, but I certainly don't think this would be one.
I ended up using the following which corresponds to the first SQL statement.
// Retrieve only the latest (greatest value in timestamp field) record for each Access Code
var last = AccessCodeUsages.Where(u1 => !AccessCodeUsages
.Any(u2 => u2.LocationId == u1.LocationId &&
u2.AccessCode == u1.AccessCode &&
u2.Timestamp > u1.Timestamp));

How to force LINQ to SQL to evaluate the whole query in the database?

I have a query which is fully translatable to SQL. For unknown reasons LINQ decides the last Select() to execute in .NET (not in the database), which causes to run a lot of additional SQL queries (per each item) against database.
Actually, I found a 'strange' way to force the full translation to SQL:
I have a query (this is a really simplified version, which still does not work as expected):
MainCategories.Select(e => new
{
PlacementId = e.CatalogPlacementId,
Translation = Translations.Select(t => new
{
Name = t.Name,
// ...
}).FirstOrDefault()
})
It will generates a lot of SQL queries:
SELECT [t0].[CatalogPlacementId] AS [PlacementId]
FROM [dbo].[MainCategories] AS [t0]
SELECT TOP (1) [t0].[Name]
FROM [dbo].[Translations] AS [t0]
SELECT TOP (1) [t0].[Name]
FROM [dbo].[Translations] AS [t0]
...
However, if I append another Select() which just copies all members:
.Select(e => new
{
PlacementId = e.PlacementId,
Translation = new
{
Name = e.Translation.Name,
// ...
}
})
It will compile it into a single SQL statement:
SELECT [t0].[CatalogPlacementId] AS [PlacementId], (
SELECT [t2].[Name]
FROM (
SELECT TOP (1) [t1].[Name]
FROM [dbo].[Translations] AS [t1]
) AS [t2]
) AS [Name]
FROM [dbo].[MainCategories] AS [t0]
Any clues why? How to force the LINQ to SQL to generate a single query more generically (without the second copying Select())?
NOTE: I've updated to query to make it really simple.
PS: Only, idea I get is to post-process/transform queries with similar patterns (to add the another Select()).
When you call SingleOrDefault in MyQuery, you are executing the query at that point which is loading the results into the client.
SingleOrDefault returns IEnumerable<T> which is no longer an IQueryable<T>. You have coerced it at this point which will do all further processing on the client - it can no longer perform SQL composition.
Not entirely sure what is going on, but I find the way you wrote this query pretty 'strange'. I would write it like this, and suspect this will work:
var q = from e in MainCategories
let t = Translations.Where(t => t.Name == "MainCategory"
&& t.RowKey == e.Id
&& t.Language.Code == "en-US").SingleOrDefault()
select new TranslatedEntity<Category>
{
Entity = e,
Translation = new TranslationDef
{
Language = t.Language.Code,
Name = t.Name,
Xml = t.Xml
}
};
I always try to separate the from part (selection of the datasources) from the select part (projection to your target type. I find it also easier to read/understand, and it generally also works better with most linq providers.
You can write the query as follows to get the desired result:
MainCategories.Select(e => new
{
PlacementId = e.CatalogPlacementId,
TranslationName = Translations.FirstOrDefault().Name,
})
As far as i'm aware, it's due to how LINQ projects the query. I think when it see's the nested Select, it will not project that into multiple sub-queries, as essentially that would be what would be needed, as IIRC you cannot use multiple return columns from a sub-query in SQL, so LINQ changes this to a query-per-row. FirstOrDefault with a column accessor seems to be a direct translation to what would happen in SQL and therefore LINQ-SQL knows it can write a sub-query.
The second Select must project the query similar to how I have written it above. It would be hard to confirm without digging into a reflector. Generally, if I need to select many columns, I would use a let statement like below:
from e in MainCategories
let translation = Translations.FirstOrDefault()
select new
{
PlacementId = e.CatalogPlacementId,
Translation = new {
translation.Name,
}
})

LinqToSql query that spans a many-to-many relation?

Let say i have the following schema
Content(Id, ....)
TagContent(TagId, ContentId)
Tag(TagId, Name)
Suppose I'd like to select all content records that have tag with name "test".
In SQL I would write:
select Content.Id
from Content
join TagContent as TC on (TC.ContentId = Content.Id)
Join Tag on (TC.TagId = Tag.Id)
where Tag.Name = 'Test'
Could you suggest how to write a similar query in Linq if you have only Table available?
(I'd like to create an extension method Content.ByTag('tag') -> IQueryable )
I've only managed to create a query that use the sql exists statement instead of join.
Which means that the queries are extremely inefficient.
My current inefficient solution looks as follows:
DataContext.Contents.Where(c => c.TagContents.Any(tc => tc.Tag.Name == "Test"))
NOTE:
As I'd like to make the extension method on DataContext.Contents I won't have access to other tables that is DataContext.Tag and DataContext.ContentTag.
Something like this perhaps
var contentIds = from c in Content
join tc in TagContent on c.Id equals tc.ContentId
join t in Tag on tc.TagId equals t.Id
where t.Name == "Test"
select new
{
ContentId = c.Id
};

Categories

Resources