MVC 3多租户和View编译缓存问题

时间:2012-07-24 09:28:12

标签: c# asp.net asp.net-mvc asp.net-mvc-3 multi-tenant

我对ASP.NET MVC 3中的一种多租户实现有一个无法预见的问题。

假设我有2个网站:example.comexample.fr。它们都由IIS中的同一个MVC网站提供服务。

然后我有一个自定义VirtualPathProvider,基于域,提供来自不同位置的视图。 Controller始终相同,只从不同位置获取视图。

这一切都运作良好。 ASP.NET View编译带来了问题。假设两个域都具有相同名称和路径的视图(为清晰起见,MVC视图路径):

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

这应该运作良好。但ASP.NET BuildManager(将Razor代码编译为程序集)缓存仅基于虚拟路径的构建

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

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

到目前为止,我已经到了MvcWebRazorHostFactory,覆盖CreateHost方法以返回具有正确名称空间的RazorEngineHost。不确定它是否会起作用,因为我觉得我当时没有所需的所有信息(HttpContext就是其中之一)

任何人都有任何想法?我错过了一些明显的东西吗?

由于

2 个答案:

答案 0 :(得分:2)

好的,结果很简单。

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

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

答案 1 :(得分: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,因为我已经存储了被访问的站点(通过主机名),并根据该主机名(或客户端的唯一ID)缓存结果。它还允许我使用自己的路径来搜索视图,所以我可以:

视图/控制器/ Action.cshtml 要么 Views / Custom / [client] /Controller/Action.cshtml(或者非常小的部分) 如果我想覆盖视图的一部分。

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