Passing hint to nhibernate using QueryExpressionPlan - c#

I am using NHibernate's QueryExpressionPlan to convert Hibernate Query Language to SQL.
Here's a sample code in C#:
var hql = SELECT _table0.Id FROM MyTable1 _table0 WHERE 1=1 and _table0.Address.ZipCode LIKE '%58745%' ;
var sql = NHibernate.Engine.Query.QueryExpressionPlan
(new StringQueryExpression(hql), false, session.EnabledFilters, sessionFactory) ;
Here's the generated SQL. NHibernate is not able to add INNER JOIN to the generated SQL and returns records in comma separated format as shown below.
select myTable1.AssetId as col_0_0_ from MyTable1 myTable1, Address address1_
where myTable1.AddressId=address1_.AddressID
and 1=1 and (address1_.ZipCode like '%58745%');
My query is what should I add to the QueryExpression so that it automatically adds inner join to the generated sql.
Or is it that NHibernate won't add the INNER JOIN by itself, and somehow i need to get that information myself and pass it to NHibernate.

Yes, you can define a join on NHibernate code using the inner join on the HQL statement. Take a look at the 15.3. Associations and joins on the documentation.
Remember that the HQL is Hibernate Query Language and it is based on your entity model (mapped classes) and not on your relation model (tables).
Remeber to bind the parameters on your hql statement and set it on the session. For sample:
var hql = "SELECT e.Id FROM MyEntity e
INNER JOIN e.Address a
WHERE a.ZipCode LIKE :zipCode";
var zipCodeData = "58745";
var result = session.CreateQuery(hql)
.SetParameter("zipCode", $"%{zipCodeData}%")
.List<int>();

Related

SQL to LINQ inner JOIN

Could somebody help me form the below SQL query in Entity framework linq?
The AccountApplication-table has got two FK's:
AccountApplication[AccountId]->Account[AccountId]
and
AccountApplication [ApplicationId] -> Application[ApplicationId]
select * from Invoice a inner join AccountApplication b
on b.AccountId = 3 and b.ApplicationId = a.ApplicationId
I do not have the AccountApplication entity table available in EF.
What I want is a list of invoice who belongs to the application(s) what the given account id has rights to. And the relation between accountid's applicationid's are in the AccountApplication-table.
Screenshot of EF classes with navigation properties
With navigation properties it's quite simple
var query = db.Invoices.Where(invoice => invoice.Application.Account
.Any(account => account.AccountId == 3));
I hope you see now why they are called navigation properties. Inside the query you use them to "navigate" (access) the data needed, and the EF uses them as metadata for generating the necessary joins.
var records= (from a in context.Invoices join b in context.AccountApplication
on a.accountId equals b.accountId
where b.accountId==3
select new {a.Id,b.Id,....etc});

Linq to Sql - Manual association with manual parameter

I have the following tables in my database:
SageAccount
ID (bigint)
LegacyID (nvarchar)
Customer (bit)
Consignments
ID (bigint)
Customer (nvarchar)
What I want to do is have a navigation property/association in my Linq to Sql dbml from Consignment to SageAccount. The difficulty with this is that not only do we need to match SageAccount.LegacyID => Consignments.Customer but we also need to only join to sage accounts where SageAccount.Customer is TRUE. So on the Consignments end, it isn't joining onto a field but instead a static value.
Is this possible in Linq to Sql? Note this database doesn't (and unfortunately can't) have any foreign keys setup in the database.
Yes it is possible. linq have join method. You can use it ike this in your situation:
var res = from sageAccount in _context.SageAccount
join consignments in _context.Consignments
on
new
{
LegacyID = sageAccount.LegacyID,
Customer = sageAccount.Customer
}
equals
new
{
LegacyID = consignments.ID,
Customer = true
}
select new { SageAccountID = sageAccount.ID };
Note that Property name, Type and order in the anonymous objects that you're joining on must match.
You can't use OR and AND in joins - use just equals one object to other.
This will have a this kind of result in your SQL:
SELECT [t0].[ID] AS [SageAccountID]
FROM [dbo].[SageAccount] AS [t0]
INNER JOIN [dbo].[Consignments] AS [t1] ON (([t0].[LegacyID]) = [t1].[ID])
AND ([t0].[Customer] = 1)

NHibernate: How to eager fetch sub entities with a filter over sub entity through one sql query without lazyload?

I'am using NHibernate 3.3.1 with FluentNhibernate 1.3 for Data Layer.
I have the following entities:
Database Diagram:
I need a method that gets the Products by MediaCategory of Medias of Products. I want NHibernate to send only one query to db and fetch all sub properties of products.
I want NHibernate send a query like this:
declare #mediaCategoryId int = 13
select *
from Product p
inner join Media m on m.ProductId=p.Id
inner join MediaCategoryMedia mcm on mcm.MediaId=m.Id
inner join MediaCategory mc on mc.Id=mcm.MediaCategoryId
left join ProductSeller ps on ps.ProductId=p.Id
left join Seller s on ps.SellerId=s.Id
where mc.Id=#mediaCategoryId
I have tried the following options to solve this challenge;
session.QueryOver< ProductEntity >()...
I have tried Inner.JoinQueryOver< .. >().Fetch.Eager... but I couldn't fetch all the sub entities.
session.CreateCriteria< ProductEntity >().SetFetchMode("",FetchMode.Eager)...
In this case lazy load works and I dont want lazyload. If I disable lazyload from mappings NH sends lots of queries.. what I want is eager load with one single query that fetches all sub entities.
session.Query< ProductEntity >().FetchMany(p=>p.MediaList).ThenFetchMany(m=>m.SellerList)...
I couldn't create alias to pass mediaCategoryId filter in this case. Instead I used .Where(x=>x.MediaList.Any(m=>m.CategoryList.Any(...))) and the query generated is not optimum, too.
(from p in session.Query< ProductEntity >()
from m in p.MediaList
from c in m.MediaCategoryList
where c.Id==23
select p).Fetch(x=>x.MediaList);
this didn't work as I wanted, too..
var hql=#"select p from ProductEntity as p join fetch p.MediaList as m join fetch m.MediaCategoryList as mc left join fetch p.SellerList as s where mc.Id=:catId ";
THIS WORKS with "join fetch" in hql.
I need the best practice of this case, however Hql is the king.
Can we handle this case with session.Query<>() or session.CreateCriteria, or QueryOver ?
For a direct translation of your query...
Media mediaAlias = null;
MediaCategory categoryAlias = null;
return session.QueryOver<Product>()
.JoinAlias(x => x.Medias, () => mediaAlias)
.JoinAlias(() => mediaAlias.Categories, () => categoryAlias)
.Fetch(x => x.Sellers).Eager
.Where(() => categoryAlias.Id == mediaCategoryId)
.List();
JoinAlias does an inner join by default, and Fetch(...).Eager does a left outer join. JoinAlias allows us to dig down through Media to the categories, and it also eagerly fetches the data.
Note that there is a Cartesian product between Sellers and Medias in this query. If there are 20 Medias and 20 Sellers on a single Product, then this query would return 20 * 20 = 400 rows, which is not ideal for performance. You can address this by splitting the Media fetching and the Seller fetching into separate queries, but batch them together in one round-trip to the database using Future(), meaning the query would return 20 + 20 = 40 rows. Much better.
Also, this query will not return all of the categories associated with a Media. If you need this, then you should apply the mediaCategoryId constraint in an Exists sub-query.

LINQ Group by and having where clause

Below is the SQL Query I am trying to translate
SELECT dbo.Contracts.Supplier
FROM dbo.Contracts INNER JOIN dbo.Products ON dbo.Contracts.Product = dbo.Products.Product
where dbo.Products.ProductGroup='Crude'
GROUP BY dbo.Contracts.Supplier
Am I doing something wrong because I do not get same results with the following LINQ
var result = from c in context.Contracts
join p in context.Products on c.Product equals p.Product1
where p.Product1.Equals("Crude")
group c by c.Supplier into g
select new { supplier = g.Key };
It is generating a weird statement
SELECT
1 AS [C1],
[Distinct1].[Supplier] AS [Supplier]
FROM ( SELECT DISTINCT
[Extent1].[Supplier] AS [Supplier]
FROM [dbo].[Contracts] AS [Extent1]
WHERE N'Crude' = [Extent1].[Product]
) AS [Distinct1]
Using distinct would work but to get same results, LINQ should be generating a statement like so (it's like it is ignoring the join):
SELECT distinct dbo.Contracts.Supplier
FROM dbo.Contracts INNER JOIN dbo.Products ON dbo.Contracts.Product = dbo.Products.Product
where dbo.Products.ProductGroup='Crude'
I'm assuming that you are using 'EntityFramework' or 'Linq To SQL'. If so, you should be able to use navigation properties to navigate to product and filter invalit results out. This way your query might look something like this:
var result = (from c in context.Contracts
where c.Products.Any(p => p.ProductGroup == "Crude")
select c.Supplier).Distinct();
It will automatically convert into correct query (in this case possibly without join even, just using Exists sql keyword) and return distinct suppliers. This is if I understand your objective correctly - you want to obtain all suppliers assigned to contracts that contain product from 'Crude' product group.
Basically you should try to avoid using joins from linq to sql or linq to entities as much as possible when you can use navigation properties. System will probably be better at converting them into specific sql.

NHibernate Criteria engine with inner join and subquery

Is it posible in NHibernate to create a query that looks like this?
select hi.ContactId
From dbo.vw_HostInterests hi INNER JOIN
( Select cm1.ContactId
From dbo.vw_ContactMoments cm1 INNER JOIN
(
Select Contactid
From dbo.vw_ProfileNaw
where GenderId = 1000
) as pn1 on cm1.ContactId = pn1.ContactId
where cm1.ActivityId = 1001
)as cm on hi.ContactId = cm.ContactId
where hi.ActivityId = 1038
I've managed to create the correct output with the IN statement, but I'd realy like the SQL to look like this.
The Criteria below shows part of above query with the IN Statement I used (but want to replace):
ICriteria criteria = DbSession.CreateCriteria<Contact>();
var dCriteria1 = DetachedCriteria.For(typeof(VwHostInterest))
.Add(Expression.Eq("ActivityId", 1038))
.SetProjection(Projections.ProjectionList()
.Add(Projections.GroupProperty("ContactId")));
var dCriteria2 = DetachedCriteria.For(typeof(VwContactMoment))
.Add(Expression.Eq("ActivityId", 1001))
.SetProjection(Projections.ProjectionList()
.Add(Projections.GroupProperty("ContactId")));
criteria.Add(Subqueries.PropertyIn("ContactId", dCriteria1));
criteria.Add(Subqueries.PropertyIn("ContactId", dCriteria2));
int count = (Int32)criteria
.SetProjection(Projections.Count("ContactId"))
.UniqueResult();
Probably not the answer you are looking for and apologies if it isn't but my experience is that your best bet with such complex queries would be to:
a) Do this whole thing as a view and map it in NHibernate
b) Create the inner select as a view and create a mapping such that you can relate it in your query
b) Or override nhibernate (override as in skip and not in OO terms ;) and write this as a named query using native SQL.
Does that nested query produce the same result as the following?
SELECT hi.ContactId
FROM dbo.vw_HostInterests hi
INNER JOIN vw_ContactMoments cm1 on hi.ContactId = cm1.ContactId
AND cm1.ActivityId = 1001
INNER JOIN dbo.vw_ProfileNaw pn1 on pn1.ContactId = cm1.ContactId
AND pn1.GenderId = 1000
WHERE hi.ActivityId = 1038

Categories