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

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

Related

Select from many tables - Entity Framework

I have that table construction and code sample:
var Tasks = db.Users
.Where(t => t.Id == 1)
.Where(t => t.Tables.Where(a => a.Id == 1)))
.Select(a => a.Tasks.Select(a => a.Tasks.Text));
This code don't work, how can I get Tasks.Text when I using many where questions?
Thanks

Simplify multiple nhibernate queryover

I'm doing a query over two different tables.
In the first query, i get some Ids that I then have to check in another table.
Then I do the first query again with the result of the second query.
This can't be the best way to do this.
But I haven't found a good way to solve it. So some help would be appreciated.
IntOrderInvoiceCostOut y = null;
var list = session.QueryOver<IntOrderInvoiceCostOut>(() => y)
.Where(x => x.IntegrationHandleDate == null)
.Select(Projections.Distinct(Projections.Property(() => y.Externalid)))
.List<string>();
var nonPreliminaryOrders = session.QueryOver<RefImplOrderEntity>()
.WhereRestrictionOn(x => x.ExternalId).IsIn(list.ToList())
.Where(x => x.StatusTypeId != 95)
.Select(x => x.ExternalId)
.List<string>();
var finalList = session.QueryOver<IntOrderInvoiceCostOut>()
.WhereRestrictionOn(x => x.Externalid).IsIn(nonPreliminaryOrders.ToList())
.Where(x => x.IntegrationHandleDate == null)
.OrderBy(x => x.IntegrationCreateDate)
.Asc
.List();
The code works...but i't really ugly.
you could use detacheCriteria for this. I have omitted couple of conditions and you might have to twick a bit as per your requirement.
for example
IntOrderInvoiceCostOut y = null;
var list = QueryOver.Of<IntOrderInvoiceCostOut>(() => y)
.Where(x => x.IntegrationHandleDate == null)
.Select(Projections.Distinct(Projections.Property(() => y.Externalid)))
.DetachedCriteria;
var nonPreliminaryOrders = QueryOver.Of<RefImplOrderEntity>()
.Where(Subqueries.PropertyIn(nameof(RefImplOrderEntity.ExternalId), list));
.Select(x => x.ExternalId)
.DetachedCriteria
var finalList = session.QueryOver<IntOrderInvoiceCostOut>()
.Where(Subqueries.PropertyIn(nameof(IntOrderInvoiceCostOut.ExternalId), nonPreliminaryOrders));
.List();

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

NHibernate Hierarchical Master Details QueryOver

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

Categories

Resources