I am trying to find a better way to load the relation than this:
result = session.Get<Author>(id);
Course course = result.Courses.FirstOrDefault();
I can do this with QueryOver API like this:
result = session.QueryOver<Author>()
.Where(item => item.Id == id)
.Fetch(item => item.Courses).Eager
.SingleOrDefault();
I guess it would generate the same SQL but it is too verbose.
Is there a way to do something like below?
session.Fetch(result, author => author.Courses);
Get is driven by mapping. If it really make sense, change your mapping (but I would not do that). There is no runtime switch of constructed mapping.
From my experience, few more select statements during the Get(id) is not an issue... And for N + 1 you've already shown the better solution in your question.
Interesting reading about eagar loading: http://nhforge.org/wikis/howtonh/lazy-loading-eager-loading.aspx
Related
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
If I want to create a DTO from properties on classes, all of which are mapped, can I do this without writing a regular SQL statement?
I've seen a lot of documentation on making the DTO class, as well as using it, but not much about how it gets created. I've seen SQL queries used with the Transformer, but that requires the use of magic strings.
Edit:
Thanks for the comments. I am aware there are multiple methods to retrieve DTOs, but I have been unable to find examples of methods that don't use SQL/HQL. Is there a reference somewhere, or is this one of those sparsely documented areas of NHibernate?
From your latest comment here are some examples of projecting a DTO using QueryOver and Linq
Using QueryOver:-
var schoolList = Session.QueryOver<lmschool>()
.SelectList(i => i
.Select(p => p.Name).WithAlias(() => dto.Name)
.Select(p => p.Lat).WithAlias(() => dto.Lat)
.Select(p => p.Lng).WithAlias(() => dto.Lng)
)
.Where(w => w.Lat != null && w.Lng != null)
.TransformUsing(Transformers.AliasToBean<MarkerDto>())
.List<MarkerDto>();
Using the NH Linq provider:-
var schoolList = (from school in Session.Query<lmschool>()
.Where(w => w.Lat != null && w.Lng != null)
select new LmSchoolMarkerDto {
Name = school.Name,
Lat = school.Lat,
Lng = school.Lng,
}).ToList();
Source is from my blog
Also if you need to see how multiple joins are used the see this blog post.
There are also lots of S.O. posts that should help you, see this search.
You could load all necessary entities and use AutoMapper to map them to DTOs more easily ?
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.
I hope someone can help with this please.
I am trying to query an OLAP Fact table with NHibernate, but am struggling to get it to work. Its seems a simple requirement but I just cant see what the problem could be.
I have a central Fact table with several Dimension tables, one of the Dimensions has a secondary Dimension.
So ERD is. Fact >---1 Factor_Dim >---1 Target_Dim
My NHibernate query is.
facts = session.CreateCriteria(typeof(Fact), "facts")
.CreateAlias("facts.FactorDimension", "factDim", JoinType.InnerJoin)
.CreateAlias("factDim.TargetDimension", "targetDim",JoinType.InnerJoin)
.Add(Restrictions.Eq("targetDim.TargetID", targetId))
.List();
The error is "The multi-part identifier "targetdim2_.TargetID" could not be bound.". The generated SQL does not have the Factor_DIM or Target_DIM tables in the From clause.
Are there any alternative techniques to get this query to work? Id like to stick to this style as opposed to CreateSQLQuery() if possible.
Please help. Thanks.
Linq or QueryOver will be your cleanest solutions. If you are determined to stay with ICriteria you probably would want to wrap each of your entities with a class with common crud methods, it also makes your code access common, so code corrections are done in one place, not over hundres of files or classes.
Theres plenty of projects at http://nhforge.org/wikis/general/open-source-project-ecosystem.aspx which can help you out. I know NhGen ( http://sourceforge.net/projects/nhgen/ ) creates a CRUD class for each entity based on the NHibernate.Burrows GenericDao class with a few CRUD methods. It takes care of all the aliases and joins so queries become as simple as
IMessageDao messageDao = new MessageDao();
// Get All
IList<IMessage> messageList1 dao.FindAll();
// Find using QueryByExample
IList<IMessage> messageList2 = dao.FindByExample(messageDetails, orderBy)).ToList();
// Find using a simple entity query
IList<IMessage> messageList3 = messageDao.Find( new [] { Restrictions.Le(MessageHelper.Columns.Date, dateLastChecked) } );
// Find using a join and a query on said joined entities
IList<IMessage> messageList4 = messageDao.Find
( new []
{
Restrictions.Le(MessageHelper.Columns.Date, dateLastChecked),
Restrictions.Eq(MessageHelper.Columns.IsActive, true))
}, new[]
{
Restrictions.Eq(CategoryHelper.KeyColumns.Rsn, categoryRsn),
Restrictions.Eq(CategoryHelper.Columns.IsActive, true))
}, new []
{
Restrictions.Eq(ChannelHelper.KeyColumns.Rsn, channelRsn),
Restrictions.Eq(ChannelHelper.Columns.IsActive, true))
}
);
Theres plenty of overrides so you can specify your join type or it naturally assumes inner join.
Im trying to return a SimpleQuery list that queries a single table and uses IN.
I can get this to work using
return new List<Jobs>(
ActiveRecordMediator<Jobs>.FindAll(Expression.In("ServiceId", ids))
);
However this is really really really slow. So id like to do something like this
SimpleQuery<Job> query =
new SimpleQuery<Job>(#"from Job as j where ? in (j.ServiceId)", ids);
return new List<Job>(query.Execute());
However I cant get the SimpleQuery to work. I cant find any documentation covering this and was hoping someone out there would be able to help.
Thanks
Have a look at the NHibernate HQL documentation here.
I'm guessing from your code, that you're after a HQL query to return all jobs where the job.ServiceID in a list of ids.
Maybe something along the lines,
IQuery q = s.CreateQuery("from Job as j where j.ServiceId in (:serviceIds)");
q.SetParameterList("serviceIds", ids);
BTW, have you heard of the NHibernate Lambda Extensions project?
Below is an example of the IN query done using the the mentioned library. Might be something interesting to look at as an alternative to using HQL.
DetachedCriteria after =
DetachedCriteria.For<Person>()
.Add(SqlExpression.In<Person>(p => p.Name,
new string[] { "name1", "name2", "name3" }));