NHibernate Hierarchical Master Details QueryOver - c#

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>();

Related

nHibernate Subquery-WhereAll displays "not a delegate type"-error

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)
);

How to partially project a child object with many fields in nHibernate

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...

NHibernate, Separate queries (using QueryOver) to populate complex object

We're using a database that doesn't support query batching so we can't make use of NHibernate Futures in this instance. We know we can still populate our complex object graph using multiple-queries (to avoid Cartesian products) but need advice if I can refactor the approach.
Please note > Sole developer here, so seeking advice.
Here is some sample code which illustrates the current approach;
var fruitBasketAlias = new Store.FruitBasket();
var yoghurtAlias = new Store.Yoghurt();
var flavourAlias = new Store.Flavour();
var ownersAlias = new Store.Owner();
var FruitBaskets = session.QueryOver(() => fruitBasketAlias)
.Where(() => fruitBasketAlias.Owner.ID == OwnerID
&& fruitBasketAlias.ExpiryDate <= dateTO
&& fruitBasketAlias.ExpiryDate >= dateFROM)
.Fetch(x => x.BasketLiner).Eager
.List();
session.QueryOver(() => fruitBasketAlias)
.Select(x => x.Yoghurts)
.Where(() => fruitBasketAlias.Owner.ID == OwnerID
&& fruitBasketAlias.ExpiryDate <= dateTO
&& fruitBasketAlias.ExpiryDate >= dateFROM)
.JoinAlias(() => fruitBasketAlias.Yoghurts, () => yoghurtAlias, JoinType.LeftOuterJoin)
.JoinAlias(() => yoghurtAlias.Flavour, () => flavourAlias, JoinType.InnerJoin)
.List();
session.QueryOver(() => fruitBasketAlias)
.Where(() => fruitBasketAlias.Owner.ID == OwnerID
&& fruitBasketAlias.ExpiryDate <= dateTO
&& fruitBasketAlias.ExpiryDate >= dateFROM)
.JoinAlias(() => fruitBasketAlias.Yoghurt, () => yoghurtAlias, JoinType.LeftOuterJoin)
.JoinAlias(() => yoghurtAlias.Flavour, () => flavourAlias, JoinType.InnerJoin)
.JoinAlias(() => flavourAlias.Owners, () => ownersAlias, JoinType.LeftOuterJoin)
.List();
You can see from the code above that I am using three separate queries to populate a list of FruitBaskets. This approach is working but I suspect there is a better way to join all the children into the parent object without having to query from the root object each time.
Is there an approach I can use which will enable me to apply the where condition to the parent object and use the results of that query to automatically obtain all the children objects. Please note that children can go 3 levels deep, i.e. FruitBasket.Yoghurt.Flavour.Owners.
Any advice is appreciated.
C# .NET 4, NHibernate 3.0
var FruitBaskets = session.QueryOver<FuitBasket>()
.Where(b => b.Owner.ID == OwnerID
&& b.ExpiryDate <= dateTO
&& b.ExpiryDate >= dateFROM)
.Fetch(x => x.BasketLiner).Eager
.JoinAlias(b => b.Yoghurts, () => yoghurtAlias, JoinType.LeftOuterJoin)
.List();
var yoghurts = session.QueryOver<Store.Yoghurt>()
.WhereRestrictionOn(y => y.Id).In(FruitBaskets.SelectMany(b => b.Yoghurts).Select(y = > y.Id).Distinct())
.JoinAlias(y => y.Flavour, () => flavourAlias, JoinType.InnerJoin)
.List();
session.QueryOver<Store.Flavor>()
.WhereRestrictionOn(f => f.Id).In(yoghurts.SelectMany(y => y.Flavors).Select(b = > f.Id).Distinct())
.JoinAlias(f => f.Owners, () => ownersAlias, JoinType.LeftOuterJoin)
.List();
couldn't test it though

NHibernate using QueryOver with WHERE IN

I would create a QueryOver like this
SELECT *
FROM Table
WHERE Field IN (1,2,3,4,5)
I've tried with Contains method but I've encountered the Exception
"System.Exception: Unrecognised method call: System.String:Boolean Contains(System.String)"
Here my code
var qOver = _HibSession.QueryOver<MyModel>(() => baseModel)
.JoinAlias(() => baseModel.Submodels, () => subModels)
.Where(() => subModels.ID.Contains(IDsSubModels))
.List<MyModel>();
I've found the solution!! :-)
var qOver = _HibSession.QueryOver<MyModel>(() => baseModel)
.JoinAlias(() => baseModel.Submodels, () => subModels)
.WhereRestrictionOn(() => subModels.ID).IsIn(IDsSubModels)
.List<MyModel>();
You can try something like this:
// if IDsSubModels - array of IDs
var qOver = _HibSession.QueryOver<MyModel>()
.Where(x => x.ID.IsIn(IDsSubModels))
You don't need a join in this situation
This works and is more elegant
var Strings = new List<string> { "string1", "string2" };
var value = _currentSession
.QueryOver<T>()
.Where(x => x.TProperty == value)
.And(Restrictions.On<T>(y=>y.TProperty).IsIn(Strings))
.OrderBy(x => x.TProperty).Desc.SingleOrDefault();
where T is a Class and TProperty is a property of T

NHibernate QueryOver Select only needed model

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>();

Categories

Resources