NHibernate - LAZY LOADING PROBLEM -Initializing[]-Could not initialize proxy - no Session."}
Asked Answered
S

6

9

Hi I use Fluent NHibernate and I am little confusing with Lazy Loading.

  1. I queried object in database
  2. modified object properties
  3. update database with this object

Here is code:

public class Credentials
{
    public virtual int Id { get; set; }
    public virtual string Nick { get; set; }
    public virtual string Password { get; set; }
}

public class CredentialsMap : ClassMap<Credentials>
{
    public CredentialsMap()
    {
        Id(x => x.Id);
        Map(x => x.Nick).Column("NICK");
        Map(x => x.Password).Column("PASSWORD");
        Table("R_CREDENTAILS");
    }
}

public class Status
{
    public virtual int Id { get; set; }
    public virtual string Msg { get; set; }
    public virtual DateTime AddTime { get; set; }
}

public class StatusMap : ClassMap<Status>
{
    public StatusMap()
    {
        Id(x => x.Id);
        Map(x => x.Msg).Column("MESSAGE");
        Map(x => x.AddTime).Column("ADD_TIME");
        Table("R_STATUS");
    }
}

public class Account
{
    public virtual int Id { get; set; }
    public virtual string SelfNick { get; set; }
    public virtual Credentials Credentials { get; set; }
    public virtual Status Status { get; set; }
}

public class AccountMap : ClassMap<Account>
{
    public AccountMap()
    {
        Id(x => x.Id);
        Map(x => x.SelfNick).Column("SELF_NICK");
        References(x => x.Credentials)
            .Column("CREDENTIALS_ID")
            .ForeignKey();
        References(x => x.Status)
            .Column("STATUS_ID")
            .ForeignKey();
        Table("R_ACCOUNTS");
    }
}

NHibernate configuration class:

public class NHiberanteHelper
{
    private static ISessionFactory _sessionFactory;

    private static ISessionFactory SessionFactory
    {
        get
        {
            if (_sessionFactory == null)
                InitializeSessionFactory();

            return _sessionFactory;
        }
    }

    private static void InitializeSessionFactory()
    {
        _sessionFactory = Fluently.Configure()
            .Database(MsSqlConfiguration.MsSql2008
                          .ConnectionString(
                               @"Server=TEST\SQLEXPRESS;Database=SimpleNHibernate;Trusted_Connection=True;").
                               ShowSql()
                              )
            .Mappings(m => m.FluentMappings.AddFromAssemblyOf<Account>().Conventions.Add( DefaultCascade.All()))
            .ExposeConfiguration(cfg => new SchemaUpdate(cfg).Execute(true, true))
            .BuildSessionFactory();
    }

    public static ISession OpenSession()
    {
        return SessionFactory.OpenSession();
    }
}

Here is usage:

    public class LoginDbHelper
    {
        public static Account GetAccount(string nick)
        {
            using (var session = NHiberanteHelper.OpenSession())
            {
                var account = (session.QueryOver<Account>()
                    .JoinQueryOver<Credentials>(a => a.Credentials)
                    .Where(c => c.Nick == nick));

                if (account != null)
                    return account.SingleOrDefault();

                return null;
            }
        }

        public static void SaveOrUpdateAccount(Account account)
        {
            using (var session = NHiberanteHelper.OpenSession())
            {
                using (var trans = session.BeginTransaction())
                {
                    session.SaveOrUpdate(account);
                    trans.Commit();
                }
            }
        }
   }

Problem code:

var actualAccount = LoginDbHelper.GetAccount(nick);

//modify
actualAccount.Status.Msg = "New status 2";
actualAccount.Status.AddTime = DateTime.Now;


LoginDbHelper.SaveOrUpdateAccount(account);

I get this error:

{"Initializing[NHibernateSample1.Status#1]-Could not initialize proxy - no Session."}

StackTrace:

 at NHibernate.Proxy.AbstractLazyInitializer.Initialize() in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Proxy\AbstractLazyInitializer.cs:line 113
   at NHibernate.Proxy.AbstractLazyInitializer.GetImplementation() in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Proxy\AbstractLazyInitializer.cs:line 191
   at NHibernate.ByteCode.Castle.LazyInitializer.Intercept(IInvocation invocation) in d:\CSharp\NH\NH\nhibernate\src\NHibernate.ByteCode.Castle\LazyInitializer.cs:line 61
   at Castle.DynamicProxy.AbstractInvocation.Proceed()
   at Castle.Proxies.StatusProxy.set_Msg(String value)
   at NHibernateSample1.Program.Main(String[] args) in E:\C# PROJECTS\Samples\SimpleNHibernateClient\NHibernateSample1\Program.cs:line 215
   at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
   at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
   at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()

I google it and I think It cause by Lazy Loading because in method GetAccount I close SESSION. It is my first attempt with NHibernate so HOW CAN SOLVE THIS PROBLEM CORRECTLY? It is possible disable LAZY LOADING if YES how to do it?

Spoliate answered 28/9, 2011 at 13:49 Comment(0)
H
13

You are correct. Because the NHibernate Session is closed in your GetAccount method (it is only open in the scope of the using statement), you cannot load additional objects outside of this method. There are 2 potential fixes:

  1. Create the session at the operation level (i.e. in the method containing the problem code), then use this session in the get & save methods. You can use the session by passing it in as a parameter to the methods.
  2. Change the object to not use lazy loading. You can do this by adding .Not.LazyLoad() to the Status object in your fluent mapping.
Hyonhyoscine answered 28/9, 2011 at 13:56 Comment(0)
M
4

I find the easiest way to turn off lazy loading is to add a DefaultLazy convention, i.e.:

.Conventions.Add( DefaultCascade.All(), DefaultLazy.Never() )

Note that turning lazy loading on (DefaultLazy.Always()) can really increase performance, depending on your application.

The downside is, you always have to have a session open before you can lazy load the rest of the data in an entity. Session management to support lazy loading is one of the big pain points with NHibernate, in my experience.

Mahone answered 28/9, 2011 at 22:45 Comment(2)
I am feeling big PITA Session management to support lazy loading is one of the big pain points with NHibernate, in my experience. totally agree with you. feeling like why did i choose nhibernate, should have gone with EFElectrolyze
EF was quite immature when we started our project. We mainly used Fluent Nhibernate because it did auto schema mapping - at the time, nothing else did. Even with the session management hassle, it was still a net win, IMO.Mahone
C
0

You open and close session in LoginDbHelper.GetAccount(...) method.
Try to create and open session outside of method and pass it as method param, for example:

    // method
    public static Account GetAccount(string nick, ISession session) 
    {   
     var account = (session.QueryOver<Account>().JoinQueryOver<Credentials>(a => a.Credentials).Where(c => c.Nick == nick));   

           if (account != null)                      
            return account.SingleOrDefault();                    
    return null;  
    }

// usage
    var actualAccount = LoginDbHelper.GetAccount(nick);   
    actualAccount.Status.AddTime = DateTime.Now;   
    using (var session = NHiberanteHelper.OpenSession())  

    LoginDbHelper.SaveOrUpdateAccount(account, session); 
Christine answered 28/9, 2011 at 14:1 Comment(1)
And, yeah, solution point #2 from Ryan Gross is applicable as well. What's more, propablt it's better in your case just to turn lazy loading off.Christine
S
0

As a workaround you can replace this code:

if (account != null)
    return account.SingleOrDefault();

With this code:

if (account != null)
{
    var returnValue = account.SingleOrDefault();
    if (returnValue != null)
    {
        returnValue.Status.Msg = returnValue.Status.Msg;//Cause to load the lazy object now
    }
    return returnValue;
}
Shavonneshaw answered 28/10, 2020 at 15:58 Comment(0)
R
0

This can also happen if your session is perfectly open but you have called Session.Clear(). Don't do that.

Ragsdale answered 7/4, 2021 at 17:30 Comment(0)
L
0

If you are using this in combination with ASP.Net MVC or ASPCORE, the issue might be because the Json serializer is trying to retrieve the data from an IEnumerable that you are getting via your session. Because the serializer executes after you exit your action method and your controller is disposed of (along with the NH session), it cannot access the lazy-loaded navigation properties.

The simple solution is to add .ToList() or .ToArray() to your LINQ query within the action method.

Lovettalovich answered 28/6, 2022 at 11:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.