2009-08-20 6 views
10

J'essaie d'utiliser le conteneur Unity pour faciliter le test unitaire de mes contrôleurs. Mon contrôleur utilise un constructeur qui accepte une interface avec un référentiel. Dans le fichier global.asax, j'instancie UnityContainerFactory et l'enregistre dans le framework MVC, puis enregistre le référentiel et son implémentation. J'ai ajouté l'attribut [Dependency] au paramètre CTOR Repository du contrôleur. Tout semble fonctionner correctement, sauf que, occasionnellement, GetControllerInstance (type controllerType) de l'usine est appelé plus d'une fois et un argument null est passé en tant que controllerType.ASP.NET MVC et Unity 1.2 Container question

Le premier appel à l'usine est toujours correct et le controllerType "ProductsController" est passé en argument. Mais parfois, l'usine est appelée plusieurs fois après que la vue a été affichée avec une valeur nulle pour le contrôleur et je ne sais pas pourquoi. Quand la valeur correcte du type de contrôleur est passée, "Call Stack" a du sens pour moi, mais quand un null est passé, je ne sais pas pourquoi ou qui fait l'appel. Des idées?

Les piles de code et d'appel de l'exemple sont illustrées ci-dessous.

Call Stack est quand fonctionne

TEST.DLL! Test.UnityHelpers.UnityControllerFactory.GetControllerInstance (System.Type controllerType = {Name = "ProduitsController" FullName = "Test.Controllers.ProductsController"}) Ligne 23 C# TEST.DLL! Test._Default.Page_Load (objet expéditeur = {} ASP.default_aspx, System.EventArgs e = {} System.EventArgs) ligne 18 + 0x1a octets C#

Call Stack lorsque NULL est passé au controllerType

Test.DLL! Test.UnityHelpers.UnityControllerFactory.GetControllerInstance (Syst em.Type controllerType = null) Ligne 27 C#

D'abord, je créé un UnityControllerFactory

public class UnityControllerFactory : DefaultControllerFactory 
{ 
    UnityContainer container; 

    public UnityControllerFactory(UnityContainer container) 
    { 
     this.container = container; 
    } 

    protected override IController GetControllerInstance(Type controllerType) 
    { 
     if (controllerType != null) 
     { 
      return container.Resolve(controllerType) as IController; 
     } 
     else 
     { 
      return null; // I never expect to get here, but I do sometimes, the callstack does not show the caller 
     } 
    } 
} 

Ensuite, j'ai ajouté le code suivant le fichier global.asax instancier l'usine de conteneurs

protected void Application_Start() 
    { 
     RegisterRoutes(RouteTable.Routes); 

     // Create Unity Container if needed 
     if (_container == null) 
     { 
      _container = new UnityContainer(); 
     } 

     // Instantiate a new factory 
     IControllerFactory unityControllerFactory = new UnityControllerFactory(_container); 

     // Register it with the MVC framework 
     ControllerBuilder.Current.SetControllerFactory(unityControllerFactory); 

     // Register the SqlProductRepository 
     _container.RegisterType<IProductsRepository, SqlProductRepository> 
      (new ContainerControlledLifetimeManager()); 
    } 

L'application a un contrôleur

public class ProductsController : Controller 
{ 
    public IProductsRepository productsRepository; 

    public ProductsController([Dependency]IProductsRepository productsRepository) 
    { 
     this.productsRepository = productsRepository; 
    } 
} 
+0

Êtes-vous sûr à 100% que cette ligne ne renvoie pas de valeur null: return container.Resolve (controllerType) comme IController; Cela semble peu probable, mais cette distribution pourrait facilement renvoyer une valeur nulle si le type résultant n'était pas un IController ou si l'appel Resolve échouait. –

+0

Salut Anderson, Comme vous pouvez le voir dans la pile des appels, null est transmis. J'ai également arrêté à la ligne en utilisant le débogueur et il est nul avant la distribution. Cet appel est la seule fonction sur la pile à ce moment-là. Ce que je ne comprends pas non plus. Test.DLL! Test.UnityHelpers.UnityControllerFactory.GetControllerInstance (System.Type controllerType = null) Ligne 27 C# – Rick

+0

C'est ce que je pensais voir dans votre pile, mais je voulais vérifier mes hypothèses pour être sûr. –

Répondre

9

Ceci est probablement dû ype ne correspond pas à un contrôleur dans vos itinéraires. (images, par exemple). Cela se produit plus souvent lorsque vous déboguez localement avec Cassini dans mon expérience, car Cassini autorise toutes les demandes à router via ASP.NET tandis que dans IIS, beaucoup de requêtes sont traitées par IIS pour vous. Ce serait aussi pourquoi vous ne voyez pas votre code dans la pile pour cette requête. Si vous désactivez l'option "Just My Code" dans Visual Studio, vous pouvez parfois obtenir un meilleur indice sur ces choses.

Ce n'est pas la seule raison pour laquelle cela peut arriver, mais c'est courant.

La meilleure chose à faire serait de permettre à la méthode de base de gérer la requête dans ces situations. C'est généralement une simple demande de fichier et cela ne devrait pas avoir d'impact sur vous.

chose à faire Simplest serait à la porte comme ceci:

if (controllerType != null) 
    { 
     return container.Resolve(controllerType) as IController; 
    } 
    else 
    { 
     return base.GetControllerInstance(requestContext, controllerType); 
    } 

Cela devrait le faire.

Pour voir ce que la demande est pour, vous pourriez être en mesure de vérifier HttpContext.Current.Request pour voir quel fichier n'est pas dans votre itinéraire. Souvent, ce n'est pas quelque chose que vous voulez contrôler, mais vous vous sentirez mieux de savoir quelle est l'origine de la demande.

+0

Merci Pour la réponse et les conseils ..... – Rick

+2

J'ai pris votre suggestion et regardé le HttpContext.Current.Request et remarqué que cherchait favicon.ico. Cela explique pourquoi cela a fonctionné parfois et pas d'autres. Quand une instance existante du navigateur était ouverte, elle n'a pas essayé de trouver favicon.ico. – Rick

+0

Hé, bon à savoir et content de pouvoir aider. –