3
votes

I am considered new to NHibernate. I want my Article entity class to have two lazy properties. One of them is User other is Content. My entity and mapping is like

<class name="Article" table="TBL_ARTICLE">
    <id name="Id" column="ART_ID">
        <generator class="native" />
    </id>
    <property name="UserId" column="USR_ID" not-null="true" />
    <many-to-one name="User" column="USR_ID" insert="false" update="false" />
    <property name="Content" column="ART_CONTENT" not-null="true" lazy="true" />
</class>

public class Article
{
    public virtual long Id { get; set; }
    public virtual long UserId { get; set; }
    public virtual User User { get; set; }
    public virtual string Content { get; set; }
}

I select it like

using(ISession session = sessionFactory.OpenSession()) {
     return session.Query<Article>()
                   .SingleOrDefault(a => a.Id == id);
}

It works fine. When I try to access User or Content property outside the using block I get some kind of lazy loading exception. This is what I've expected.

For some cases I like to fetch User data eagerly. I select it like:

using(ISession session = sessionFactory.OpenSession()) {
     return session.Query<Article>()
                   .Fetch(a => a.User)
                   .SingleOrDefault(a => a.Id == id);
}

It still works fine. When I try to access User property outside the using block I can have its property values but when try to access Content property I stil get some kind of lazy loading exception and this is still what I've expected.

When I want to fetch content data like

using(ISession session = sessionFactory.OpenSession()) {
     return session.Query<Article>()
                   .Fetch(a => a.Content)
                   .SingleOrDefault(a => a.Id == id);
}

I get an exception:

Invalid join: a.Content
[.SingleOrDefault[Repository.NH.Article](.Fetch[Repository.NH.Article,System.String]
(NHibernate.Linq.NhQueryable`1[Repository.NH.Article], Quote((a, ) => (a.Content)), ),
Quote((a, ) => (Equal(a.Id, 1))), )]

I read some blog post about doing this by HQL but I am looking for a solution using Linq Provider.

3

3 Answers

1
votes

The Fetch construct only works on properties that map relationships between objects, so properties that are mapped with "References", "Many-To-One" or "Many-To-Many".

This is why it works as expected with the User property but not against the string Content property.

After further investigation I don't think you can achieve what you want using LINQ syntax but it should be possible using an equivalent HQL statement using the fetch all properties hint:

session.CreateQuery("from Article fetch all properties")
0
votes

You generally use Lazy on Reference Types, e.g. things that cause a Join in the SQL. Since the Content is a string, and is in the exact same level of the object graph, what you are doing doesn't make much sense really. Whats happening is when you try to reference the Objects string property outside of your nhibernate session, its throwing an error. On the other hand, you can't fetch a non-reference type, hence your issue. However, you can do something a bit different. If you need the objects to be pulled out with Lazy content, use a Query within the scope of your nhibernate session, if you want the Content to be pulled out eagerly, use a _session.Get<Article>(id); The Get will load the full object graph from the database.

0
votes

As others have explained, references and lazy scalar properties behave differently, and the LINQ Fetch method is only good for the former.

There are three possible solutions to your problem:

  • Use HQL instead of LINQ, as CSL suggested, when you need to eagerly load lazy properties.
  • Do not use lazy properties, and use a projection when displaying a list instead of fetching full entities.
  • Fix your actual problem, which is the scope of your session. It should be available all the time while you are still using the entities.