Ayende describes a really great way to get page count, and a specific page of data in a single query here:
http://ayende.com/blog/2334/paged-data-count-with-nhibernate-the-really-easy-way
His method looks like:
IList list = session.CreateQuery("select b, rowcount() from Blog b")
.SetFirstResult(5)
.SetMaxResults(10)
.List();
The only problem is this example is in HQL, and I need to do the same thing in an ICriteria query. To achieve the equivalent with ICriteria, I need to do something like:
IList list = session.CreateCriteria<Blog>()
.SetFirstResult(5)
.SetMaxResults(10)
.SetProjection(Projections.RootEntity(), Projections.SqlFunction("rowcount", NHibernateUtil.Int64))
.List();
The problem is there is no such thing as Projections.RootEntity(). Is there any way to select the root entity as one of the projections in a projection list?
Yes, I know I could just use CriteriaTransform.TransformToRowCount() but that would require executing the query twice - once for the results and once for the row count. Using Futures may help a little by reducing it to one round-trip, but it's still executing the query twice on the SQL Server. For intensive queries this is unacceptable. I want to avoid the overhead, and return the row count and the results in the same query.
The basic question is: using ICriteria, is there any way to select the root entity AND some other projection at the same time?
EDIT: some related links:
https://nhibernate.jira.com/browse/NH-1372?jql=text%20~%20%22entity%20projection%22
https://nhibernate.jira.com/browse/NH-928
I implemented a RootEntityProjection. You can try the code, which you can find at: http://weblogs.asp.net/ricardoperes/archive/2014/03/06/custom-nhibernate-criteria-projections.aspx.
Let me know if it helped.
Related
NHibernate.Linq returns IQueryable giving me late evaluation. Can this also be done with QueryOver?
Update:
I will use it to define lots of queries where only a subset would be used. Therefor Future is not the solution, which would execute them all.
I like the IQueryable (IEnumerable) return type from NHibernate.Linq, that will never execute the query if never used.
Firstly, even QueryOver is just a set of definitions to be later converted to the SQL statement. So until you are working with a reference to a
IQueryOver<Entity, Entity> ab = session.QueryOver<Entity>();
and not calling List<Entity>() ... the execution is deferred. That's also how you can use the Detached queries 16.1. Structure of a Query
QueryOver<Cat> query = QueryOver.Of<Cat>()
.Where(c => c.Name == "Paddy");
Another powerful feature is Future. This represents a very easy way how to put few queries on the stack, and only when first of them is required... all are executed and passed to DB Server as a batch. Read here more: NHibernate Futures
They essentially function as a way to defer query execution to a later
date, at which point NHibernate will have more information about what
the application is supposed to do, and optimize for it accordingly
The biggest difference is that it cannot be returned as IQueryable when using QueryOver
EDIT: Extend on a question update
While IQueryable<TEntiy> could be returned only from a session.Query<TEntity>() and not when using QueryOver, ICriteria, HQL ... the same behavior could not be reached when using QueryOver.
Take for example: a Person that has a collection of Pets. I want to only list the Persons that have at least 5 pets.
I have tried:
var result = (from a in UnitOfWork.CurrentSession.QueryOver<Person>()
where a.Pets.Count >4
select a
).List()
But it says it does not recognize the property Count (which makes sense because it is not a DB field). I also tried Count() and it still doesn't work saying it doesn't understand that function (throws exception).
I've tried all kinds of subqueries and criteria methods but I don't know enough to put it all together. And I don't know whether I shold use LINQ or HQL or QueryOver or Criteria...It would be much much mch easier in SQL but I don't want to "cheat"
I have been searching google like crazy, and everything I found either does not compile or I get a runtime error
You are using QueryOver instead of LINQ (Query<T>() extension method)
Using nHibernate QueryOver, if I want to enforce a projection for performance, are "Select" and "Where" the same thing? In other words, will ..
var member = session.QueryOver<Member>()
.Select( projections => projections.Email == model.Email )
.Take(1).SingleOrDefault();
Run the same as
var member = session.QueryOver<Member>()
.Where( context => context.Email == model.Email )
.Take(1).SingleOrDefault();
Or is there a difference in the two?
Select projects (you could also say maps); Where filters. This is the same as SQL and all LINQ providers (and QueryOver is also sort of a LINQ provider). It seems that in this case you want to filter, not project, so you need Where
No offense intended, but I think the best way to answer a question like the one you asked is to try it. Sometimes things become clearer when you can see the output.
That said, when you use Select, you're telling NHibernate how to project your data. This determines the final makeup of the data resulting from the query. There's a little bit more to this, but that's the general idea. You use Where when you want to specify the criteria that the data that you are querying should satisfy.
I have two separate queries that both return the same IQueryable, and I'd like to combine them prior to projection. It looks like neither Union or Concat are implemented in Linq to NHibernate? Does anyone know how I might go about achieving this?
It's not possible. You'll have to do it on the client.
Example:
var allItems = queryable1.AsEnumerable().Concat(queryable2)
#Diago Mijelshon gives a good answer, but I would like to add that depending on what you're doing with the data, you may need to first cast it to an array or list so that NHibernate doesn't try to do any funny stuff with your operations.
I've used Entity Framework for years and I'm well familiar with this, and I've only used NHibernate a little bit, but the two tools appear to be similar in this regard.
I'm trying to reproduce the HqlQuery style 'select new ObjectToProjectOut' functionality. i.e. take a list of columns returned from a query and return as a list of ObjectToProjectOut types that are instantiated using a Constructor with as many parameters as the columns in the query.
This is in effect what 'select new ObjectToProjectOut' achieves in Hql.... but clearly that's not available in SqlQuery. I think I need to set a result transform and use either PassThroughResultTransformer, DistinctRootEntityResultTransformer etc to get it to work.
Anyone know what I should use ?
ok.... after looking at the NHibernate code it seems that I was looking for AliasToBeanConstructorResultTransformer.... of course!
However I may have found an nHibernate bug. If you have the same column name returned twice from two different tables (market.name and account.name, say) then by the time nHibernate returns the array from the db to the transformer, the first occurance of 'Name' will be used for both. Nasty.
Work around is to uniquely alias. With Hql, the sql generated is heavily aliased, so this is only a bug with SqlQuery.
Grrrr. Today must be my day, also found another nHibernate bug/issue I've posted to StackOverflow for comment.
You could use the AddEntity method to fill entities from a SQL query.
Here are two examples from the NHibernate docs:
sess.CreateSQLQuery("SELECT * FROM CATS")
.AddEntity(typeof(Cat));
sess.CreateSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS")
.AddEntity(typeof(Cat));