Fetching a lazy loaded property in NHibernate that *isn't* a reference - nhibernate

I have an entity with a binary column that's set to lazy load in the mapping. In some cases, though, we want to get the entity along with the binary data at the same time. I tried using Linq.Fetch(x => x.BinaryData), but that gives an invalid join exception. Understandable, considering it shouldn't be a join in the first place. Is there a way to get this working? I'm using NHibernate 3.1
This is the mapping:
Map(x => x.BinaryData)
.CustomSqlType("image")
.Length(int.MaxValue)
.Not.Nullable()
.LazyLoad(); // Wanna make sure we don't kill the app by loading the image data when we don't need it.
This is the fetching:
Linq.Where(x => x.Id == id).Fetch(x => x.BinaryData).FirstOrDefault();

This looks like to be not possible at the moment : https://nhibernate.jira.com/browse/NH-2888
So, You have to use HQL :
var post = session.CreateQuery("from Post fetch all properties")
.SetMaxResults(1)
.UniqueResult<Post>();
Source : http://ayende.com/blog/4377/nhibernate-new-feature-lazy-properties

In HQL you can use fetch all properties to eagerly load lazy property. But in NH3.1 it is not yet implemented for Linq queries. As I know this bug is in NHibernate Jira so you can check if it is resolved or you can fix it yourself. For our company prototype i fixed this bug, but I did so in very brute-force way so i didn't send patch to NHibernate project

Related

Override lazy loading behavior 'lazy=false'

I would like to override the default lazy loading behavior which is set in mappings as 'lazy=false'. Can't change it as many parts of existing application depend on this setting.
After spent some hours on it I didn't find a solution so I'm asking here.
How to do this?
What I want to achieve is to fine tune my query to load only what is needed.
This is what I've tried already:
Using QueryOver api:
var properties = session.QueryOver<Property>()
.Fetch(prop => prop.Transactions).Eager
.Fetch(prop => prop.Districts).Eager
//I dont want to load those entities below so I mark
//them as lazy - IT DOESN'T WORK
//I can see in SQL that they are getting loaded in separate queries
.Fetch(prop => prop.Districts.First().Aliases).Lazy
.Fetch(prop => prop.Districts.First().PolygonPoints).Lazy
.Skip(i * pageSize)
.Take(pageSize)
.List();
Using Criteria api:
var criteria = session.CreateCriteria<Property>();
criteria.SetFetchMode("Transactions", NHibernate.FetchMode.Join);
criteria.SetFetchMode("Districts", NHibernate.FetchMode.Join);
criteria.SetFetchMode("Districts.Aliases", NHibernate.FetchMode.Select); // tried Lazy too
criteria.SetFetchMode("Districts.PolygonPoints", NHibernate.FetchMode.Select); // tried Lazy too
criteria.AddOrder(NHibernate.Criterion.Order.Asc("Id"));
criteria.SetFirstResult(i * pageSize);
criteria.SetMaxResults(pageSize);
var properties = criteria.List<Property>();
Using any of the above methods 'Aliases' and 'PolygonPoints' are always being loaded when calling List<>(). I don't need them in my process.
I'm using Nhibernate 4.0.
Any ideas?
We cannot override mapping in this case. We can do it opposite way - have lazy in place - and use eager fetching for querying.
The decision process (of reference loading) is done outside of the query, ex post. So it could be pre-loaded, but cannot be avoided.
Solution here could be of two types.
The first is preferred (by me) - do your best and make laziness the default: Ayende -
NHibernate is lazy, just live with it
Use projections. Instruct NHibernate to create just one query, use transformer to get expected object graph - without any proxies in it
There is pretty clear example how to (properly) use projection list even for references:
Fluent NHibernate - ProjectionList - ICriteria is returning null values
And we would also need Custom result transformer, which will ex-post create all the references from the returned data:
Custom DeepResultTransfomer

NHibernate Eager Loading with Queryover API on a complex object graph

I've got a pretty complex object graph that I want to load in one fell
swoop.
Samples have Daylogs which have Daylog Tests which have Daylog
Results
Daylog Tests have Testkeys, Daylog Results have Resultkeys, and
TestKeys have Resultkeys.
I'm using the QueryOver API and Future to run these all as one query,
and all the data that NHibernate should need to instantiate the entire
graph IS being returned, verfied by NHProf.
public static IList<Daylog> DatablockLoad(Isession sess,
ICollection<int> ids)
{
var daylogQuery = sess.QueryOver<Daylog>()
.WhereRestrictionOn(dl => dl.DaylogID).IsIn(ids.ToArray())
.Fetch(dl => dl.Tests).Eager
.TransformUsing(Transformers.DistinctRootEntity)
.Future<Daylog>();
sess.QueryOver<DaylogTest>()
.WhereRestrictionOn(dlt =>
dlt.Daylog.DaylogID).IsIn(ids.ToArray())
.Fetch(dlt => dlt.Results).Eager
.Inner.JoinQueryOver<TestKey>(dlt => dlt.TestKey)
.Fetch(dlt => dlt.TestKey).Eager
.Inner.JoinQueryOver<ResultKey>(tk => tk.Results)
.Fetch(dlt => dlt.TestKey.Results).Eager
.Future<DaylogTest>();
sess.QueryOver<DaylogResult>()
.Inner.JoinQueryOver(dlr => dlr.DaylogTest)
.WhereRestrictionOn(dlt =>
dlt.Daylog.DaylogID).IsIn(ids.ToArray())
.Fetch(dlr => dlr.ResultKey).Eager
.Fetch(dlr => dlr.History).Eager
.Future<DaylogResult>();
var daylogs = daylogQuery.ToList();
return daylogs;
}
However, I still end up with proxies to represent the relationship
between Testkey and ResultKey, even though I'm specifically loading
that relationship.
I think this entire query is probably representative of a poor
understanding of the QueryOver API, so I would like any and all advice
on it, but primarily, I'd like to understand why I get a proxy and not
a list of results when later I try to get
daylogresult.resultkey.testkey.results.
Any help?
The answer was to call NHibernateUtil.Initialize on the various objects. Simply pulling the data down does not mean that NHibernate will hydrate all the proxies.
You have to load all your entities in one QueryOver clause to get rid of proxies. But in this case you will have a lot of joins in your query, so I recommend to use lazy loading with batching.

Fluent NHibernate LazyLoad Issues

I couldn't find an answer to this issue so I assume it is something I am doing wrong.
I have a PersistenceModel set up where I have set a convention as follows: -
persistenceModel.Conventions.Add(DefaultLazy.Always());
However, for one of the HasManyToMany relationships in one of my entities I want eager loading to take place which I am setting up as follows: -
HasManyToMany(x => x.Affiliates).Not.LazyLoad();
Intuitively, I expect eager loading to take place as I am overriding the lazy load default that I have specified as a convention but it still lazy loads. If I set the DefaultLazy convention to never and then set LazyLoad on an individual relationship it doesn't work either.
Any ideas?
When you set Not.LazyLoad(), you tell NHibernate to load Affiliates when the parent loads. NHibernate will do this by performing another select on the Affliates many-to-many table regardless of whether you access the Affiliates collection or not. NHibernate is using another select because that is the default fetching mode. You want to override fetching mode as well, either in the query or in the mapping. To do it in the mapping, add the following:
HasManyToMany(x => x.Affiliates)
.Not.LazyLoad()
.Fetch.Join();
You might also want to include a ".Cascade.AllDeleteOrphan()" if you want NHibernate to persist new Affiliaites added to the collection and delete orphaned ones. If you do not do this, you will have to explicitly call session.Save(newAffiliate). Otherwise you'll receive a TransientObjectException when your Affiliates collection contains a new Affiliate.
It may be one stupid thing to ask, but have you execute the query inside your session? Say,
Using(var session = OpenSession())
{
session.Query<Entity>().ToList();
}
I had this problem before, and finally realized the objects that I was accessing hadn't been queried before disposing the session.

Unable to figure out how to do Joins within IQueryable

Here is what I am trying:
IQueryable query = this.MyRepository.GetShippingCollection();
IList<SomeListType> myList = query.Where(x => x.Settings
.Where(y => y.SelectorID.Equals(5))
.Count() > 0)
.OrderBy(x => x.Order)
.ToList();
Produces this error:
could not resolve property: Settings.ID
If I do it this way it works, but causes over 3,000 queries on my SQL Server:
IList<SomeListType> myList = this.MyRepository.GetShippingCollection().ToList();
myList = myList.Where(x => x.Settings
.Where(y => y.SelectorID.Equals(5))
.Count() > 0)
.OrderBy(x => x.Order)
.ToList();
I know the solution resides within using a "Join".
I have been looking at examples for the last couple hours and can only find Join examples within the Mapping file. I am also finding examples for "ICriteria".
I don't want to have to create seporate entries for all my complex queries in the mapping file so the join within that file will not work.
Since I am using Fluent NHibernate, I am not using "ICriteria". I am using "IQueryable". How do you join multiple tables within "IQueryable"?
Any help with this would be greatly appreciated.
Thanks in advance.
If the second query is executing 3,000 queries, it is almost certainly lazy-loading the Settings collection. As you iterate over the list, you access this collection, and each time NHibernate goes back to the database to fetch it. Try setting the fetch mode for the Settings property to eager load in the mapping.
Beyond that, the LINQ provider could be an issue. What version of NHibernate are you using? The 2.x LINQ provider has some real limitations. It has been reimplemented in the 3.0 trunk, but you'll have to download and compile it from the source.
By the way, ICriteria vs IQueryable is not related to Fluent NHibernate. Criteria API and LINQ are two providers through which you can create queries. Fluent NHibernate is an alternative way to perform configuration.

How do I use Fluent Nhibernate many-to-many for optimal performance?

I have a product table that has a many-to-many relation to itself (using a two-column many-to-many table) and I have set it up in Fluent NHibernate with the following code:
public class ProductConfiguration : ClassMap<Product>
{
public ProductConfiguration()
{
Table("Product");
Id(p => p.Id).GeneratedBy.Guid();
Map(p => p.Name).Not.Nullable().Length(254);
Map(p => p.Description).Not.Nullable().Length(1000);
Map(p => p.CreatedAt).Not.Nullable();
HasManyToMany(p => p.CrossSell)
.Table("ProductCrossSell")
.ParentKeyColumn("Id")
.ChildKeyColumn("ProductId");
}
}
My MVC application has two pages that uses this setup:
Index - Uses a generic repository GetAll method to display all products.
Detail - Uses a generic repository GetById method to display one product and any related cross sell products setup in the many-to-many realation.
It looks like NHibernate is set to LazyLoad the many-to-many by default so when I fire up the application and watch it in profiler I can see that it does LazyLoad the many-to-many with the following alert "Use of implicit transactions is discouraged".
How do I get rid of this alert? I couldn't find any information on how to wrap a LazyLoad inside a transaction to get rid the alert. Is it even possible?
Is there a way to not lazyload this by telling NHibernate that whenever I ask for GetById make sure to join the tables a get everything in one query? I tried using .Fetch.Join() in the many-to-many mapping but that also affected my GetAll query which now displays a joined result set as well which is incorrect.
What is the best apprach for this kind of simple scenario?
Thanks
The way to get rid of the warning is to access the object graph and fully populate the UI elements inside a single transaction.
Not by configuration. You can create an HQL query that eager fetches the association and use that query for a specific view. I would stick with lazy loading and not make that optimization unless needed. The HQL would be:
return session.CreateQuery("from ProductionConfiguration pc join fetch pc.CrossSell where pc.Id = ?")
.SetGuid(0, id)
.List<ProductConfiguration>();
All collections are lazily loaded in NHibernate by default.
You must be triggering loading with a call of some kind (maybe even with the debugger watches)