2009-07-19 9 views
44

Il ya quelques jours, j'ai eu ce problème avec le threading ASP.Net. Je voulais avoir un objet singleton par requête web. J'ai vraiment besoin de ceci pour mon unité de travail. Je voulais instancier une unité de travail par requête web afin que la carte d'identité soit valide tout au long de la requête. De cette façon, je pourrais utiliser un IoC pour injecter mon propre IUnitOfWork à mes classes de dépôt de manière transparente, et je pourrais utiliser la même instance pour interroger puis mettre à jour mes entités.Singleton par appel contexte (demande Web) dans l'unité

Depuis que j'utilise Unity, j'ai utilisé par erreur PerThreadLifeTimeManager. J'ai vite réalisé que le modèle de threading ASP.Net ne supporte pas ce que je veux réaliser. Fondamentalement, il utilise un theadpool et recycle les threads, et cela signifie que je reçois un UnitOfWork par fil !! Cependant, ce que je voulais, c'était une unité de travail par requête Web.

Un peu de googling m'a donné this great post. C'était exactement ce que je voulais. sauf pour la partie unité qui était assez facile à réaliser.

Ceci est mon application pour PerCallContextLifeTimeManager pour l'unité:

public class PerCallContextLifeTimeManager : LifetimeManager 
{ 
    private const string Key = "SingletonPerCallContext"; 

    public override object GetValue() 
    { 
     return CallContext.GetData(Key); 
    } 

    public override void SetValue(object newValue) 
    { 
     CallContext.SetData(Key, newValue); 
    } 

    public override void RemoveValue() 
    { 
    } 
} 

Et bien sûr, je l'utiliser pour enregistrer mon unité de travail avec un code similaire à ceci:

unityContainer 
      .RegisterType<IUnitOfWork, MyDataContext>(
      new PerCallContextLifeTimeManager(), 
      new InjectionConstructor()); 

L'espoir, il sauve quelqu'un un peu de temps.

+0

Belle solution. Si vous me le permettez, je recommanderais de renommer ceci à "CallContextLifetimeManager" puisque les demandes Web sont probablement seulement l'une des applications potentielles. –

+0

Certes, j'ai mis à jour le texte et le code pour refléter cela. Merci. –

+0

+1 Très utile. – MrDustpan

Répondre

25

solution Neat, mais chaque instance de LifetimeManager doit utiliser une clé unique plutôt qu'une constante:

private string _key = string.Format("PerCallContextLifeTimeManager_{0}", Guid.NewGuid()); 

Sinon, si vous avez plus d'un objet enregistré avec PerCallContextLifeTimeManager, qu'elles partagent la même clé d'accès CallContext, et vous ne récupérerez pas votre objet attendu.

Il convient également de mettre en œuvre pour assurer removeValue objets sont nettoyés:

public override void RemoveValue() 
{ 
    CallContext.FreeNamedDataSlot(_key); 
} 
+0

-1 CallContext est recréé à chaque requête. La clé n'a pas besoin d'être unique entre différentes instances de votre singleton par requête. –

+8

+1 En fait, cela doit être exactement comme l'a dit Sixeyed. Si vous n'attribuez pas de clés uniques, tous les objets sont enregistrés sous une clé unique et les choses se gâchent. –

+0

-1 Voir la réponse de Steven Robbins et le commentaire de Micah Zoltu à la question: en charge 'CallContext' n'est pas conservé pour une seule requête. –

20

Bien que ce soit bien appeler cette PerCallContextLifeTimeManager, je suis sûr que c'est pas « sûr » pour être considéré comme un ASP.Net LifeTimeManager par demande.

Si ASP.Net fait son thread-échange alors la seule chose prise dans le nouveau fil à travers CallContext est le HttpContext actuel - tout ce que vous stockez dans CallContext sera parti. Cela signifie que, sous une lourde charge, le code ci-dessus pourrait avoir des résultats inattendus - et j'imagine que ce serait vraiment difficile de savoir pourquoi!

La seule façon de le faire est avec HttpContext.Current.Items, ou faire quelque chose comme « sûr »:

public class PerCallContextOrRequestLifeTimeManager : LifetimeManager 
{ 
    private string _key = string.Format("PerCallContextOrRequestLifeTimeManager_{0}", Guid.NewGuid()); 

    public override object GetValue() 
    { 
     if(HttpContext.Current != null) 
     return GetFromHttpContext(); 
     else 
     return GetFromCallContext(); 
    } 

    public override void SetValue(object newValue) 
    { 
     if(HttpContext.Current != null) 
     return SetInHttpContext(); 
     else 
     return SetInCallContext(); 
    } 

    public override void RemoveValue() 
    { 
    } 
} 

Cela signifie évidemment prendre des dépendances sur le système.Web :-(

Beaucoup plus d'informations sur ce site:

http://piers7.blogspot.com/2005/11/threadstatic-callcontext-and_02.html

3

Merci pour votre contribution,

Mais la question la mise en œuvre proposée présente deux inconvénients, dont l'un est un bug grave comme cela a déjà déclaré par Steven Robbins dans his answer et Micah Zoltu in a comment.

  1. contexte d'appel est pas t garanti d'être préservé par asp.net pour une seule requête. Sous charge, il peut passer à un autre, ce qui provoque une interruption de l'implémentation proposée.
  2. Il ne gère pas la libération des dépendances à la fin de la requête.

Actuellement, le package Nuget Unity.Mvc fournit un PerRequestLifetimeManager pour effectuer le travail. N'oubliez pas d'enregistrer son UnityPerRequestHttpModule associé dans le code d'amorçage sinon les dépendances libérées ne seront pas gérées non plus.

En utilisant bootstrapping

DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule)); 

ou web.config system.webServer/modules

<add name="UnityPerRequestHttpModule" type="Microsoft.Practices.Unity.Mvc.UnityPerRequestHttpModule, Microsoft.Practices.Unity.Mvc" preCondition="managedHandler" /> 

Il apparaît sa mise en œuvre actuelle est également adapté pour les formulaires Web. Et cela ne dépend même pas de MVC. Malheureusement, son assemblage le fait, à cause d'autres classes qu'il contient.

Attention, dans le cas où vous utilisez un module http personnalisé en utilisant vos dépendances résolues, ils peuvent être déjà disposés dans le module EndRequest. Cela dépend de l'ordre d'exécution du module.

Questions connexes