webForumDet fria alternativet

Lazy load

10 svar · 698 visningar · startad av doggelito

doggelitoMedlem sedan juni 20003 076 inlägg
#1

Har stött på lazy load problem med nhibernate och behöver er hjälp! :)
Får: failed to lazily initialize a collection, no session or session was closed

Har en klass som hämtar upp en användare, metoden ser ut:

private static Entities.User _currentUser;
public static Entities.User CurrentUser
{
            get
            {
                if (_currentUser == null)
                {
                    BLUser blUser = new BLUser();
                    _currentUser = blUser.GetByUserName(CurrentUserName);
                }
                return _currentUser;
            }
}

Tidigare hade jag inte det privata fältet _currentUser utan jag hämtade från databasen varje gång vilket resulterade i många databasanrop (om metoden används flera gånger), jag fick dock inga fel på lazy loads då!

Nu med fältet tillagt så får jag lazy load error på en collection som användaren innehåller, Roles.

Utdrag ur mappningsfilen:

	<class name="Entities.User, Entities" table="Users">
		<id name="ID" column="UserID" type="System.Guid" unsaved-value="00000000-0000-0000-0000-000000000000">
			<generator class="guid" />
		</id>
    <property name="UserName" column="UserName" type="String"/>
    <bag name="Roles" lazy="true" cascade="delete" generic="true" table="UsersRoles">
      <key column="UserID"/>
      <many-to-many class="Entities.UserRole, Entities" column="RoleID"/>
    </bag>		
	</class>

Rollerna vill jag ha lazy eftersom de inte används så ofta.
Jag har läst en del om lazy load men hittar inga bra exempel på lösningar!
Hur kommer jag undan felet?

erkaMedlem sedan dec. 19996 522 inlägg
#2

Lazy load fungerar via en proxy, stänger du din session för tidigt? Du bör nog se över din sessionshantering, vilket sessionsmönster kör du? Session per request? Session per conversion?

Du försöker inte köra det under medium trust, lazy-load i nhibernate fungerar inte då out of the box.

NickemannenMedlem sedan aug. 20003 575 inlägg
#3

Hmm, är det en ASP.NET application? isf tycker jag currentuser som static ser farlig ut.
Loada current user tidigt och lägg i HttpContext :).
Tror egentligen inte du förlorar något direkt på att ladda rollerna också? eller innehåller de i sin tur flera olika objekt som måste laddas?

Sedan kan du också lagra din Session i httpcontext som initieras när anropet kommer och förstörs när anropet avslutas finns massor av kod för det att hitta.

doggelitoMedlem sedan juni 20003 076 inlägg
#4

erka skrev:

Lazy load fungerar via en proxy, stänger du din session för tidigt?

Session stängs i application_endrequest vilket väl bör vara ok?!

erka skrev:

Session per request? Session per conversion?

Hmm, jag vet inte vad det kallas. Använder denna hjälpklass:

public sealed class NHibernateHelper
    {
        private const string CurrentSessionKey = "nhibernate.current_session";
        private static readonly ISessionFactory sessionFactory;

        static NHibernateHelper()
        {
            //log4net.Config.XmlConfigurator.Configure();

            NHibernate.Cfg.Environment.UseReflectionOptimizer = true;
            sessionFactory = new NHibernate.Cfg.Configuration().Configure().BuildSessionFactory();
        }

        public static ISession GetCurrentSession()
        {
            HttpContext context = HttpContext.Current;
            ISession currentSession = context.Items[CurrentSessionKey] as ISession;

            if (currentSession == null)
            {
                currentSession = sessionFactory.OpenSession();
                context.Items[CurrentSessionKey] = currentSession;
            }

            return currentSession;
        }

        public static void CloseSession()
        {
            HttpContext context = HttpContext.Current;
            ISession currentSession = context.Items[CurrentSessionKey] as ISession;

            if (currentSession == null)
            {
                return;
            }

            currentSession.Close();
            context.Items.Remove(CurrentSessionKey);

        }

        public static void CloseSessionFactory()
        {
            if (sessionFactory != null)
            {
                sessionFactory.Close();
            }
        }
    }

Och sen stänger jag som sagt via en httpmodul i app endrequest.

erka skrev:

Du försöker inte köra det under medium trust, lazy-load i nhibernate fungerar inte då out of the box.

Jag kör på binero. De använder en modifierad medium trust config som bla. tillåter reflection. Jag har fått för mig att det ska fungera på binero men jag kan ha fel!?

Nickemannen skrev:

Hmm, är det en ASP.NET application? isf tycker jag currentuser som static ser farlig ut.
Loada current user tidigt och lägg i HttpContext

Japp, asp.net app.
CurrentUser hämtas via CurrentUserName som ser ut så här:

public static string CurrentUserName
        {
            get
            {
                if (HttpContext.Current.User != null)
                    return HttpContext.Current.User.Identity.Name;

                return string.Empty;
            }
        }

Det borde väl vara safe att göra så?

Nickemannen skrev:

Tror egentligen inte du förlorar något direkt på att ladda rollerna också? eller innehåller de i sin tur flera olika objekt som måste laddas?

Ja, rollerna innehåller i sin tur ett par collections. CurrentUser innehåller även andra collections förutom rollerna så jag måste lista ut detta ska fungera. Eftersom jag tidigare inte sparat undan CurrentUser så är det typ så jag laddat allt tidigare, dvs. helt utan lazy eftersom CurrentUser hämtats om från databasen när den behövts vilket gjort appen tokseg!

Nickemannen skrev:

Sedan kan du också lagra din Session i httpcontext som initieras när anropet kommer och förstörs när anropet avslutas finns massor av kod för det att hitta.

Det är exakt vad jag gör också! :)

En dum fråga bara:
Felet uppstår inte pga. att jag hämtar CurrentUser static och lagrar den i ett static fält?
Kan det statiska fälet få problem att hämta en collection lazy då?

NickemannenMedlem sedan aug. 20003 575 inlägg
#5

Dublettpostning

NickemannenMedlem sedan aug. 20003 575 inlägg
#6

Det kan mycket väl vara så, det static fältet kan nog leda till många problem, dock så är det problem värre än så tror jag.

            get
            {
                if (_currentUser == null)
                {
                    BLUser blUser = new BLUser();
                    _currentUser = blUser.GetByUserName(CurrentUserName);
                }
                return _currentUser;
            }

Om du kollar på den koden så kommer den under hela applikationens livstid bara gå in i if-satsen en gång. Och det är ju egentligen när första användaren anropar din webbsida, sedan kommer den alltid att vara satt till den personen, isf så röstar jag för att du lägger även den i httpcontext dvs istället för static fält så.

get
{
    if(HttpContext.Items["currentUser"] == null)
         HttpContext.Items["currentUser"] = ......

    return HttpContext.Items["currentUser"] as User
}

Sedan angående lazyload, så försöker jag att aldrig använda mig av det, uppstår det problem så som i ditt fall så skulle jag nog mer kika efter collections att ta bort.. dom kanske innehåller för mkt?

doggelitoMedlem sedan juni 20003 076 inlägg
#7

Nickemannen skrev:

Om du kollar på den koden så kommer den under hela applikationens livstid bara gå in i if-satsen en gång. Och det är ju egentligen när första användaren anropar din webbsida, sedan kommer den alltid att vara satt till den personen, isf så röstar jag för att du lägger även den i httpcontext dvs istället för static fält så.

Aha, är det på det viset! :OO
Det kanske inte är någon huvudregel men man skulle alltså kunna säga att man inte ska använda parameterlösa statiska metoder då den kan returnera fel data, korrekt?

NickemannenMedlem sedan aug. 20003 575 inlägg
#8

doggelito skrev:

Nickemannen skrev:

Om du kollar på den koden så kommer den under hela applikationens livstid bara gå in i if-satsen en gång. Och det är ju egentligen när första användaren anropar din webbsida, sedan kommer den alltid att vara satt till den personen, isf så röstar jag för att du lägger även den i httpcontext dvs istället för static fält så.

Aha, är det på det viset! :OO
Det kanske inte är någon huvudregel men man skulle alltså kunna säga att man inte ska använda parameterlösa statiska metoder då den kan returnera fel data, korrekt?

Statiska metoder fungerar utmärkt, men inte statiska fält.

GladhMedlem sedan maj 20012 812 inlägg
#9

doggelito skrev:

En dum fråga bara:
Felet uppstår inte pga. att jag hämtar CurrentUser static och lagrar den i ett static fält?
Kan det statiska fälet få problem att hämta en collection lazy då?

Kan för lite om NHibernate och LazyLoad för att svara på det, men problemet med att ha statiska variabler är att ALLA användare av ditt program (i ditt fall websida) kommer att dela på detta fält. Vilket betyder att ALLA besökare på din sida kommer ha samma User-information när den väl är satt, vilket kanske inte är vad du har önskat dig.

Om det är så att du vill spara undan denna användare i någon sorts cache för att det är prestandakrävande att alltid hämta upp användaren igen, så finns det bättre lösningar för det än ett statisk fält. HttpContext eller HttpCache är betydligt bättre lösningar, eller så skapar du ett statiskt fält som innehåller en Dictonary, där du kan ge en nyckel till ett värde och då kan du skapa en nyckel som ser ut så här: user_[DITT_SESSIONS_ID] och lägga in användar informationen där, men då måste du själv hanterar trådsäkerheten mot detta Dictonaryobjekt, vilket gör att det är bättre att använda ett redan färdigt :)

- M

doggelitoMedlem sedan juni 20003 076 inlägg
#10

Gladh skrev:

Kan för lite om NHibernate och LazyLoad för att svara på det, men problemet med att ha statiska variabler är att ALLA användare av ditt program (i ditt fall websida) kommer att dela på detta fält. Vilket betyder att ALLA besökare på din sida kommer ha samma User-information när den väl är satt, vilket kanske inte är vad du har önskat dig.

Det är ABSOLUT inte vad jag tänkt mig! :)

Jag har stuvat om det hela nu och spar undan objektet i httpcontext istället och det verkar fungera bra!
Fast det löser inte mitt lazy problem. Ska jag spara i httpcontext så måste jag hämta alla collections från start vilket är synd. Alternativet är då som Nickemannen nämde att ta bort samlingarna från objektet vilket inte heller känns superbra! :l

NickemannenMedlem sedan aug. 20003 575 inlägg
#11

Ditt problem är att session kopplingen har blivit stängd innan du anropar en collection. Men som du beskrev det för mig så stängs den först när End_Request eventet kasstas på HttpModule antar jag? Kan det vara så att den anropas efter det eventet?

Min tanke var att du vid användandet av det statiska fältet satte usern en gång som var kopplad till en session som stängdes ner när end_request utfördes och sedan nästa gång så hade den fortfarande en referens till din förra användaren som hade en referens till din förra session som stängdes. Men om du fortfarande får samma problem så är det något skumt..

135 ms totalt · 3 externa anrop · v20260731065814-full.fb544a5a
0 ms — hämta forumlista (cache)
0 ms — hämta statistik (cache)
132 ms — hämta tråd, inlägg och bilagor (db)