views:

826

answers:

3

I'm working with NHibernate and need to retrieve and process up to 2 million rows. Ideally, I could process each row - one at a time - without NHibernate loading all 2 million in memory at once (because, you know, that hurts).

I'd prefer to get an IEnumerable which would call the data reader iteratively for each read so I could process the data read - then discard it. By doing it this way I save a boatload of memory, and begin processing results far faster. I could also improve performance through multithreading and/or the use of PLinq.

Is this possible with NHibernate's ICriteria? Everything it returns seems to be IList, and fully loaded before handing the collection reference off. Why IList instead of IEnumerable?!

I don't mean "lazy" in the traditional sense that NHibernate uses with regards to loading child or parent objects. I want a lazy IEnumerable meaning someway of getting a IEnumerable from an ICriteria object. ICriteria only has a List() method which loads the results in an ArrayList.

A: 

What you want to do is wrap your data access in a method like so:

public IEnumerable<YourObject> GetALotOfRows() {
  ..execute DataReader
  while(..read..) {
    yield return yourObject;
  }
}

Don't have VS or nHibernate handy now, so sorry for semi-pseudo code. But the key here is to use "yield return".

Strelok
I believe NHibernate's execute statement makes it not Lazy like the OP wanted.
sixlettervariables
When the OP said LAZY I think he meant something like what the SqlDataReader does.
Strelok
This is what I want, but I want to know how to make NHibernate do it for me, or at least support it. NHibernate hands me an IList.... Actually, I use Linq to NHibernate (from NContrib), but that doesn't seem to matter....
TheSoftwareJedi
A: 

What kind of operation is it that you have to do it row by row ? I'm just curious :).

You could try to page the results - get the first 10, the next 10 ... and so on.

EDIT: So you would have

Session.CreateCriteria(typeof(T)).SetFirstResult(0).SetMaxResults(1).UniqueResult<T>();
Session.CreateCriteria(typeof(T)).SetFirstResult(1).SetMaxResults(1).UniqueResult<T>();
Session.CreateCriteria(typeof(T)).SetFirstResult(2).SetMaxResults(1).UniqueResult<T>();

You get the picture, I guess it's not the best way, it's not IEnumerable ... but it would work. You could also do SetMaxResults(10) or something bigger, so not to send 1 at a time.

sirrocco
How would I page the results?! What I'm doing is streaming the results back to an HTTP client as a CSV file. I already implemented the CSVStream, and it would support converting and writing one record at a time. I just need NHibernate to do that too. FROM ICRITERIA. :)
TheSoftwareJedi
There is no method to get an IEnumerable from ICriteria.
Mauricio Scheffer
+1  A: 

ICriteria doesn't have any methods that return an IEnumerable, but IQuery does.

Mauricio Scheffer
is there a way to transform an ICriteria to an IQuery?
TheSoftwareJedi
AFAIK IQuery is for HQL and SQL (string-based queries) and ICriteria is... well, Criteria, I don't see any bridges between the two. I find it weird that ICriteria doesn't implement an Enumerate...
Mauricio Scheffer
Me too. I'm using the Linq-NHibernate code from NContrib, and they implement using an ICriteria (after looking at their code). I'll try and fix their crap. Any idea when NHibernate will support Linq officially (I know the beginnings of it are there).
TheSoftwareJedi
Official Linq support is coming with the 2.1 release.
Mauricio Scheffer