IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

ASP.NET MVC Discussion :

Durée de vie du context


Sujet :

ASP.NET MVC

  1. #1
    Membre habitué Avatar de Colbix
    Profil pro
    Développeur Web
    Inscrit en
    Juin 2006
    Messages
    266
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Juin 2006
    Messages : 266
    Points : 150
    Points
    150
    Par défaut Durée de vie du context
    Rebonjour à tous ,

    J'ai de nouveau un problème. Pour commencer, voici mon code :

    Study Controller (Couche GUI) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    ViewResult Index(string search)
            {
                var studies = StudyManager.SelectAll();
                return View(studies);
            }
    Study Manager (Couche BLL) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    public static List<Study> SelectAll()
            {
                return StudyRep.Select();
            }
    Study Repository (Couche DAL) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    public static ICollection<Study> Select()
            {
                using (PrismaDAL dal = new PrismaDAL())
                {
                    return dal.Studies.ToList();
                }
            }
    On voit que le Repository des Studies se crée un contexte pour charger la liste complète des studies.
    Le souci est que dans ma vue, j'aimerai accéder à un sous objet (une Phase) de chaque étude de la liste :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    @if (Model.Phase != null)
    {
        @Html.ActionLink(Model.Phase.Label, "Details/" + Model.Phase.Id.ToString(), "Codelist")
    }
    Au moment ou ma vue est exécutée, le contexte est déjà détruit et le lazy-loading est donc impossible.

    Ma question :
    Comment faire pour que mon sous-objet soit accessible ? Comment avoir un contexte qui "vit" assez longtemps pour permettre le lazy-loading dans la vue.

    Je me suis renseigné avant de poster évidemment, voici les solutions possibles :
    - Créer le context dans le controller, mais la division en couche n'est pas respectée.
    - Une autre option (qui est plus conseillée) est d'avoir un contexte par requete. En chipotant dans le global.asax, ca semble possible, mais je n'y arrive pas... Comment faire ?

    A+ et merci bcp
    Problème résolu ? N'oubliez pas le bouton ainsi que le "Pertinent". Ça fait du bien au forum.

  2. #2
    Rédacteur
    Avatar de Nathanael Marchand
    Homme Profil pro
    Expert .Net So@t
    Inscrit en
    Octobre 2008
    Messages
    3 615
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Expert .Net So@t
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2008
    Messages : 3 615
    Points : 8 080
    Points
    8 080
    Par défaut
    Le Eager Loading est préférable
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    public static ICollection<Study> Select()
    {
        using (PrismaDAL dal = new PrismaDAL())
        {
            return dal.Studies.Include("Phase").ToList();
         }
    }

  3. #3
    Membre habitué Avatar de Colbix
    Profil pro
    Développeur Web
    Inscrit en
    Juin 2006
    Messages
    266
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Juin 2006
    Messages : 266
    Points : 150
    Points
    150
    Par défaut
    Salut,

    Merci pour ta réponse . Par contre ta solution implique que la couche GUI connaisse la couche DAL. La division en couche n'est donc pas respectée.

    Normalement, la couche GUI ne devrait appeler que la couche BLL :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    GUI > BLL > DAL
       \   |   /
           BO
    Si tu as une autre idée, n'hésite pas .

    A+
    Problème résolu ? N'oubliez pas le bouton ainsi que le "Pertinent". Ça fait du bien au forum.

  4. #4
    Rédacteur
    Avatar de Nathanael Marchand
    Homme Profil pro
    Expert .Net So@t
    Inscrit en
    Octobre 2008
    Messages
    3 615
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Expert .Net So@t
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2008
    Messages : 3 615
    Points : 8 080
    Points
    8 080
    Par défaut
    Euh ben non, c'était une méthode pour ta DAL que je t'ai proposé.

  5. #5
    Membre habitué Avatar de Colbix
    Profil pro
    Développeur Web
    Inscrit en
    Juin 2006
    Messages
    266
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Juin 2006
    Messages : 266
    Points : 150
    Points
    150
    Par défaut
    Salut,

    Oups, j'avais pas vu le " Include("Phase") ".

    Le problème ici, c'est qu'on perd la simplicité d'utilisation du lazy-load. De plus, "Phase" était un exemple, ma structure est beaucoup plus complexe et un objet study est composé d'un grand nombre d'autres objets (dont phase).


    J'ai trouvé un moyen pour que le contexte ne soit détruit qu'à la fin de la requête :
    Global.asax (Couche GUI) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    protected void Application_EndRequest()
            {
                PrismaBLL.initializeContext();
            }
    PrismaBLL (couche BLL) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    public static void initializeContext()
            {
                PrismaDAL.RemoveContext();
            }
    PrismaDAL (Couche DAL) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    public class PrismaDAL : DbContext
        {
            private static PrismaDAL _instance = null;
            public static PrismaDAL Instance
            { 
                get
                {
                    if (_instance == null)
                        _instance = new PrismaDAL();
                    return _instance;
                }
            }
     
            public static void RemoveContext()
            {
                if (_instance != null)
                    _instance.Dispose(true);
                _instance = null;
            }
     
            public DbSet<Codelist> Codelists { get; set; }
            public DbSet<Study> Studies { get; set; }
        }
    En faisant du contexte un singleton réinitialisé à la fin de chaque requête, ca passe parfois, mais dans certains cas, j'ai toujours mon problème :'(.

    Ouiiin ! Que faire ?


    Merci pour ton aide
    Problème résolu ? N'oubliez pas le bouton ainsi que le "Pertinent". Ça fait du bien au forum.

  6. #6
    Rédacteur
    Avatar de Nathanael Marchand
    Homme Profil pro
    Expert .Net So@t
    Inscrit en
    Octobre 2008
    Messages
    3 615
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Expert .Net So@t
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2008
    Messages : 3 615
    Points : 8 080
    Points
    8 080
    Par défaut
    Dans l'idéal c'est effectivement ce qu'il faudrait faire: un contexte par requête. Par contre attention à ton cas: deux requêtes simultanées partageraient le contexte!
    Malhereusement, tu n'as pas trop le choix dans ton cas. Le lazy-loading c'est un truc qui peut être pratique mais qui peut s'avérer finalement très complexe à utiliser correctement et très dangereux sur les perfs (problème des requêtes n+1 par exemple).
    Ce que je conseille/ferais c'est de ne pas présenter les entités EF mais créer un objet de présentation qui ressemble à mon entité et le remplir avec plusieurs requêtes.
    D'une manière générale, le lazy-loading dans l'interface utilisateur n'est pas une bonne idée.

  7. #7
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Points : 39 749
    Points
    39 749
    Par défaut
    Citation Envoyé par Nathanael Marchand Voir le message
    Par contre attention à ton cas: deux requêtes simultanées partageraient le contexte!
    Effectivement... une solution simple serait de rendre ThreadStatic le champ _instance. Comme ça chaque thread (et donc chaque requête simultanée) aurait sa propre instance du DbContext.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    [ThreadStatic]
    private static PrismaDAL _instance = null;

  8. #8
    Membre régulier
    Profil pro
    Inscrit en
    Mai 2007
    Messages
    69
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2007
    Messages : 69
    Points : 82
    Points
    82
    Par défaut
    Déjà pour avoir un faible couplage / une couche anticorruption, il est préferable de passer par le system de DTO entre tes couches. (automapper est ton amis).
    Comme ca le jour au ta besoin de rajouter une couche entre les deux (comme une couche service) ba pas de problème.
    Enfin c'est pas obligé.
    C'est sur le lazy loading est réservé a la class qui utilise ton repository, mais avec ASP MVC 3 tu peu faire de l'IOC dans tes vues, dans ce context (ce n'est pas la bonne maniere de faire si tu ne veux pas de dépendance entity/Gui) tu peu initializer ton repository directement dans le vue, et tappé dans ta DAL uniquement a l'aide de l'interface de ta BLL ITrucMuchRepository (grace a IOC). Par contre la c'est la methode sans DTO.

    IOC pour les vues :
    http://bradwilson.typepad.com/blog/2...pt3-views.html

  9. #9
    Membre habitué Avatar de Colbix
    Profil pro
    Développeur Web
    Inscrit en
    Juin 2006
    Messages
    266
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Juin 2006
    Messages : 266
    Points : 150
    Points
    150
    Par défaut
    Salut à tous,

    Merci pour ces conseils . Je vais donc utiliser les DTOs entre la couche GUI et BLL. Ca demande une bonne refonte du code, mais c'est pour la bonne cause .

    Pour rendre mon signleton thread-safe, j'ai rajouté un lock comme suit :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    26
    27
    28
    29
    30
    31
    public class PrismaDAL : DbContext
        {
            private static PrismaDAL _instance = null;
            static readonly object _instanceLock = new object();
            public static PrismaDAL Instance
            { 
                get
                {
                    lock (_instanceLock)
                    {
                        if (_instance == null)
                            _instance = new PrismaDAL();
                        return _instance;
                    }
                }
            }
     
            public static void RemoveContext()
            {
                if (_instance != null)
                    _instance.Dispose(true);
                _instance = null;
            }
     
            private PrismaDAL()
            {
            }
     
            public DbSet<Codelist> Codelists { get; set; }
            public DbSet<Study> Studies { get; set; }
        }
    Vous en pensez-quoi ? C'est risqué pour un grand nombre d'utilisateur ?

    Merci beaucoup .
    Problème résolu ? N'oubliez pas le bouton ainsi que le "Pertinent". Ça fait du bien au forum.

  10. #10
    Rédacteur
    Avatar de Nathanael Marchand
    Homme Profil pro
    Expert .Net So@t
    Inscrit en
    Octobre 2008
    Messages
    3 615
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Expert .Net So@t
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2008
    Messages : 3 615
    Points : 8 080
    Points
    8 080
    Par défaut
    Ben ca empeche que deux threads distincts crééent en même temps un contexte mais ca mutualise quand même ton instance à tous les threads.

  11. #11
    Membre régulier
    Profil pro
    Inscrit en
    Mai 2007
    Messages
    69
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2007
    Messages : 69
    Points : 82
    Points
    82
    Par défaut
    Ba si tu passe par des DTOs et la methode qui consiste a les chargers dans ta BLL, je te conseil de laisser beton le singleton pour le dbcontext vue que tu na plus de problème.
    Apparement si tu veux pas le regretter un jour faut mieu eviter le singleton de DbContext
    http://www.britishdeveloper.co.uk/20...ts-entity.html
    En plus en ASP.Net la notion de singleton est bien pourri (contrairement a du C++), en effet le singleton est unifier a l'ensemble des visiteur/utilisateur de ton site web.

  12. #12
    Membre habitué Avatar de Colbix
    Profil pro
    Développeur Web
    Inscrit en
    Juin 2006
    Messages
    266
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Juin 2006
    Messages : 266
    Points : 150
    Points
    150
    Par défaut
    Hmm,

    C'est pas super si on espère avoir beaucoup de monde sur le site...
    Comme dit ci-dessus, il faudrait créer un contexte par requête.

    On aurait donc un code semblable à ceci :
    Dans le global.asax :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    protected void Application_BeginRequest()
            {
                _dal = new PrismaDAL();
            }
     
            protected void Application_EndRequest()
            {
                _dal.dispose;
            }
    Et dans mes repositories, comment récupérer ce Contexte tout fraîchement créé ?

    Encore et toujours un grand merci pour votre aide .
    Problème résolu ? N'oubliez pas le bouton ainsi que le "Pertinent". Ça fait du bien au forum.

  13. #13
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Points : 39 749
    Points
    39 749
    Par défaut
    Bah non tu peux pas faire comme ça... l'objet Global est global à l'application (comme son nom l'indique), donc les requêtes simultanées vont se marcher sur les pieds. Mets plutôt ton DbContext dans le contexte de la requête (HttpContext.Current)

  14. #14
    Membre régulier
    Profil pro
    Inscrit en
    Mai 2007
    Messages
    69
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2007
    Messages : 69
    Points : 82
    Points
    82
    Par défaut
    Perso je ferrai pas comme tu fait, mais plutot comme tous les autre fond, moi compris,

    Solution "Standard" casse pied pour les jointures :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class HomeController : Controller
    {
         [Dependency]
         public IPhaseRepository repository {get; set;}
     
     
         public ActionResult Index()
         {
               return View(repository.ToList());
         }
    }
    Solution Proxy/partage du context entre repository:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    public class HomeController : Controller
    {
         [Dependency]
         public IRepository repository {get; set;}
     
     
         public ActionResult Index()
         {
               return View(repository.Phase.ToList());
         }
    }
    Ou dans ta BLL :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class MyBll : IMyBll
    {
         [Dependency]
         public IRepository repository {get; set;}
     
     
         public List<PhaseDto> Index()
         {
               return repository.Phase.ToDto());
         }
    }
    Ici chaque class qui porte le nom repository a sa propre instance de dbcontext, et ne la partage pas avec l'exterieur.

  15. #15
    Membre habitué Avatar de Colbix
    Profil pro
    Développeur Web
    Inscrit en
    Juin 2006
    Messages
    266
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Juin 2006
    Messages : 266
    Points : 150
    Points
    150
    Par défaut
    Citation Envoyé par tomlev Voir le message
    Bah non tu peux pas faire comme ça... l'objet Global est global à l'application (comme son nom l'indique), donc les requêtes simultanées vont se marcher sur les pieds. Mets plutôt ton DbContext dans le contexte de la requête (HttpContext.Current)
    C'est une manière courante de faire ?
    Si oui, ça ressemblerais à quoi en code ?

    Merci
    Problème résolu ? N'oubliez pas le bouton ainsi que le "Pertinent". Ça fait du bien au forum.

  16. #16
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Points : 39 749
    Points
    39 749
    Par défaut
    Citation Envoyé par Colbix Voir le message
    C'est une manière courante de faire ?
    Aucune idée... je fais très peu de développement web, donc je saurais pas dire si c'est une pratique répandue (ni si c'est une bonne pratique d'ailleurs...)

    Citation Envoyé par Colbix Voir le message
    Si oui, ça ressemblerais à quoi en code ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
            protected void Application_BeginRequest()
            {
                HttpContext.Current.Items["dal"] = new PrismaDAL();
            }
     
            protected void Application_EndRequest()
            {
                var dal = (PrismaDAL)HttpContext.Current.Items["DAL"];
                dal.Dispose();
            }

Discussions similaires

  1. Durée de vie d'une session
    Par dbass dans le forum Langage
    Réponses: 8
    Dernier message: 21/03/2006, 19h38
  2. [Cookies] durée de vie de l'objet
    Par ozzmax dans le forum Langage
    Réponses: 13
    Dernier message: 13/01/2006, 21h38
  3. [savoir] durée de vie d'un PC?
    Par afrikha dans le forum Composants
    Réponses: 20
    Dernier message: 24/10/2005, 13h28
  4. [AS2] durée de vie d'une classe (extends movieclip)
    Par ooyeah dans le forum ActionScript 1 & ActionScript 2
    Réponses: 4
    Dernier message: 23/07/2005, 13h33
  5. prob de durée de vie de IDvdGraphBuilder
    Par Chaksss dans le forum DirectX
    Réponses: 11
    Dernier message: 30/12/2004, 16h09

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo