Am I doing something wrong with Nhibernate Query Over fetch? - nhibernate

I have this
using (ITransaction transaction = session.BeginTransaction())
{
Task tAlias = null;
CompletedTask cAlias = null;
List<Task> tasks = session.QueryOver<Task>(() => tAlias)
.Where(Restrictions.In(Projections.Property(() => tAlias.Course.Id), courseIds))
.Fetch(pt => pt.PersonalTaskReminders).Eager
.List<Task>().ToList().ConvertToLocalTime(student);
transaction.Commit();
return tasks;
}
PersonalTaskReminders == Collection
So a task can have many personalTaskReminders. I am finding though if I set 2 personalTaskReminders(so PersonalTaskReminders will now have 2 rows in it's collection from the db)
That it returns the same task twice.
So if I had 50 personaltaskReminders for that task. I would get 50 results of the same task. I don't understand why.
If I remove the eager loading. I get the one task back from the database as I expected.

It is obvious, because eager fetch causes join with 2 tables. To get rid of duplicated results you should use DistinctRootEntityTransformer.
By the way, NHibernate offers much nicer syntax for IN clause. So your query should look like this:
var tasks = Session.QueryOver<Task>()
.WhereRestrictionOn(x => x.Id).IsIn(courseIds)
.Fetch(pt => pt.PersonalTaskReminders).Eager
.TransformUsing(Transformers.DistinctRootEntity)
.List<Task>();

Xelibrion's solution is the right way to fix the problem.
To understand why the results are duplicated when doing Fetch, you can compare the generated SQL:
Without Fetch the fields in the SELECT are just your root entity Task fields.
With Fetch the fields of PersonalReminder entities have been added to the SELECT. So if you have two PersonalReminder registers for the same Task you get two registers in the results, and a DISTINCT clause would not remove them (as the real returned registers are different because they contain the PersonalReminder fields).
The SQL generated with and without TransformUsing is exactly the same, but NH processes the returned registers to remove the duplicates.

Related

NHibernate Queryover - How do I return only a child collection

I am running into difficulty with the following nhibernate queryover query. I am probably over-complicating things, but here is the problem:
I have an entity named AuctionStatistic that links to an auction (this is uni-directional and I do not have links from auctions back to statistics)
I would like to query the statistic table, find all auction IDs and pull back only those that meet a certain threshold - i.e. top 500 auctions by views
Once I've gotten the top X (in this example i'm hardcoding to 10000 views) I want to pull back the auction id and name. For this particular query I don't need any of the data stored in the statistics table (though this is used elsewhere and is not redundant)
I figured I could use something like the following to get back just the auctions, but because I'm querying over AuctionStatistic it expects the selected value to be of type AuctionStatistic (or a list thereof)
var auctions = _session.QueryOver<AuctionStatistic>().Where(c => c.ViewCount > 10000).Fetch(x=>x.Auction).Eager.Select(x=>x.Auction);
Can anyone suggest a better way of doing this?
Thanks
JP
Without bi-directional this is probably your best bet.
Auction auctionAlias = null;
AuctionDTO dto = null;
var auctionDtos = _session.QueryOver<AuctionStatistic>()
.Where(c => c.ViewCount > 10000)
.JoinAlias(x => x.Auction, () => auctionAlias)
.SelectList(list => list
.Select(() => auctionAlias.id).WithAlias(() => dto.id)
.Select(() => auctionAlias.name).WithAlias(() => dto.name))
.TransformUsing(Transformers.AliasToBean<AuctionDTO>())
.List<AuctionDTO>();

nHibernate QueryOver performing a SubSelect to load a collection instead of a Join

Ok I'm trying to load some many to many collections on my User object (Followers, Following, PostLikes, CommentLikes). However when I perform a Left Join on these collections using QueryOver it returns more records than should be returned.
I looked at the SQL with SQL Profiler and it seems as instead of just producing 4 joins it is producing 8 creating a somewhat looping query. This is my current query.
User userAlias = null;
User followingAlias = null;
User followersAlias = null;
Post postLikesAlias = null;
Comment commentLikesAlias = null;
var entity = Session.QueryOver(() => userAlias)
.Where(x => x.Id == id)
.Left.JoinAlias(() => userAlias.Followers, () => followersAlias)
.Left.JoinAlias(() => userAlias.Following, () => followingAlias)
.Left.JoinAlias(() => userAlias.PostLikes, () => postLikesAlias)
.Left.JoinAlias(() => userAlias.CommentLikes, () => commentLikesAlias)
.SingleOrDefault();
ReleaseCurrentSession();
return entity;
Anyway when I do not selectively load things and use eager loading through my fluent mappings. The collections load perfectly. Again I looked at Sql Profiler and it seems to execute a separate select query for each collection. Is there a way I can do this using QueryOver instead of using joins? I know in your mappings you can specify FetchTypes, but when I do this and just use .Fetch(x => x.Followers) etc it still produces a join!
Thanks in advanced,
Jon
Try using this as the end of your query.
.TransformUsing(new DistinctRootEntityResultTransformer())
.SingleOrDefault();
or
.TransformUsing(new DistinctRootEntityResultTransformer())
.List();
and accessing the first item.
You can't do it that way. NHibernate doesn't issue separate queries for collections. So querying a collection is easy when you're only dealing with a single collection off the root.
You can use CreateMultiCriteria() to create separate queries batched together and transform them into a single result.
Alternatively i believe you can also use .Future() on each of the queries so they are batched together, NH will use first level cache to grab the collections.

Multiple Fetches in linq to nhibernate

I was looking at this
Be careful not to eagerly fetch
multiple collection properties at the
same time. Although this statement
will work fine:
var employees =
session.Query()
.Fetch(e => e.Subordinates)
.Fetch(e => e.Orders).ToList();
I need to fetch 2 references so I would need to do something like that. Is there a better way of doing this.
I can't do .ThenFetchMany() as it goes to the into the child objects but the ones I am after on the same level.
Well, the query will still return the results you want, but as stated, it will return a cartesian product, i.e. the SQL query will return count(e.Subordinates) * count(e.Orders) results, which could add up pretty quickly, especially if you have more than just two collections.
NHibernate introduced Futures with the 2.1 release. Unfortunately there seems to be no way in the current NHibernate 3.0 release to make them work with NHibernate.Linq (session.Query<T>()).
Futures allow you to perform multiple queries in one roundtrip to the database (as long as the DB supports it, but most do). In that case you will only have count(e.Subordinates) + count(e.Orders) results, which is obviously the minimum.
Futures work with the criteria API, HQL and they are supposed to work with the new QueryOver API (I have not tested that, yet).
NHibernate.Linq does have Query().ToFuture() and Query().ToFutureValue(), but so far I only get Exceptions when I use them.
Edit:
I just checked again for the Linq API and it seems as if it is working if you do not use Fetch. The following will result in three SQL queries that are executed in one roundtrip. The total number of rows return will be 1 + count(Subordinates) + count(Orders).
int id = 1;
// get the Employee with the id defined above
var employee = repo.Session.Query<Employee>()
.Where(o => o.Id == id)
.ToFuture<Employee>();
// get the Subordinates (these are other employees?)
var subordinates = repo.Session.Query<Employee>()
.Where(o => o.HeadEmployee.Id == id)
.ToFuture<Employee>();
// get the Orders for the employee
var orders = repo.Session.Query<Order>()
.Where(o => o.Employee.Id == id)
.ToFuture<Order>();
// execute all three queries in one roundtrip
var list = employee.ToList();
// get the first (and only) Employee in the list, NHibernate will have populated the Subordinates and Orders
Employee empl = list.FirstOrDefault();
Thank you for having marked this as the answer anyway.

How to delete data in DB efficiently using LinQ to NHibernate (one-shot-delete)

Producing software for customers, mostly using MS SQL but some Oracle, a decision was made to plunge into Nhibernate (and C#).
The task is to delete efficiently e.g. 10 000 rows from 100 000 and still stay sticked to ORM.
I've tried named queries - link already,
IQuery sql = s.GetNamedQuery("native-delete-car").SetString(0, "Kirsten");
sql.ExecuteUpdate();
but the best I have ever found seems to be:
using (ITransaction tx = _session.BeginTransaction())
{
try
{
string cmd = "delete from Customer where Id < GetSomeId()";
var count = _session.CreateSQLQuery(cmd).ExecuteUpdate();
...
Since it may not get into dB to get all complete rows before deleting them.
My questions are:
If there is a better way for this kind of delete.
If there is a possibility to get the Where condition for Delete like this:
Having a select statement (using LinQ to NHibernate) => which will generate appropriate SQL for DB => we get that Where condition and use it for Delete.
If there is a better way for this kind of delete.
Yes, you could use HQL instead of SQL.
If there is a possibility to get the Where condition for Delete [using Expressions]:
No, AFAIK that's not implemented. Since NHibernate is an open source project, I encourage you to find out if anyone has proposed this, and/or discuss it on the mailing list.
Thanks for your quick reply. Now I've probably got the difference.
session.CreateSQLQuery(cmd).ExecuteUpdate();
must have cmd with Delete From DbTable. On the contrary the HQL way
session.CreateQuery(cmd).ExecuteUpdate();
needs cmd with Delete From MappedCollectionOfObjects.
In that case it possibly solves my other question as well.
There now is a better way with NHibernate 5.0:
var biggestId = GetSomeId();
session.Query<Customer>()
.Where(c => c.Id < biggestId)
.Delete();
Documentation:
//
// Summary:
// Delete all entities selected by the specified query. The delete operation is
// performed in the database without reading the entities out of it.
//
// Parameters:
// source:
// The query matching the entities to delete.
//
// Type parameters:
// TSource:
// The type of the elements of source.
//
// Returns:
// The number of deleted entities.
public static int Delete<TSource>(this IQueryable<TSource> source);

Eager loading child collection with NHibernate

I want to load root entities and eager load all it's child collection and aggregate members.
Have been trying to use the SetFetchMode in FluentNHibernate, but I am getting duplicates in one of the child collection since I have a depth of 3 levels. DistinctRootEntityResultTransformer unfortunately only removes the root duplications.
return Session.CreateInvoiceBaseCriteria(query, archived)
.AddOrder(new Order(query.Order, query.OrderType == OrderType.ASC))
.SetFetchMode("States", FetchMode.Eager)
.SetFetchMode("Attestations", FetchMode.Eager)
.SetFetchMode("AttestationRequests", FetchMode.Eager)
.SetFetchMode("AttestationRequests.Reminders", FetchMode.Eager)
.SetResultTransformer(new DistinctRootEntityResultTransformer())
.List<Invoice>();
Could I use multi queries or something similar to archive this?
Furthermore, wouldn't this approach result in unnecessarily huge result sets from the database?
Any suggestions?
Found a solution but it isn't pretty. First I go and find all the Invoice IDs, then I use them in the multiquery and then at the end filtering the results through a HashedSet. Because of the large number of items sometimes i couldn't use the normalt Restriction.In and was forced to send it as a string.
Any suggested tweaks?
var criteria = Session.CreateInvoiceBaseCriteria(query, archived)
.SetProjection(Projections.Id());
var invoiceIds = criteria.List<int>();
if (invoiceIds.Count > 0)
{
var joinedIds = JoinIDs(criteria.List<int>()); // To many ids to send them as parameters.
var sql1 = string.Format("from Invoice i inner join fetch i.States where i.InvoiceID in ({0}) order by i.{1} {2}", joinedIds, query.Order, query.OrderType.ToString());
var sql2 = string.Format("from Invoice i inner join fetch i.AttestationRequests where i.InvoiceID in ({0})", joinedIds);
var sql3 = string.Format("from Invoice i inner join fetch i.Attestations where i.InvoiceID in ({0})", joinedIds);
var invoiceQuery = Session.CreateMultiQuery()
.Add(sql1)
.Add(sql2)
.Add(sql3);
var result = invoiceQuery.List()[0];
return new UniqueFilter<Invoice>((ICollection)result);
}
return new List<Invoice>();
To answer your question: yes, it results in huge resultsets.
I suggest to:
just naively write your queries without eager fetching
On certain places, put an eager fetch, but only one per query
if you really get performance problems which you can't solve with indexes or by enhance queries and mapping strategies, use your solution with the multiple queries.
While it might not be exactly what you are looking for, I would recommend looking at this article:
Eager loading aggregate with many child collections
If you look around the rest of that site you will find even more posts that discuss eager loading and other great nHibernate stuff.