[.NET] Context/Sessie object in de middle layer & threading.

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • meraki
  • Registratie: Augustus 2007
  • Laatst online: 15-05 23:19
Sinds ik mijn applicatie volgens het lagenmodel heb opgesplitst, zit ik met een probleem:

In de middle layer heb ik veel functies die dezelfde informatie nodig hebben: UserId, Role en Language.
Omdat ik niet telkens die informatie als parameter aan mijn functies wil doorgeven zat ik te denken aan een soort van context/sessie-object waarin ik User, Role en Language informatie bewaar.

Bij webapplicaties heb je een sessie-object dat per gebruiker kan worden opgevuld. Omdat ik dit object niet kan gebruiken (het zou maar raar zijn om System.Web toe te voegen in de bovenliggende laag) had ik gedacht om een klasse te maken die er als volgt uit ziet:

Assembly: DomainLayer.dll (class library):

C#: DomainContext.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
    public class DomainContext
    {
        // properties
        public int UserId { get; set; }
        public List<Role> Roles { get; set; }
        public string Language { get; set; }

        /// property to get and set the current domain context
        public static DomainContext Current { get; set; }

        // constructor
        internal DomainContext() { }
    }


Assembly: WebApplication.dll (ASP.NET MVC applicatie):

Aan de hand van een cookie vul ik mijn DomainContext op bij het begin van een request:


C#: CustomHttpModule.cs
1
2
3
4
5
6
7
8
9
        private void BeginRequest(Object source, EventArgs e)
        {
            HttpApplication application = (HttpApplication)source;
            HttpContext context = application.Context;

            // get the domain context out of the cache via the cookie-id
            if (context.Request.Cookies["id"] != null)
                    DomainContext.Current = MemcachedHelper.GetDomainContext(context.Request.Cookies["id"].Value);
        }


En ik maak 'em weer leeg bij het het einde van een request:


C#: CustomHttpModule.cs
1
2
3
4
5
        private void EndRequest(Object source, EventArgs e)
        {
            // clear the domain context
            DomainContext.Current = null;
        }


Het probleem
  • Request 1 is een tijdsintensieve aanvraag (bv: database & netwerk aanvragen)
  • Request 2 is een simpele request (bv: teruggeven van een tekst)
Request 1 komt binnen --> DomainContext wordt opgevuld maar ondertussen komt ook Request 2 binnen --> DomainContext wordt overschreven en omdat Request 2 snel klaar is wordt de DomainContext direct op null gezet.

Request 1 spreekt hierna nog enkele middle layer functies aan waarbij veelvuldig informatie uit DomainContext wordt uitgelezen -->omdat Request 2 dit object reeds op null heeft gezet krijg ik dus vaak NullReference exceptions.

De vraag

Hoe verhelp ik dit probleem? Ik vermoed dat ik de DomainContext-klasse thread-safe moet maken zodat verschillende requests niet met hetzelfde object zitten te werken? Maar hoe moet ik dit dan gaan gebruiken in mijn middle layer? Ik zou graag gewoon properties van DomainContext.Current blijven gebruiken aldaar...

Alvast bedankt!

Acties:
  • 0 Henk 'm!

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Als je zeker weet dat een request maar op een thread uitgevoerd word zou je er over kunnen denken om een variabele ThreadStatic te maken.

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Acties:
  • 0 Henk 'm!

  • meraki
  • Registratie: Augustus 2007
  • Laatst online: 15-05 23:19
Woy schreef op zondag 02 mei 2010 @ 14:29:
Als je zeker weet dat een request maar op een thread uitgevoerd word zou je er over kunnen denken om een variabele ThreadStatic te maken.
Ik denk dat ik beter niet over threading was begonnen daar ik niet zeker weet of dit het probleem zal verhelpen.

Wat ik eigenlijk wil is een context object in de middle layer (als dit tenminste kan in een class library). Zoiets als OperationContext.Current in WCF of Session in ASP.NET dus.

Mijn kennis is nog te beperkt dus als iemand wil uitleggen hoe dit geïmplementeerd moet worden, alvast veel dank!

Acties:
  • 0 Henk 'm!

Verwijderd

op deze manier kan je alle gebruiker contexten bijhouden en opvragen:

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    public class DomainContext
    {
        public int UserId { get; set; }
        public List<Role> Roles { get; set; }
        public string Language { get; set; }

        private static Dictionary<string, DomainContext> userContexts = new Dictionary<string, DomainContext>();

        public static DomainContext GetUserContext(string userId)
        {
            lock (userContexts)
            {
                return userContexts[userId];
            }
        }

        public static void SetUserContext(string userId, DomainContext context)
        {   
            lock (userContexts)
            {
                userContexts[userId] = context;
            }
        }

    }


Wil je het goed doen maak dan een IStateGateway die je dan gebruikt vanuit je code zodat je de implementatie voor het opslaan van state los koppelt van de functionaliteit. Vervolgens kan je de implementatie (sessions state of static dictionary) injecteren met een IoC container.

Acties:
  • 0 Henk 'm!

  • Sebazzz
  • Registratie: September 2006
  • Laatst online: 16-09 15:42

Sebazzz

3dp

Je kan alleen beter op de readonly object locken, zo weet je zeker dat ie nooit wijzigt ;)

[Te koop: 3D printers] [Website] Agile tools: [Return: retrospectives] [Pokertime: planning poker]


Acties:
  • 0 Henk 'm!

Verwijderd

Schoot me dit nog te binnen. Om de domaincontext state van httpmodule naar controller te krijgen kan je de HttpContext.Items[] dictionary gebruiken. Deze state wordt alleen tijdens een uniek httprequest bewaard en niet gedeeld met evt andere threads / requests.

Echter een betere oplossing zou zijn om gebruik te maken van een mvc actionfilter ipv een httpmodule. Hierbij kan je domaincontext object makkelijker aan je controller doorgeven. Dit is ook makkelijker bij het unittesten van je controllers trouwen.

nog iets, "MemcachedHelper" zou een code-smell kunnen zijn, misschien dat de methods die je hier gebruikt beter bij een andere klasse horen?

Acties:
  • 0 Henk 'm!

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
T0mS schreef op zondag 02 mei 2010 @ 15:07:
[...]


Ik denk dat ik beter niet over threading was begonnen daar ik niet zeker weet of dit het probleem zal verhelpen.

Wat ik eigenlijk wil is een context object in de middle layer (als dit tenminste kan in een class library). Zoiets als OperationContext.Current in WCF of Session in ASP.NET dus.

Mijn kennis is nog te beperkt dus als iemand wil uitleggen hoe dit geïmplementeerd moet worden, alvast veel dank!
Als je nu wat beter gelezen had, en opgezocht had waar ik het over had, dan had je ook gezien dat OperationContext.Current d.m.v. ThreadStatic is geïmplementeerd.

Als je je overigens in het vervolg vaker afvraagt hoe iets geïmplementeerd is, dan kun je d.m.v. reflector snel even kijken hoe dat gedaan is.
Verwijderd schreef op woensdag 05 mei 2010 @ 22:13:
op deze manier kan je alle gebruiker contexten bijhouden en opvragen:
Het nadeel daarvan is dat je nog steeds moet weten welke user er is ingelogd. Het idee van de TS was juist dat hij gewoon eenvoudig de context overal op kan vragen zonder overal dat object mee te geven. Als je toch al de userid mee moet geven, dan kun je net zo goed het hele Context object meegegegven.

Op de een of andere manier zal je toch moeten weten in welke context je zit, en een manier daarvoor is het op Thread nivo af te handelen, en dat is iets wat je eenvoudig met ThreadStatic kunt doen.

Overigens kun je inderdaad beter een apart readonly object te locken, dat voorkomt problemen, zeker als er later wat aan de code wijzigt.

Ik neem meestal gewoon een SyncRoot object op als volgt
C#:
1
private static readonly object SyncRoot = new object();

Natuurlijk non-static als je niet in de static context wil locken. Op die manier is het ook duidelijk dat je op dat object moet locken. Het kan natuurlijk ook zijn dat je meerdere onafhankelijke locks nodig hebt, maar in dat geval is het ook slim om te kijken of er geen andere faciliteiten voor die vorm van synchronisatie zijn. ( ReaderWriterLock/WaitHandle/Mutex etc. )

[ Voor 3% gewijzigd door Woy op 06-05-2010 09:08 ]

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Acties:
  • 0 Henk 'm!

  • SiErRa
  • Registratie: Februari 2000
  • Laatst online: 16-09 13:43
De data die overal nodig hebt is nogal user gerelateerd, misschien dat je een Principal object wil gebruiken. Dit heeft standaard support voor rollen waardoor je op je methodes het PrincipalPermission attribuut kan gebruiken om je autorisaties te controleren. Waarschijnlijk zul je een eigen implementatie moeten maken om ook UserId en Language te onthouden. Dit object kan je aan een thread koppelen.

Beetje oud artikel maar dit is het idee: http://msdn.microsoft.com/en-us/library/ff649210.aspx

Acties:
  • 0 Henk 'm!

Verwijderd

Zelf een IPrincipal en IIdentity implementeren i.c.m. forms authenticaition +1
Pagina: 1