I've encountered a little problem selecting only the needed model in QueryOver.
Here my scenario
var qOver = _HibSession.QueryOver<ModelA>(() => Ma)
.JoinAlias(() => Ma.MbList, () => Mb, JoinType.LeftOuterJoin)
.Where(() => Mb.ID == _MbId)
.JoinAlias(() => Mb.McList, () => Mc,JoinType.LeftOuterJoin)
.Where(() => Mc.ID == _McId)
.JoinAlias(() => Mc.MdList, () => Md, JoinType.LeftOuterJoin)
.Where(() => Md.ID == _MdID)
.OrderByAlias(() => Ma.ID).Asc
.Take(iTake)
.Skip(iSkip)
.Future<ModelA>();
The previous code generate the follow SQL
SELECT TOP n Ma.*,Mb.*,Mc.*,Md.*
FROM Ma
LEFT JOIN Mb ON (...conditions...)
LEFT JOIN Mc ON (...conditions...)
LEFT JOIN Md ON (...conditions...)
WHERE Mb.ID = _MbId
AND Mc.ID = _McId
AND Md.ID = _MdId
ORDER BY Ma.ID ASC
The problem is that Mc and Md table duplicates my result. So I would have only Ma and Mb in my SELECT statement.
SELECT TOP n Ma.*,Mb.*
FROM
.
.
.
How can I reach that result?
Thank you!
Try using Fetch instead of JoinAlias. Something like this should do the job:
... .QueryOver<Ma>().Fetch(ma => ma.MbList).Eager
and do not use fetch="join" in your mapping.
When you force NHibernate to use join, it will return as many rows as there are in the cartesian product of your tables. As you only need one list item per ModelA object, you have to let NHibernate use simple select statements instead of join.
You need to inform NHibernate to not repeat the root entity (ModelA). You can do this with the statement:
.TransformUsing(Transformers.DistinctRootEntity)
It seems the only way is to use a pricipal QueryOver selecting the main model and a filtered SubQuery
I mean something like this
var qOverInclude = QueryOver.Of<MyModel>(() => mModel)
.JoinAlias(() => mModel.MyList, () => mList, JoinType.LeftOuterJoin)
.Where(() => mList.ID == myID)
.And(() => mList.Type == myType)
.Select(x => x.IdMyModel);
var qOver = _HibSession.QueryOver<MyModel>(() => mModel)
.JoinAlias(() => mModel.MyDescription, () => mDescription, JoinType.LeftOuterJoin)
.Where(() => mDescription.IDLanguage == myLanguage)
.WithSubquery.WhereProperty(() => mModel.IdMyModel).In(qOverSubQuery)
.OrderByAlias(() => mModel.IdMyModel).Asc
.Future<MyModel>();
Related
I am trying to create a nHibernate-Query with a subquery following this blog-entry.
My working SQL looks like this:
SELECT *
FROM Product
WHERE Id IN (
SELECT p.Id
FROM Product AS p
INNER JOIN ProductSupplier AS ps
ON ps.ProductId LIKE p.Id
WHERE ps.SupplierProductNumber LIKE '102.02-7100'
GROUP BY p.Id
);
I have to group by the Id because multiple suppliers can have the same productNumber for the same product.
My nHibernate looks as following:
query.WithSubquery.WhereAll(
p => p.Id ==
QueryOver.Of<Product>()
.JoinAlias(x => x.Suppliers, () => productSupplierAlias)
.Where(() => productSupplierAlias.Product.Id == productAlias.Id)
.Where(() => productSupplierAlias.SupplierProductNumber == searchtext)
.Select(p => p.Id));
But my .Select(p => p.Id) displays
cannot convert lambda expression to type 'nHibernate.Creterian.IProjection[]' because it is not a delegate type
I don't think you should be using WhereAll in this case.
Does this work:
query.WithSubquery.WhereProperty(p => p.Id)
.In(QueryOver.Of<Product>()
.JoinAlias(x => x.Suppliers, () => productSupplierAlias)
.Where(() => productSupplierAlias.Product.Id == productAlias.Id)
.Where(() => productSupplierAlias.SupplierProductNumber == searchtext)
.Select(p => p.Id)
);
I have the following nHibernate query that select a course based on its course id and then return selected fields for the course object on the initial fetch, and the query executes with no issues.
MatchMode option = ...
CourseItem courseAlias = null;
TeacherItem teacherAlias = null;
var query = session.QueryOver<CourseItem>()
.JoinAlias(c => c.Teacher, () => teacherAlias)
.Where(c => c.CourseID.IsInsensitiveLike(strNumber, option))
.SelectList(list => list
.Select(c => c.CourseID).WithAlias(() => courseAlias.CourseID)
.Select(c => c.IsActive).WithAlias(() => courseAlias.IsActive)
.Select(c => c.CourseDesc).WithAlias(() => courseAlias.CourseDesc)
.Select(c => c.Teacher).WithAlias(() => courseAlias.Teacher))
.TransformUsing(Transformers.AliasToBean<CourseItem>())
.List<CourseItem>();
I wanted to go a step further with the query to only return a partial teacher object, let's say i just wanted to return the ID and Name. So, I updated the projected list to as follows:
var query = session.QueryOver<CourseItem>()
.JoinAlias(c => c.Teacher, () => teacherAlias)
.Where(c => c.CourseID.IsInsensitiveLike(strNumber, option))
.SelectList(list => list
.Select(c => c.CourseID).WithAlias(() => courseAlias.CourseID)
.Select(c => c.IsActive).WithAlias(() => courseAlias.IsActive)
.Select(c => c.CourseDesc).WithAlias(() => courseAlias.CourseDesc)
.Select(c => c.Teacher.ID).WithAlias(() => courseAlias.Teacher.ID)
.Select(c => c.Teacher.Name).WithAlias(() => courseAlias.Teacher.Name))
.TransformUsing(Transformers.AliasToBean<CourseItem>())
.List<CourseItem>();
The query doesn't work because nHibernate has no idea how to resovled based on Teacher.ID and Teacher.Name. Any thoughts on whether it's possible to NOT fetch the entire child object back to a parent object?
I've tried the following query and it works this is not my fully desired outcome
var query = session.QueryOver<CourseItem>(() => courseAlias)
.JoinAlias(() => courseAlias.Teacher, () => teacherAlias)
.Where(() => courseAlias.CourseID.IsInsensitiveLike(strNumber, option))
.SelectList(list => list
.Select(() => courseAlias.CourseID)
.Select(() => courseAlias.IsActive)
.Select(() => courseAlias.CourseDesc)
.Select(() => teacher.ID)
.Select(() => teacher.Name))
.List<object[]>();
I can query the right values but unable to transform it back correctly to the Course / teacher data type.
Any thoughts?
thanks!
We can indeed use custom transformer. There is one, which I am using for a really very very deep projections (inlcuding dynamic objects - 5.1.13. component, dynamic-component)
DeepTransformer<TEntity>
Take it (if needed adjust it) and your final query could be like this
// just the last lines are different
var query = session.QueryOver<CourseItem>()
.JoinAlias(c => c.Teacher, () => teacherAlias)
.Where(c => c.CourseID.IsInsensitiveLike(strNumber, option))
.SelectList(list => list
.Select(c => c.CourseID).WithAlias(() => courseAlias.CourseID)
.Select(c => c.IsActive).WithAlias(() => courseAlias.IsActive)
.Select(c => c.CourseDesc).WithAlias(() => courseAlias.CourseDesc)
// the native WitAlias would not work, it uses expression
// to extract just the last property
//.Select(c => c.Teacher.ID).WithAlias(() => courseAlias.Teacher.ID)
//.Select(c => c.Teacher.Name).WithAlias(() => courseAlias.Teacher.Name))
// so we can use this way to pass the deep alias
.Select(Projections.Property(() => teacherAlias.ID).As("Teacher.ID"))
.Select(Projections.Property(() => teacherAlias.Name).As("Teacher.Name"))
// instead of this
// .TransformUsing(Transformers.AliasToBean<CourseItem>())
// use this
.TransformUsing(new DeepTransformer<CourseItem>())
And in case, that your aliases do match to property names, that transformer will built the object tree...
I'd like to resolve that problem :
SELECT Max(Date)
FROM Table
GROUP BY SubId
(Then pass it as a SubQuery to mid-action so I can get the Id of the item in Table)
SELECT Id
FROM Table
WHERE Date in
[[[ previous request ]]]
(Then Get the full Table Item with other table join)
SELECT *
FROM Table
LEFT JOIN...
WHERE Id in
[[[ previous request ]]]
I tried this kind of request :
var subquery = QueryOver.Of<Table>(() => x)
.SelectList(list => list
.SelectMax(() => x.Date)
.SelectGroup(() => x.Sub.Id)
);
var filter = QueryOver.Of<Table>().WithSubquery.
WhereExists(subquery)
.Select(p => p.Id);
var result = Session.QueryOver<Table>().WithSubquery.WhereProperty(p => p.Id).In(filter).Left.JoinQueryOver(p => p.Sub).List();
But the problem is that I can't get the first request right with only the date out of my request.
Is there a better way to do it than that kind of subqueries ? And is there a possibility in NHibernate to Groupy By a Property without selecting it ?
Thanks !
Finally did it that way and it generated the SQL i wanted. But it wasn't 3 subqueries exactly it was 3 queries looking in a set of datas (The arrays subquery and CorrespondingIds).
var subquery = Session.QueryOver<Table>(() => x)
.SelectList(list => list
.SelectMax(() => x.Date)
.SelectGroup(() => x.Sub.Id))
.List<object[]>().Select(p => p[0]).ToArray();
var CorrespondingIds = Session.QueryOver<Table>(() => x)
.WhereRestrictionOn(() => x.Date).IsIn(subquery)
.Select(p => p.Id).List<int>().ToArray();
var result = Session.QueryOver<Table>(() => x).WhereRestrictionOn(() => x.Id).IsIn(CorrespondingIds).Left.JoinQueryOver(p => p.Sub).List();
I have an object model where an Order contains many LineItems, and each LineItem has an associated Product. In the object model, these are one-way associations -- a LineItem does not know anything about its Order.
I want to query for orders that contain a line item with a product name matching a string, returning one row for each order (so that paging can be performed).
SELECT * FROM Orders
WHERE OrderID IN (
SELECT DISTINCT OrderID
FROM LineItems
INNER JOIN Products on LineItems.ProductID = Products.ProductID
WHERE Products.Name = 'foo'
)
Given that I have an ICriteria or an IQueryOver representing the subquery, how do I actually apply it to my root Order query?
var subquery = QueryOver.Of<LineItem>
.Where(l => l.Product.Name == "foo")
.TransformUsing(Transformers.DistinctRootEntity);
I've found plenty of examples that assume the root object in the query is on the "many" side of a one-to-many relationship, but I can't figure out how to add a restriction on something that the root object has many of.
I'd make it a bi-directional relationship between order and line item to allow efficient queries (reduce the number of joins required). But, if for some weird reason you can't, you'll need to start the sub-query from the Order...
LineItem lineItemAlias = null;
Product productAlias = null;
var subQuery = QueryOver.Of<Order>()
.JoinAlias(x => x.LineItems, () => lineItemAlias)
.JoinAlias(() => lineItemAlias.Product, () => productAlias)
.Where(() => productAlias.Name == "foo")
.Select(Projections.Group<Order>(x => x.Id));
var results = Session.QueryOver<Order>()
.WithSubquery.WhereProperty(x => x.Id).In(subQuery)
.List();
The direct translation of the SQL that you provided can be acheived using this
var subQuery =
QueryOver.Of<LineItem>(() => lineItem)
.JoinAlias(() => lineItem.Products, () => product)
.Where(() => product.Name == "foo")
.Select(Projections.Distinct(
Projections.Property(()=> lineItem.Order.Id)));;
var theQueryYouNeed =
QueryOver.Of<Orders>(() => order)
.WithSubquery.WherePropertyIn(() => order.Id).In(subQuery);
However if your LineItem entity does not have a Order Property then you cannot really use the subquery.
If you need to find
All Orders which have a LineItem where the Product Name is "foo" then
var theQueryYouNeed =
QueryOver.Of<Orders>(() => order)
.JoinAlias(() => order.LineItems, () => lineItem)
.JoinAlias(() => lineItem.Product, () => product)
.Where(() => product.Name == "foo")
.TransformUsing(new DistinctRootEntityResultTransformer())
Hi gyus
I'm writing here so....I've a problem :-)
Here my situation:
Table A (Category) {ID, List, List }
Table B (CategoryLanguage) {IDCategory, IDLanguage, Description}
Table C (SubCategory) {IDCategory, ID, List }
Table D (SubCategoryLanguage) {IDSubCategory, IDLanguage, Description}
Here my QueryOver (with Aliases) code
Category Cat = null;
CategoryLanguage catLang = null;
SubCategory subCat = null;
SubCategoryLanguage subCatLang = null;
var qOver = _HibSession.QueryOver<Category>(() => Cat)
.Left.JoinAlias(() => Cat.Languages, () => catLang)
.Where(() => catLang.IDLanguage == IDLanguage)
.Left.JoinAlias(() => Cat.SubCategories, () => subCat)
.Left.JoinAlias(() => subCat.Languages, () => subCatLang)
.Where(() => subCatLang.IDLanguage == IDLanguage)
.OrderBy(() => Cat.ID).Asc
.List<Category>();
With this Query, NHibernate doesn't filter the Languages fields and when I loop into the subCategories (to print out on my view the tree Category=>SubCategory ) it launches a Query for every subCategory!! (I saw it using NHibernate Profiler)
I don't use Lazy Loading but using JoinAlias I expected to see all data joined.
Anyone can help me?
Thank you!
Not sure if it helps but in some of the queries I've created using QueryOver I've specified the fetch mode e.g.
Category Cat = null;
CategoryLanguage catLang = null;
SubCategory subCat = null;
SubCategoryLanguage subCatLang = null;
var qOver = _HibSession.QueryOver<Category>(() => Cat)
.Left.JoinAlias(() => Cat.Languages, () => catLang)
.Fetch(x => x.Languages).Eager
.Where(() => catLang.IDLanguage == IDLanguage)
.Left.JoinAlias(() => Cat.SubCategories, () => subCat)
.Left.JoinAlias(() => subCat.Languages, () => subCatLang)
.Fetch(x => x.SubCategories).Eager
.Where(() => subCatLang.IDLanguage == IDLanguage)
.OrderBy(() => Cat.ID).Asc
.List<Category>();