开发者

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

开发者 https://www.devze.com 2023-04-09 05:19 出处:网络
Hi I use Fluent NHibernate and I am little confusing with Lazy Loading. I queried object in database modified object properties

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.OpenSessio开发者_Go百科n())
            {
                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?


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.


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.


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); 


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;
}


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


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号