NHibernate set fetch strategy - nhibernate

I'm using NHibernate with fluent api and got MainEntity mapping like this:
mapping.References(c => c.Parent1).Not.Nullable().Fetch.Select();
mapping.HasMany(c => c.Children1).Fetch.Select();
mapping.HasMany(c => c.Children2).Fetch.Select();
Then I'm trying to query MainEntity like this:
var query = Session.Query<MainEntity>().Where(e => e.ID == id);
query.Fetch(e => e.Parent1).ToFuture();
query.FetchMany(e => e.Children1).ToFuture();
query.FetchMany(e => e.Children2).ThenFetch(c => c.Children21).ToFuture();
var entity = query.ToFuture().FirstOrDefault();
And got sql query like this:
select * from MainEntity
left outer join Parent1 on MainEntity.Parent1ID = Parent1.ID
where MainEntity.ID = #id;
select * from MainEntity
left outer join Children1 on MainEntity.ID = Children1.MainEntityID
where MainEntity.ID = #id;
select * from MainEntity
left outer join Children2 on MainEntity.ID = Children2.MainEntityID
left outer join Children21 on Children2.Children21ID = Children21.ID
where MainEntity.ID = #id;
select * from MainEntity
where MainEntity.ID = #id;
But I want NHibernate to select MainEntity at first and then select Parent1 and Children1, Children2 by condition:
select *
from Parent1
where ID = #parent1ID;
select *
from Children1
where MainEntityID = #id;
#parent1ID and #id are selected by first query of MainEntities.
Please, help me to configure mapping to do this.

Have you tried other LINQ version ? It should look like this:
Query<Parent>()
.FetchMany(x=>x.Children1)
.FetchMany(x=>x.Children)
.FirstOrDefault(x=>x.Id=param1);
and change children mapping to
mapping.HasMany(c => c.Children1);
mapping.HasMany(c => c.Children2);

Related

Product with last 4 vendors on transaction date

Hi I have this sql and have to translate into NHibernate QueryOver
SELECT S.Number, S.Description, S.BrandDescription, subquery.vendornumber, subquery.vendorname
FROM Stocks.Stock S left join
(select vendorNumber, VendorName, POLID, LastTransactionDate from
(
SELECT top 4 v.Number vendorNumber, v.Name VendorName, PLL.Id POLID, max(por.TransactionDate) as LastTransactionDate,
ROW_NUMBER() OVER(PARTITION BY v.Number ORDER BY max(por.TransactionDate) DESC) AS rk
FROM Purchasing.PurchaseOrderLineItem PLL
inner join Purchasing.PurchaseOrder po on PLL.PurchaseOrderId = po.Id
inner join Purchasing.PurchaseOrderVendor POV on po.Id = POV.PurchaseOrderId
inner join Purchasing.Vendor V on pov.VendorId = v.Id
left outer join Purchasing.PurchaseOrderReceipt POR on PLL.Id = por.PurchaseOrderLineItemId
group by v.Number, v.Name,PLL.Id
order by LastTransactionDate desc
) subquery
where subquery.rk = 1) B on PL.Id = b.POLID
Or just to explain it simply see its simplified version
Select * from master m
outer apply (select top 4 * From Details d where m.Id = d.Id order by someColumns desc)o
I think we cannot use subquery as derived table in nhibernate. If you have suggestions, please share.
Thanks
I was keep working on this and found that it could be very difficult if want to do totally with QueryOver. I want to show how I did this.
First I took all the vendors with StockID to join with StockQuery later.
var stockVendors =
Session.QueryOver<Vendor>(() => V)
.Left.JoinQueryOver(p => V.Stock, () => sstk)
.Where(sstk.Number !=null)
.OrderBy(Projections.Max(() => V.TransactionDate)).Desc()
.ThenBy(() => sstk.Number).Asc()
.ThenBy(() => sv.Number).Asc()
.SelectList(
lst =>
lst.SelectGroup(() => V.Name).WithAlias(() => svhModal.VendorName)
.SelectGroup(() => V.Number).WithAlias(() => svhModal.VendorNumber)
.SelectGroup(() => sstk.Number).WithAlias(() => svhModal.StockNumber)
.Select(Projections.Max(() => V.TransactionDate)).WithAlias(() => svhModal.LastTransactionDate)
)
.TransformUsing(Transformers.AliasToBean()).List();
Then select Stock only
var stockDetail = Session.QueryOver<Stock>(() => stk)
.Where(soneCriteria)
.SelectList(list => list
.Select(() => stk.Id).WithAlias(() => sdrModal.Id)
.Select(() => stk.Number).WithAlias(() => sdrModal.Number)
.TransformUsing(Transformers.AliasToBean<StockDetailReportModal>())
.List<StockDetailReportModal>();
IList<StockVendor> vlst2 = null;
IList<StockDetail> newStock = new List<StockDetail>();
Here starts two loops to fill List object with each stock and its 5 top vendors from vendors list. Loop from Stockdetail result from query and inside loop from vendor result filtered to outer loop stockid, get first 5 vendors only in loop, when done just return the result to report. Its working fine.
foreach (StockDetail ostk in stockDetail)
{
stkid = ostk.Number;
vlst2 = (from v in stockVendors where v.StockNumber == stkid orderby v.LastTransactionDate descending select v).ToList<StockVendor>();
vndrcnt = 0;
stok = new StockDetail
{
Id = ostk.Id,
Number = ostk.Number,
//// other fields too here
};
if (vlst2.Count() == 0)
{
newStock.Add(stok);
}
foreach (StockVendor vn in vlst2)
{
if (vndrcnt == 0)
{
stok.VendorName = vn.VendorName;
stok.VendorNumber = vn.VendorNumber;
// other fields here...
newStock.Add(stok);
}
else
{
newStock.Add(new StockDetail
{
Id = ostk.Id,
Number = ostk.Number,
VendorName = vn.VendorName,
VendorNumber = vn.VendorNumber,
// adding vendor information in stock record.
});
}
vndrcnt++;
if (vndrcnt >= 4)
break;
}
This solved my problem and achieved this after investigating many days. You may find better approach, so please share.

QueryOver Many to Many with a single SQL join

I've got 2 entities, linked many-to-many. (Product & User)
I want to restrict Products by Users:
User userAlias = null;
query.JoinAlias(product => product.Users, () => userAlias)
.Where(() => userAlias.Id == currentUser.Id);
It's generated SQL code:
SELECT this_.Id as y0_
FROM [Product] this_
inner join UserToProduct users5_
on this_.Id = users5_.Product_id
inner join [User] useralias3_
on users5_.User_id = useralias3_.Id
....
In "Where" i use only user_id and i don't need second join.
How I can write the query(by QueryOver) with a single SQL join ?
This may help? I have a similar setup with UsersRoles
Role roleAlias = null;
var roles = _session.QueryOver<UsersRole>().JoinAlias(x => x.Role, () => roleAlias, JoinType.LeftOuterJoin).Where(
x => x.User.UserId == userId).List();
produces the following:
SELECT this_.UsersRolesId as UsersRol1_32_1_,
this_.UserId as UserId32_1_,
this_.RoleId as RoleId32_1_,
rolealias1_.RoleId as RoleId27_0_,
rolealias1_.RoleName as RoleName27_0_,
rolealias1_.Active as Active27_0_,
rolealias1_.DateCreated as DateCrea4_27_0_,
rolealias1_.LastUpdated as LastUpda5_27_0_,
rolealias1_.RoleSettingsId as RoleSett6_27_0_
FROM UsersRoles this_
left outer join Roles rolealias1_
on this_.RoleId = rolealias1_.RoleId
WHERE this_.UserId = 'a7eec4eb-21cc-4185-8847-a035010e779f' /* #p0 */

Filtered join with NHibernate QueryOver

Using the Criteria API, I can generate a query that creates a JOIN with an extra condition on the JOIN
var criteria = Session.CreateCriteria<Product>()
.SetReadOnly(true)
.SetMaxResults(1)
.CreateAlias("ProductCategory", "U", JoinType.LeftOuterJoin, Expression.Eq("U.SubType", "Premium"))
.AddOrder(Order.Desc("U.Sequence"));
This generates a JOIN similar to this:
SELECT * FROM dbo.Product w
LEFT JOIN dbo.ProductCategory u
ON u.DefaultProductId = w.Id AND u.SubType = 'Premium'
How do I do the same thing with the QueryOver syntax?
Off the top of my head I think it's like:
ProductCategory category = null;
var result = Session.QueryOver<Product>()
.JoinAlias(x => x.Categories, () => category, JoinType.LeftOuterJoin)
.Where(() => category.SubType == "Premium")
.OrderBy(() => category.Sequence).Desc
.Take(1)
.List();
Edit: Included OrderBy, and gave it a test. Works.
Using the Blog > Posts type example, the generated SQL looks like:
SELECT this_.Id as Id1_1_,
this_.Title as Title1_1_,
post1_.BlogId as BlogId3_,
post1_.Id as Id3_,
post1_.Id as Id3_0_,
post1_.Title as Title3_0_,
post1_.Content as Content3_0_,
post1_.DatePosted as DatePosted3_0_,
post1_.BlogId as BlogId3_0_
FROM [Blog] this_
left outer join [Post] post1_
on this_.Id = post1_.BlogId
WHERE post1_.DatePosted > '2011-11-22T19:43:11.00' /* #p0 */
ORDER BY post1_.DatePosted desc
.JoinAlias has an overload with a withClause
var result = Session.QueryOver<Product>()
.Left.JoinAlias(x => x.Categories, () => category, c => c.SubType == "Premium")
.OrderBy(() => category.Sequence).Desc
.Take(1)
.List();

NHibernate query over comparing two sub queries

How do you I combine two sub queries with queryover with WithSubQuery ? I want something like below (exact syntax doesn't matter):
query.WithSubquery.WhereValue(QueryOver.Of<Child>()
.Where(m => m.Parent.Id == paretAlias.Id)
.Select(Projections.Max("SomeProp")))
.Lt(QueryOver.Of<Child>(() => childAlias)
.Where(m => childAlias.Id == parentAlias.Id)
.Select(Projections.Max("SomeOtherProp")));
I can't see any methods of WithSubquery that allows me to compare two methods. It has
Where : takes a lambda
WhereProperty : takes a property compares with a sub query
WhereValue : takes a value compares with a sub query
WhereExists : takes a query.
Basically I want a method with that takes sub query and compares with another sub query
A sample output query in the sql would be :
select * from Parent inner join child on parent.id = child.parentid where
(select max(SomeProp) from child where child.parentid = parent.id) > (select max(SomeOtherProp) from child where child.parentid = parent.id)
I think you should be able to resolve your problem by slightly modifying the sql-query:
SELECT p.* FROM [Parent] as p
WHERE EXISTS
(
SELECT c.[parentId] as parentid FROM [Child] as c
WHERE c.[parentid] = p.[id]
GROUP BY c.[parentid]
HAVING MAX(c.[someProp]) < MAX(c.[someOtherProp])
)
If this returns the correct result set then you can implement it with QueryOver like so:
Parent p = null;
Child c = null;
var subquery = QueryOver.Of(() => c)
.SelectList(list => list.SelectGroup(() => c.ParentId))
.Where(Restrictions.LtProperty(
Projections.Max(() => c.SomeProp),
Projections.Max(() => c.SomeOtherProp)))
.And(Restrictions.EqProperty(
Projections.Property(() => c.ParentId),
Projections.Property(() => p.Id)));
var query = QueryOver.Of(() => p)
.WithSubquery.WhereExists(subquery);
IList<Parent> resutl = Session.CreateQuery(query);
I've already replied on a similar question, there is also a Criteria API version of the above query:
Selecting on Sub Queries in NHibernate with Critieria API
I hope this helps, cheers!

How to rewrite SQL query using Linq to Entity

I need to rewrite the query below using Linq to Entity. Does someone know how to do it the most sufficient way?
SELECT DISTINCT
C.ClientId,
C.CompanyName
FROM Application A WITH (NOLOCK)
INNER JOIN
(
SELECT ApplicationId
FROM CAContracts WITH (NOLOCK)
WHERE ID = 1212 AND CAContractStatusId IN (2,3)
UNION ALL
SELECT OBA.ApplicationId
FROM OpportunityAssignment OA WITH (NOLOCK)
INNER JOIN OpportunityByApp OBA WITH (NOLOCK) ON
OBA.OpportunityId = OA.OpportunityId
WHERE OA.ID = 1212
AND OA.OpporStatusId IN (5,7)
) ACPA ON
ACPA.ApplicationId = A.Applicationid
INNER JOIN Client C WITH (NOLOCK) ON
C.ClientId = A.ClientId
ORDER BY C.CompanyName
Assuming that Context has all the relevant tables defined already as entities and has proper relationships defined:
Context.Clients
.Include("Applications")
.Include("Applications.CAContracts")
.Include("Applications.OpportunityAssignments")
.Include("Applications.OpportunityAssignments.OpportunityByApps")
.Where<Client>(c => (c.Applications
.Any<Application>(a => a.CAContracts
.Any<CAContract>(cac => cac.ID == 1212 && (cac.CAContractStatusId == 2 || cac.CAContractStatusId == 3)))
|| (c.Applications
.Any<Application>(a => a.OpportunityAssignments
.Any<OpportunityAssignment>(oa => oa.ID == 1212 && (oa.OpporStatusId == 5 || oa.OpporStatusId == 7) && oa.OpportunityByApps.Any<OpportunityByApp>()))))
.Select(c => new { ClientId = c.ClientId, CompanyName = c.CompanyName})
.Distinct()
.OrderBy(c => c.CompanyName);
If I knew more about the schema, I might be able to do a little better. You end up with a collection of anonymous types with ClientId and CompanyName properties; I tend to avoid anonymous types personally unless the corresponding object would otherwise be excessively large.