Why is everything being eagerly loaded? - nhibernate

I'm trying to get to grips with QueryOver, and I was expecting this to return me a Summary item with its ReportRows collection eagerly loaded.
Update
The first block of code wasn't in my original question but it was the cause of my problem - thanks to dotjoe for the answer
// some code to create a Summary and a whole graph of child records
...
// then...
session.Save(summary);
session.Flush(); // write the changes
session.Evict(summary); // clear out session to check my query below is 'fresh'
// Originally-posted code
Summary summaryAlias = null;
Report reportAlias = null;
var summaryQuery =
session.QueryOver<Summary>(() => summaryAlias)
.Fetch(x => summaryAlias.Reports).Eager
.Left.JoinAlias(() => summaryAlias.Reports, () => reportAlias)
.Where(() => summaryAlias.Id == workItemId);
Summary summary = summaryQuery.SingleOrDefault<Summary>();
session.Close();
However when I hit a breakpoint after session.Close() has been called (to prevent any further lazy loading), I find that everything in my Summary class has been populated, not just the ReportRows collection.
Some examples of things that have been populated even though I wasn't expecting them to be:
ReportRow.Student
ReportRow.Programme
ReportRow.Programme.Modules (a collection)
ReportRow.Programme.Modules.Components (another collection inside each 'Module')
I'm using Fluent automappings, and I've configured it to be lazy-loaded just to be sure using:
.Conventions.Add(FluentNHibernate.Conventions.Helpers.DefaultLazy.Always())
and also tried...
.Conventions.Add(FluentNHibernate.Conventions.Helpers.LazyLoad.Always())
Why is it loading everything?
Thanks

Could you post the generated hbm.xml? example
Also, try this query and see what happens if you access any lazy properties after the using statements...
Summary summary = null;
using(var session = factory.OpenSession())
using(var tx = session.BeginTransaction())
{
summary = session.QueryOver<Summary>()
//.Fetch(x => x.Reports).Eager
.Where(x => x.Id == workItemId)
.SingleOrDefault<Summary>();
tx.Commit();
}

Related

Iterating over controllers in solution

I am trying to get a list of all public methods that return an ActionResult from any controller in my solution using reflection but I am experiencing some strange behavior.
Assembly asm = Assembly.GetAssembly(typeof(MyDLL.MvcApplication));
var controllerActionList = asm.GetTypes().ToList();
If I run the above code I get a list of all my types including all my models and controllers etc. just like I would expect. However, if I modify it and run the below code my results list comes back empty. Any idea what's going on here? I would think this should filter the types down so I get a list of all the controllers right?
Assembly asm = Assembly.GetAssembly(typeof(MyDLL.MvcApplication));
var controllerActionList = asm.GetTypes()
.Where(type => typeof(Controller).IsAssignableFrom(type)).ToList();
I got it working by using the code below. I think the direct type comparison is failing for me because I believe I have two different .net versions between these 2 projects.
Assembly asm = Assembly.GetAssembly(typeof(SCCView.MvcApplication));
var controllerActionList = asm.GetTypes()
.Where(type => type.BaseType.Name == "Controller")
.SelectMany(type => type.GetMethods())
.Where(
m => m.IsPublic && m.ReturnType.Name == "ActionResult")
.Select(x => new {Controller = x.DeclaringType.Name, Action = x.Name})
.OrderBy(x => x.Controller).ThenBy(x => x.Action).ToList();
The above will give you a paired list of every public method in a controller that returns an ActionResult

NHibernate Stackoverflow exception in composite relationship

I get a stackoverflow exception when trying to remove a composite object from a collection (but only when i wrap it inside a transaction). However getting the object and deleting it on the session, instead of the collection, and saving the parent works.
The code looks like this:
using (var session = sessionProvider.GetSession())
{
using (var transaction = session.BeginTransaction())
{
var products = session.Query<MyObject>().Select(x => x.MyObjectId > 0).ToList();
//Would fail
var variant = session.Query<MyObject>().FirstOrDefault(x => x.Name == "some name that exists");
var parent = MyObject.Parent;
parent.RemoveMyObject(variant);
session.Save(parent);
//Doesn't fail
//session.Delete(variant);
transaction.Commit();
}
session.Flush();
}
And my mapping looks like this:
HasMany(x => x.MyObject).AsSet().Cascade.AllDeleteOrphan().Inverse().KeyColumn("MyObjectId").BatchSize(2000);
References(x => x.MyObject).Column("MyObjectId");
What should i do to fix this ? It should work in both ways. And it is, as said, only when there's a transaction around. Both approaches works without a transaction.
Best regards
Morten

Is this the right way to eager load child collections in NHibernate

I have used too much time to find a nice way to eager load child collections. so far this is what I got. It's working but I find it hard to believe that this is the best way to write this query
[Fact]
public void EagerLoadQueryOverWithFutureTest()
{
const long journalNr = 1470;
var query = _repo.QueryOver().Where(s => s.JournalNr == journalNr).Future<Sag>();
var sagsansoegning = _repo.QueryOver().Where(an => an.JournalNr == journalNr).Fetch(x => x.Sagsansoegning).Eager.Future<Sag>();
var noter = _repo.QueryOver().Where(n => n.JournalNr == journalNr).Fetch(x => x.Noter).Eager.Future<Sag>();
var filer = _repo.QueryOver().Where(f => f.JournalNr == journalNr).Fetch(x => x.Filer).Eager.Future<Sag>();
var emails = _repo.QueryOver().Where(e => e.JournalNr == journalNr).Fetch(x => x.Emails).Eager.Future<Sag>();
var journal = _repo.QueryOver().Where(j => j.JournalNr == journalNr).Fetch(x => x.Journal).Eager.Future<Sag>();
var sagsTilstand = _repo.QueryOver().Where(t => t.JournalNr == journalNr).Fetch(x => x.Tilstand).Eager.Future<Sag>();
var boligsocialEvalueringer = _repo.QueryOver().Where(b => b.JournalNr == journalNr).Fetch(x => x.BoligsocialEvaluering).Eager.Future<Sag>();
var result = query.SingleOrDefault();
result.JournalNr.Should().Be(journalNr);
result.Emails.Should().HaveCount(c => c > 20);
result.Filer.Should().HaveCount(c => c > 20);
}
I would like to append different point of view, not the answer how to use "future" to make only one round-trip to DB.
Not sure why you need to fetch all the properties. I guess it could be 1) to be used by your code, e.g. to render UI or 2) as the API which somehow serializes that and passes it to the client.
In the first scenario, I would suggest to use the session per request and use the lazy load. In the second, I would suggest to introduce one step: building the DTO before passing it to client. That could be done while session is still open, therefore use the lazy load.
Lazy load in both scenarios. So, in both cases I would suggest to use different approach then explicit fetching. It will use more or less the same amount of selects and round-trips to DB. But at the end it could be more effective. Because all that job would be left on the NHibernate
The thing is to change the eager-fetch (manual) loading with the batch-size (native) loading. See more here 19.1.5. Using batch fetching. In this case, NHibernate will process your query, and then will go again with few more queries to load the remaining data. But only if really needed
The advantage is, that you are not a prisoner of your queries. You can consume any property which could/should be available on the entity, until the session is open (I.e. during View rendering or serializing). You do not have to go and append another explicit query over join to get eager data.
So, while Future could be solution to the world were your connectivity to DB from APP is very slow... it is not the world were to use NHibernate (ORM). The advantage of the lazy mapping and ad hoc loading (with power of batching) is from maintenance perspective the right way... I'd say

given a list of objects using C# push them to ravendb without knowing which ones already exist

Given 1000 documents with a complex data structure. for e.g. a Car class that has three properties, Make and Model and one Id property.
What is the most efficient way in C# to push these documents to raven db (preferably in a batch) without having to query the raven collection individually to find which to update and which to insert. At the moment I have to going like so. Which is totally inefficient.
note : _session is a wrapper on the IDocumentSession where Commit calls SaveChanges and Add calls Store.
private void PublishSalesToRaven(IEnumerable<Sale> sales)
{
var page = 0;
const int total = 30;
do
{
var paged = sales.Skip(page*total).Take(total);
if (!paged.Any()) return;
foreach (var sale in paged)
{
var current = sale;
var existing = _session.Query<Sale>().FirstOrDefault(s => s.Id == current.Id);
if (existing != null)
existing = current;
else
_session.Add(current);
}
_session.Commit();
page++;
} while (true);
}
Your session code doesn't seem to track with the RavenDB api (we don't have Add or Commit).
Here is how you do this in RavenDB
private void PublishSalesToRaven(IEnumerable<Sale> sales)
{
sales.ForEach(session.Store);
session.SaveChanges();
}
Your code sample doesn't work at all. The main problem is that you cannot just switch out the references and expect RavenDB to recognize that:
if (existing != null)
existing = current;
Instead you have to update each property one-by-one:
existing.Model = current.Model;
existing.Make = current.Model;
This is the way you can facilitate change-tracking in RavenDB and many other frameworks (e.g. NHibernate). If you want to avoid writing this uinteresting piece of code I recommend to use AutoMapper:
existing = Mapper.Map<Sale>(current, existing);
Another problem with your code is that you use Session.Query where you should use Session.Load. Remember: If you query for a document by its id, you will always want to use Load!
The main difference is that one uses the local cache and the other not (the same applies to the equivalent NHibernate methods).
Ok, so now I can answer your question:
If I understand you correctly you want to save a bunch of Sale-instances to your database while they should either be added if they didn't exist or updated if they existed. Right?
One way is to correct your sample code with the hints above and let it work. However that will issue one unnecessary request (Session.Load(existingId)) for each iteration. You can easily avoid that if you setup an index that selects all the Ids of all documents inside your Sales-collection. Before you then loop through your items you can load all the existing Ids.
However, I would like to know what you actually want to do. What is your domain/use-case?
This is what works for me right now. Note: The InjectFrom method comes from Omu.ValueInjecter (nuget package)
private void PublishSalesToRaven(IEnumerable<Sale> sales)
{
var ids = sales.Select(i => i.Id);
var existingSales = _ravenSession.Load<Sale>(ids);
existingSales.ForEach(s => s.InjectFrom(sales.Single(i => i.Id == s.Id)));
var existingIds = existingSales.Select(i => i.Id);
var nonExistingSales = sales.Where(i => !existingIds.Any(x => x == i.Id));
nonExistingSales.ForEach(i => _ravenSession.Store(i));
_ravenSession.SaveChanges();
}

How do you work with detached QueryOver instances?

This NHibernate blog entry notes how detached QueryOver queries (analogous to DetachedCriteria) can be created (using QueryOver.Of<T>()). However, looking this over, it doesn't look analogous to me at all.
With DetachedCriteria, I would create my instance and set it up however I need, and afterwards call GetExecutableCriteria() to then assign the session and execute the query. With the "detached" QueryOver, most of the API is unavailable (ie, to add restrictions, joins, ordering, etc...) until I call GetExecutableQueryOver, which requires takes an ISession or IStatelessSession, at which point you are no longer disconnected.
How do you work with detached QueryOver instances?
EDIT:
Actual problem was related to how I'm storing the detached QueryOver instance:
public class CriteriaQuery<T>
{
internal protected QueryOver<T> _QueryOver { get; set; }
public CriteriaQuery()
{
_QueryOver = QueryOver.Of<T>();
}
// Snip
}
It should be a QueryOver<T, T>.
I'm using NHibernate 3.1.0.4000. The following code compiles successfully:
Employee salesRepAlias = null;
var query = QueryOver.Of<Customer>()
.JoinAlias(x => x.SalesRep, () => salesRepAlias)
.Where(x => x.LastName == "Smith")
.Where(() => salesRepAlias.Office.Id == 23)
.OrderBy(x => x.LastName).Asc
.ThenBy(x => x.FirstName).Asc;
return query.GetExecutableQueryOver(session)
.List();
This illustrates using restrictions, joins, and ordering on a detached QueryOver just like you would with a regular one.
Could you please post the code that demonstrates the API features that are unavailable?