2012-07-24 27 views
4

我有一个关于在ASP.NET MVC一种多租户执行3.MVC 3多租户和查看编译缓存问题

说我有2个网站无法预料的问题:example.comexample.fr。它们都由IIS中的相同MVC网站提供服务。

然后我有一个自定义VirtualPathProvider,基于域,从不同的位置提供视图。控制器总是相同的,只有视图从不同的位置获取。

这一切都很好。这个问题伴随着ASP.NET视图编译。假设两个域具有相同的名称和路径的视图(MVC为清楚起见意见路径):

example.com/Views/MyController/Index.cshtml 
example.fr/Views/MyController/Index.cshtml 

这应该很好地工作。但是ASP.NET BuildManager(将Razor代码编译为程序集)缓存了仅基于虚拟路径的内部版本

所以这意味着当我第一次访问example.com时,我得到了正确的看法。但是,如果我尝试在example.fr的上下文中呈现视图,则ASP.NET认为视图未被修改(虚拟路径相同,这是真实的),并且它将从缓存执行视图,因此呈现错误的视图。

解决这个问题的一种方法是根据域名在不同的命名空间中编译视图。

到目前为止,我得到了MvcWebRazorHostFactory,覆盖CreateHost方法返回一个RazorEngineHost与正确的命名空间。不知道它是否会工作,因为我不认为我在那一点上有所有需要的信息(HttpContext是其中之一)

任何人有任何想法?我在这里错过了很明显的东西吗

感谢

回答

2

好吧,结果很简单。

我所要做的只是在我的VirtualPathProvider中覆盖GetCacheKey并返回一个考虑主机名的密钥字符串。

在我的情况下,我只是串联主机和虚拟路径并返回结果字符串的哈希码。

+0

嗨,佩德罗,你可以把这段代码放在Gist或在线的地方吗?我正在努力寻找第二个租户的虚拟路径时编译错误,它看起来像你可能有它排序 - 也许这只是这个CreateHost我需要担心,虽然... – mcintyre321 2012-11-29 09:33:58

+0

我认为这是绝对CreateHost,作为我按照描述重写了GetCacheKey,并且可以从我的租户加载js文件,这只是它正在努力的xshtml。 – mcintyre321 2012-11-29 15:51:59

+0

这个概念的缺点是,当请求每个域的页面时,您将失去之前完成的缓存。所以基本上你失去了缓存编译视图的好处。有没有更好的解决方案,允许框架在第一次加载后仍然缓存每个单独的视图? – 2013-03-22 03:32:56

1

我不知道你是否已经走了这条路太远考虑其他方法,但我也有一个多租户系统,我发展,我已经做到了通过覆盖基于Razor的视图引擎。

public class MultiTenancyRazorViewEngine : RazorViewEngine 
{ 
    /// <summary> 
    /// Finds the specified partial view by using the specified controller context. 
    /// </summary> 
    /// <param name="controllerContext">The controller context.</param> 
    /// <param name="partialViewName">The name of the partial view.</param> 
    /// <param name="useCache">true to use the cached partial view.</param> 
    /// <returns>The partial view.</returns> 
    /// <exception cref="T:System.ArgumentNullException">The <paramref name="controllerContext"/> parameter is null (Nothing in Visual Basic).</exception> 
    /// <exception cref="T:System.ArgumentException">The <paramref name="partialViewName"/> parameter is null or empty.</exception> 
    public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache) 
    { 
     var searchedLocations = new List<string>(); 
     var foundFile = Support.ResolvePath(string.Format("{0}.cshtml", partialViewName), controllerContext.HttpContext, controllerContext.RouteData, searchedLocations); 

     return foundFile == null 
      ? new ViewEngineResult(searchedLocations) 
      : base.FindPartialView(controllerContext, foundFile, useCache); 
    } 

    /// <summary> 
    /// Finds the view. 
    /// </summary> 
    /// <param name="controllerContext">The controller context.</param> 
    /// <param name="viewName">Name of the view.</param> 
    /// <param name="layoutPath">The layout path.</param> 
    /// <param name="useCache">if set to <c>true</c> [use cache].</param> 
    /// <returns></returns> 
    public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string layoutPath, bool useCache) 
    { 
     var searchedLocations = new List<string>(); 
     var foundFile = Support.ResolvePath(string.Format("{0}.cshtml", viewName), controllerContext.HttpContext, controllerContext.RouteData, searchedLocations); 

     return foundFile == null 
      ? new ViewEngineResult(searchedLocations) 
      : base.FindView(controllerContext, foundFile, layoutPath, useCache); 
    } 

我有我自己的查找视图的支持方法:“ResolvePath”。我使用HttpContext,因为我已经存储了正在访问的站点(通过主机名),并根据该主机名(或客户端的唯一标识符)缓存结果。这也让我做我自己的路径表单搜索视图,这样我就可以有:

查看/控制器/ Action.cshtml 或 查看/自定义/ [客户] /Controller/Action.cshtml(或者真的非常小的部分) 如果我想覆盖视图的一部分。

对不起,它没有真正回答你的具体问题,但它有帮助吗?如果你对这种方法感兴趣,我可以提供更多的代码。

+0

感谢你的想法,它也会工作得很好。我找到了适合我的特殊情况的标准解决方案,并在以上供以后参考。 – Pedro 2012-07-24 11:41:24