I am having a performance issue on a system I'm working on when I try to load ~7700 records from the DB. NHB takes 10x longer to do its load, vs the same query using SqlDataReader
. Obviously there is going to be some performance overhead using NHB, but 10x the time vs ADO.NET seems excessive. From what I can determine, NHB is the issue, specifically in how it handles the collections which hang off the entity.
We have this general issue across our system when we try to load large(ish) numbers of entities, when the entities themselves have a large number of collections hanging off them.
I'm hoping someone can lend some advice on how I can improve this issue.
The query is simple.
var employees = session.QueryOver<Employee>().List();
There are no Select n+1 / lazy fetching issues occurring either. The single DB statement issued is effectively select * from employee
.
I wont post up the full fluent mapping for the entity unless someone asks, as its quite large, but its fairly standard. The entity in question consists of:
24 fields
3 1-1 Relationships
86 1-* Relationships
Extract:
HasMany(e => e.Roles)
.Access.CamelCaseField(Prefix.Underscore)
.Cascade.AllDeleteOrphan()
.Fetch.Select()
.Inverse()
.LazyLoad()
.KeyColumns.Add("[EmployeeId]")
.Cache.ReadWrite().IncludeAll();
Below are the average timings of 5 tests executed 1000 times. The timing starts / stops when I create/dispose of the NHB session, it does not include creation of the session factory.
00:00:00.7080910 - 7677 records loaded/entity instantiation/manual map using ADO.NET
00:00:01.6055084 - 7677 entities - session - Fields Only
00:00:01.7866198 - 7677 entities - session - Fields + 1-1 Relationships
00:00:11.3384154 - 7677 entities - session - Fields + 1-1 Relationships + 1-* Relationships
00:00:10.9083002 - 7677 entities - stateless session - Fields + 1-1 Relationships + 1-* Relationships
Just to be clear, this is not a DB/network/lazy fetching issue. IMO the issue seems to be in how NHB hydrates the entities.
Edit:Even though the fNHB mapping specifies caching, its disabled in the session factory. I explicitly removed them from the mapping files to verify, and the durations are unchanged.
I've also used ANTS to profile the code, and it seems the majority of time is spent in the TwoPhaseLoad.InitializeEntity
. (Disregard the other 50% of the time, which is not shown below. Its nHibernates session factory, which is not the cause of the performance issue)
Environment: nHibernate 3.3.1.4000, fnHibernate 1.3.0.733, MS SQL Server 2008, c# 4.0, Windows 7-64.