Unable to figure out how to do Joins within IQueryable - nhibernate

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.

Related

Is any method exist for QueryOver like Load for Get?

I'm using Fluent NHibernate and try to do many updates. First came in mind is code like this:
using (ISession s = OpenSession())
using (s.BeginTransaction())
{
IList<SomeType> items s.QueryOver<SomeType>()
.Where(someCondition)
.List();
items.ForEach(i => {
i.Foo = "bar";
s.Update(i);
});
s.Transaction.Commit();
}
But problem is in that every updating item must be loaded before do update. So database queried twice. In SQL it can be done with one query and i believe exist some way to do this with one query in NHibernate. And found doc for Load method that not actually load item from db and only work with some proxy instead and hit database only when i do update/delete.
Exist some method in NHibernate that load not items itself but proxy like Load?
For now for update large data best way is use HQL or raw SQL how mentioned #DavidOsborne.
I unfortunately not found any other methods that works lazy as Load method.

NHibernate QueryOver projection on many-to-one

I am trying to get a QueryOver working using a Projection on a many-to-one.
The class "Post" has a property many-to-one "Creator".
Using
session.QueryOver(Of Post).
Select(Projections.
Property(of Post)(Function(x) x.Creator).
WithAlias(Function() postAlias.Creator)).
TransformUsing(Transformers.AliasToBean(Of Post)()).
List()
works BUT each creator is retrieved by a single query rather than using a join like it is done when not using a select/projection. So if there are 5 posts with 5 different creators, 6 queries will be run 1 for the list of posts and 5 for the creators.
I tried to get it working using a JoinAlias but nothing really did the job.
I already searched for a solution, but all solutions I found did use the Linq-Provider which does not really fit since the actual "field list" is passed via a parameter.
Does anyone know if there is a solution to this other than the linq provider?
There is a solution, we can use projections for many-to-one and then custom result transformer.
DISCLAIMER: I can read VB syntax but do not have enough courage to write... I expect that you can read C# and convert it into VB....
So we can have projection like this:
// aliases
Post root = null;
Creator creator = null;
// projection list
var columns = Projections.ProjectionList();
// root properties
columns.Add(Projections.Property(() => root.ID).As("ID"));
columns.Add(Projections.Property(() => root.Text).As("Text"));
// reference properties
columns.Add(Projections.Property(() => creator.ID).As("Creator.ID"));
columns.Add(Projections.Property(() => creator.FirstName).As("Creator.FirstName"));
// so our projections now do have proper ALIAS
// alias which is related to domain model
// (because "Creator.FirstName" will be use in reflection)
var query = session.QueryOver<Post>(() => root)
.JoinAlias(() => root.Creator, () => creator)
.Select(columns)
Now we would need smart Transformer, our own custome one (plugability is power of NHibernate). Here you can find one:
public class DeepTransformer
And we can continue like this
var list = query
.TransformUsing(new DeepTransformer<Post>())
.List<Post>()
Check also this:
Fluent NHibernate - ProjectionList - ICriteria is returning null values
NHibernate AliasToBean transformer associations

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

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

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.

LLBLGEN: Linq to LLBGEN don't work

I want to make custom select from the database table using Linq. We use LLBGEN as ORM solution.
I can't do LINQ query to Entities Collection Class unless I call GetMulti(null) method of it.
Is it possible to do LINQ query to LLBGEN without extracting all table first?
BatchCollection batches = new BatchCollection();
BatchEntity batch = batches.AsQueryable()
.Where(i => i.RegisterID == 3)
.FirstOrDefault(); // Exception: Sequence don't contains any elements
batches = new BatchCollection();
batches.GetMulti(null); // I don't want to extract the whole table.
BatchEntity batch = batches.AsQueryable()
.Where(i => i.RegisterID == 3)
.FirstOrDefault(); //Works fine
To query your database using LINQ (which is different from operating on an enumerable collection using the LINQ syntax) you have to use the LinqMetaData provider that comes with LLBLGen in the yourrootnamespace.Linq assembly. Once you add this assembly to your project you can use something like this to create your db query: (from the LLBL documentation)
LinqMetaData metaData = new LinqMetaData();
var q = from c in metaData.Customer
where c.Country=="USA"
select c;
In your example above you are using LINQ syntax to perform a where clause on a collection, but this does not have anything to do with LLBL or executing a query on the database. That is why it will not work with the empty collection, but does work on the filled collection.
Look more into LinqMetaData for the specifics of querying your db using LINQ to LLBLGen.