2011-09-18 1 views
1

Je n'aime pas afficher des douzaines de lignes de code et j'imagine que la communauté dans son ensemble est intéressée à démêler mon désordre. Dans ce cas, j'ai fait tout ce que je peux penser à rechercher sur Google, tracé par Glimpse, et Firebug/Fiddler, et ce qui me reste est un comportement de travail occasionnel, ce qui est particulièrement ennuyeux à déboguer. Donc, je demande de l'aide.Une route personnalisée non trouvée déclenche une seule fois

Voici l'essentiel: J'ai une série de classes qui gèrent les routes MVC qui ne sont pas trouvées (et produiraient une erreur 404) grâce à @AndrewDavey. J'essaye d'intercepter le 404 et montre le contenu piloté par les données là où il existe. Tout fonctionne jusqu'à ce que je rafraîchisse la page. La requête fonctionne sur la première charge, mais elle ne se déclenche plus après.

Si vous vous ennuyez ou avez une démangeaison, le bloc de code entier est ci-dessous.

Setup va comme ceci:

  • Ajouter WebActivator via NuGet
  • Dans votre dossier AppStart ajouter un fichier cs avec le code ci-dessous
  • Ajouter une chaîne de connexion "PageContext" à votre web.config
  • Exécutez l'application, l'écran par défaut MVC apparaît
  • maintenant, ajoutez «/abc » à la fin de l'URL (c.-à-http://localhost/abc)
  • Une vue cshtml, stockée dans la base de données, s'affichera.
  • Modifiez le balisage de la vue dans la base de données et rechargez la page. Notez qu'il n'y a pas de modification dans votre navigateur.

le/abc itinéraire suppose que vous avez un enregistrement dans la base de données avec les éléments suivants

  • chemin: "~/abc/Index.cshtml"

  • Vue: "@{ Layout = null;}<!doctype html><html><head><title>abc</title></head><body><h2>About</h2></body></html>"

Je ne sais pas pourquoi la première requête fonctionne s et les demandes suivantes ne touchent pas les points de rupture et servent du contenu périmé.

Mes soupçons sont:

  • Certains voodoo avec le VirtualFile
  • Quelque chose en cache
  • Un gestionnaire misconfigured

Merci pour l'aide - voici le code ((mais où?) comme je honte honteusement ma queue pour poster autant de code).

using System; 
using System.Collections; 
using System.Collections.Generic; 
using System.Data.Entity; 
using System.IO; 
using System.Linq; 
using System.Text; 
using System.Web; 
using System.Web.Caching; 
using System.Web.Hosting; 
using System.Web.Mvc; 
using System.Web.Routing; 
using System.Web.SessionState; 
using Microsoft.Web.Infrastructure.DynamicModuleHelper; 
using SomeCms; 

[assembly: WebActivator.PreApplicationStartMethod(typeof(Sample.Web.App_Start.cms), "PreStart")] 

namespace Sample.Web.App_Start 
{ 
    public static class cms 
    { 
     public static void PreStart() 
     { 
      DynamicModuleUtility.RegisterModule(typeof(InstallerModule)); 
     } 
    } 
} 

namespace SomeCms 
{ 
    class ActionInvokerWrapper : IActionInvoker 
    { 
     readonly IActionInvoker actionInvoker; 

     public ActionInvokerWrapper(IActionInvoker actionInvoker) 
     { 
      this.actionInvoker = actionInvoker; 
     } 

     public bool InvokeAction(ControllerContext controllerContext, string actionName) 
     { 
      if (actionInvoker.InvokeAction(controllerContext, actionName)) 
      { 
       return true; 
      } 

      // No action method was found. 
      var controller = new CmsContentController(); 
      controller.ExecuteCmsContent(controllerContext.RequestContext); 

      return true; 
     } 
    } 

    class ControllerFactoryWrapper : IControllerFactory 
    { 
     readonly IControllerFactory factory; 

     public ControllerFactoryWrapper(IControllerFactory factory) 
     { 
      this.factory = factory; 
     } 

     public IController CreateController(RequestContext requestContext, string controllerName) 
     { 
      try 
      { 
       var controller = factory.CreateController(requestContext, controllerName); 
       WrapControllerActionInvoker(controller); 
       return controller; 
      } 
      catch (HttpException ex) 
      { 
       if (ex.GetHttpCode() == 404) 
       { 
        return new CmsContentController(); 
       } 

       throw; 
      } 
     } 

     static void WrapControllerActionInvoker(IController controller) 
     { 
      var controllerWithInvoker = controller as Controller; 
      if (controllerWithInvoker != null) 
      { 
       controllerWithInvoker.ActionInvoker = new ActionInvokerWrapper(controllerWithInvoker.ActionInvoker); 
      } 
     } 

     public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName) 
     { 
      return factory.GetControllerSessionBehavior(requestContext, controllerName); 
     } 

     public void ReleaseController(IController controller) 
     { 
      factory.ReleaseController(controller); 
     } 
    } 

    class InstallerModule : IHttpModule 
    { 
     static bool installed; 
     static readonly object installerLock = new object(); 

     public void Init(HttpApplication application) 
     { 
      if (installed) 
      { 
       return; 
      } 

      lock (installerLock) 
      { 
       if (installed) 
       { 
        return; 
       } 

       Install(); 
       installed = true; 
      } 
     } 

     static void Install() 
     { 
      Database.SetInitializer(new CreateDatabaseIfNotExists<PageContext>()); 
      HostingEnvironment.RegisterVirtualPathProvider(new ExampleVirtualPathProvider()); 
      WrapControllerBuilder(); 
      AddNotFoundRoute(); 
      AddCatchAllRoute(); 
     } 

     static void WrapControllerBuilder() 
     { 
      ControllerBuilder.Current.SetControllerFactory(new ControllerFactoryWrapper(ControllerBuilder.Current.GetControllerFactory())); 
     } 

     static void AddNotFoundRoute() 
     { 
      // To allow IIS to execute "/cmscontent" when requesting something which is disallowed, 
      // such as /bin or /add_data. 
      RouteTable.Routes.MapRoute(
       "CmsContent", 
       "cmscontent", 
       new { controller = "CmsContent", action = "CmsContent" } 
      ); 
     } 

     static void AddCatchAllRoute() 
     { 
      RouteTable.Routes.MapRoute(
       "CmsContent-Catch-All", 
       "{*any}", 
       new { controller = "CmsContent", action = "CmsContent" } 
      ); 
     } 

     public void Dispose() { } 
    } 

    public class CmsContentController : IController 
    { 
     public void Execute(RequestContext requestContext) 
     { 
      ExecuteCmsContent(requestContext); 
     } 

     public void ExecuteCmsContent(RequestContext requestContext) 
     { 
      //new CmsContentViewResult().ExecuteResult(new ControllerContext(requestContext, new FakeController())); 
      new CmsContentViewResult().ExecuteResult(new ControllerContext(requestContext, new FakeController())); 
     } 

     // ControllerContext requires an object that derives from ControllerBase. 
     // NotFoundController does not do this. 
     // So the easiest workaround is this FakeController. 
     class FakeController : Controller { } 
    } 

    public class CmsContentHandler : IHttpHandler 
    { 
     public void ProcessRequest(HttpContext context) 
     { 
      var routeData = new RouteData(); 
      routeData.Values.Add("controller", "CmsContent"); 
      var controllerContext = new ControllerContext(new HttpContextWrapper(context), routeData, new FakeController()); 
      var cmsContentViewResult = new CmsContentViewResult(); 
      cmsContentViewResult.ExecuteResult(controllerContext); 
     } 

     public bool IsReusable 
     { 
      get { return false; } 
     } 

     // ControllerContext requires an object that derives from ControllerBase. 
     class FakeController : Controller { } 
    } 

    public class CmsContentViewResult : ViewResult 
    { 
     public CmsContentViewResult() 
     { 
      ViewName = "index"; 
     } 

     public override void ExecuteResult(ControllerContext context) 
     { 
      var request = context.HttpContext.Request; 
      if (request != null && request.Url != null) 
      { 
       var url = request.Url.OriginalString; 

       ViewData["RequestedUrl"] = url; 
       ViewData["ReferrerUrl"] = (request.UrlReferrer != null && request.UrlReferrer.OriginalString != url) 
               ? request.UrlReferrer.OriginalString 
               : null; 
      } 

      base.ExecuteResult(context); 
     } 
    } 

    public class ExampleVirtualPathProvider : VirtualPathProvider 
    { 
     private readonly List<SimpleVirtualFile> virtualFiles = new List<SimpleVirtualFile>(); 

     public ExampleVirtualPathProvider() 
     { 
      var context = new PageContext(); 
      var pages = context.Pages.ToList(); 

      foreach (var page in pages) 
      { 
       virtualFiles.Add(new SimpleVirtualFile(page.Path)); 

      } 
     } 

     public override bool FileExists(string virtualPath) 
     { 
      var files = (from f in virtualFiles 
         where f.VirtualPath.Equals(virtualPath, StringComparison.InvariantCultureIgnoreCase) || 
           f.RelativePath.Equals(virtualPath, StringComparison.InvariantCultureIgnoreCase) 
         select f) 
         .ToList(); 

      return files.Count > 0 || base.FileExists(virtualPath); 
     } 

     private class SimpleVirtualFile : VirtualFile 
     { 
      public SimpleVirtualFile(string filename) : base(filename) 
      { 
       RelativePath = filename; 
      } 

      public override Stream Open() 
      { 
       var context = new PageContext(); 
       var page = context.Pages.FirstOrDefault(p => p.Path == RelativePath); 

       return new MemoryStream(Encoding.ASCII.GetBytes(page.View), false); 
      } 

      public string RelativePath { get; private set; } 
     } 

     private class SimpleVirtualDirectory : VirtualDirectory 
     { 
      public SimpleVirtualDirectory(string virtualPath) 
       : base(virtualPath) 
      { 

      } 

      public override IEnumerable Directories 
      { 
       get { return null; } 
      } 

      public override IEnumerable Files 
      { 
       get 
       { 
        return null; 
       } 
      } 

      public override IEnumerable Children 
      { 
       get { return null; } 
      } 
     } 

     public override VirtualFile GetFile(string virtualPath) 
     { 
      var files = (from f in virtualFiles 
         where f.VirtualPath.Equals(virtualPath, StringComparison.InvariantCultureIgnoreCase) || 
           f.RelativePath.Equals(virtualPath, StringComparison.InvariantCultureIgnoreCase) 
         select f).ToList(); 
      return files.Count > 0 
       ? files[0] 
       : base.GetFile(virtualPath); 
     } 

     public override CacheDependency GetCacheDependency(string virtualPath, IEnumerable virtualPathDependencies, DateTime utcStart) 
     { 
      return IsPathVirtual(virtualPath) ? null : base.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart); 
     } 

     private bool IsPathVirtual(string virtualPath) 
     { 
      var checkPath = VirtualPathUtility.ToAppRelative(virtualPath); 
      return 
       virtualFiles.Any(f => checkPath.StartsWith(virtualPath, StringComparison.InvariantCultureIgnoreCase)) || 
       virtualFiles.Any(f => checkPath.Replace("~", "").StartsWith(virtualPath, StringComparison.InvariantCultureIgnoreCase)); 
     } 

     public override bool DirectoryExists(string virtualDir) 
     { 
      return IsPathVirtual(virtualDir) || Previous.DirectoryExists(virtualDir); 
     } 

     public override VirtualDirectory GetDirectory(string virtualDir) 
     { 
      return IsPathVirtual(virtualDir) 
       ? new SimpleVirtualDirectory(virtualDir) 
       : Previous.GetDirectory(virtualDir); 
     } 
    } 

    public class ContentPage 
    { 
     public int Id { get; set; } 
     public string Path { get; set; } 
     public string View { get; set; } 
    } 

    public class PageContext : DbContext 
    { 
     public DbSet<ContentPage> Pages { get; set; } 
    } 
} 

Répondre

2

Cette question se révèle être un non-problème. Mon oubli de la dépendance du cache dans le fournisseur de chemin d'accès virtuel renvoie null pour les chemins d'accès virtuels. En tant que tel, la vue est mise en cache indéfiniment.

La solution consiste à utiliser un fournisseur de dépendances de cache personnalisé qui expire immédiatement.

public class NoCacheDependency : CacheDependency 
{ 
    public NoCacheDependency() 
    { 
     NotifyDependencyChanged(this, EventArgs.Empty); 
    } 
} 

public override CacheDependency GetCacheDependency(string virtualPath, IEnumerable virtualPathDependencies, DateTime utcStart) 
{ 
    return IsPathVirtual(virtualPath) ? new NoCacheDependency() : base.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart); 
} 
+0

Êtes-vous en mode édition ou en mode débogage? – nikmd23

+0

Ce n'était pas un problème de modes de compilation. Il s'agissait d'un problème du mécanisme de mise en cache sous-jacent en supposant que les signaux NULL étaient mis en cache pendant la durée du cycle de vie du domaine d'application. Vous pouvez le tester en modifiant le fichier web.config lors de l'exécution, ce qui a permis de recréer le domaine de l'application. À ce stade, les changements de page sont reflétés. –

Questions connexes