ASP.NET MVC 4 移动显示模式停止工作

28

ASP.NET MVC 4中的移动显示模式在运行约一小时后停止提供正确的视图,尽管浏览器覆盖正确地检测到了被覆盖的移动设备。

重新启动应用程序池可以暂时解决该问题。

新的浏览器覆盖功能允许移动设备正确查看站点的桌面版本,反之亦然。但是约经过一小时的运行时间后,移动视图不再呈现给移动设备;仅呈现默认的桌面 Razor 模板。唯一的解决方法是重新启动应用程序池。

奇怪的是,浏览器覆盖 cookie 仍然起作用。一个主要的_Layout.cshtml 模板正确显示“mobile”或“desktop”文本,具体取决于 ViewContext.HttpContext.GetOverriddenBrowser().IsMobileDevice 值,但仍会呈现错误的视图。这使我相信问题出在 DisplayModes 上。

所涉及的操作没有被缓存:

[OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
我正在使用51Degrees进行移动设备检测,但我认为这不应影响覆盖的移动设备检测。ASP.NET MVC 4 Beta和Developer Preview中的DisplayModes功能是否存在错误,还是我做错了其他事情?
以下是我在Application_Start中设置的DisplayModes
DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("iPhone")
{
    ContextCondition = context =>
        context.GetOverriddenBrowser().IsMobileDevice
        && (context.Request.UserAgent.IndexOf("iPhone", StringComparison.OrdinalIgnoreCase) >= 0
        || context.Request.UserAgent.IndexOf("Android", StringComparison.OrdinalIgnoreCase) >= 0
        || !context.Request.Browser.IsMobileDevice)
    });

/*  Looks complicated, but renders Home.iPhone.cshtml if the overriding browser is
    mobile or if the "real" browser is on an iPhone or Android. This falls through
    to the next instance Home.Mobile.cshtml for more basic phones like BlackBerry.
*/

DisplayModeProvider.Instance.Modes.Insert(1, new DefaultDisplayMode("Mobile")
{
    ContextCondition = context =>
        context.GetOverriddenBrowser().IsMobileDevice
});

你好,你解决了这个问题吗?我正在运行mvc4 rc,也遇到了同样的问题。它最初可以工作,但之后就停止了。 - Morten Schmidt
7个回答

21

这是MVC 4中已知的问题 (Codeplex: #280: 多个DisplayModes-缓存错误,将显示错误的视图)。这个问题将在下一个版本的MVC中得到解决。

与此同时,您可以在此处安装一个可用的解决方案包: http://nuget.org/packages/Microsoft.AspNet.Mvc.FixedDisplayModes

对于一些自定义注册视图引擎集合的应用程序,您应该确保引用Microsoft.Web.Mvc.FixedRazorViewEngineMicrosoft.Web.Mvc.FixedWebFormViewEngine而不是默认的视图引擎实现。


哎呀,我们一直在使用这个包,但最近在对global.asax.cs进行修改并部署到生产环境时,完全忘记了以编程方式使用FixedRazorViewEngine而不是RazorViewEngine!我简直不敢相信。所以,真的很棒的答案,特别是“对于一些自定义注册视图引擎集合的应用程序”这部分。 - Allen Rice
1
有一些像我这样的新手不知道该怎么做。只需在Global.asax中将ViewEngines.Engines.Add(new RazorViewEngine());替换为ViewEngines.Engines.Add(new Microsoft.Web.Mvc.FixedRazorViewEngine());即可。 - Okan Kocyigit

1

1

发布者是mSchmidt,你是Morten Schmidt吗? - user1436475
2
你会对自己的帖子 @user1436475 做出反应吗? - Rob

0
如果您希望所有移动设备使用相同的移动布局,您可以使用以下方法:
DisplayModeProvider.Instance.Modes.Insert(1, new DefaultDisplayMode("Mobile") 
{ 
    ContextCondition = context => 
        context.GetOverriddenBrowser().IsMobileDevice 
}); 

当然,您需要在共享布局文件夹中创建名为_Layout.Mobile.cshtml的视图。

如果您想为每种设备或浏览器类型拥有单独的布局,则需要执行以下操作:

DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("Android")
            {
                ContextCondition = (context => context.GetOverriddenUserAgent().IndexOf
                    ("Android", StringComparison.OrdinalIgnoreCase) >= 0)
            });

            DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("iPhone")
            {
                ContextCondition = (context => context.GetOverriddenUserAgent().IndexOf
                    ("iPhone", StringComparison.OrdinalIgnoreCase) >= 0)
            });

            DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("Mobile")
            {
                ContextCondition = (context => context.GetOverriddenUserAgent().IndexOf
                   ("IEMobile", StringComparison.OrdinalIgnoreCase) >= 0)
            });

当然,你需要为每个命名创建一个共享布局文件夹中的视图。
_Layout.Android.cshtml _Layout.iPhone.cshtml _Layout.Mobile.cshtml

是的,我知道。但这与我的问题完全无关。 - Petrus Theron

0

你不能只是这样做吗?

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();
    // Code removed for clarity.

    // Cache never expires. You must restart application pool
    // when you add/delete a view. A non-expiring cache can lead to
    // heavy server memory load.

    ViewEngines.Engines.OfType<RazorViewEngine>().First().ViewLocationCache =
        new DefaultViewLocationCache(Cache.NoSlidingExpiration);

    // Add or Replace RazorViewEngine with WebFormViewEngine
    // if you are using the Web Forms View Engine.
}

0

大家好,这里是解决你们所有烦恼的答案..... :)

为了避免这个问题,你可以指示ASP.NET根据访问者是否使用移动设备来改变缓存条目。在你的页面的OutputCache声明中添加一个VaryByCustom参数,如下所示:

<%@ OutputCache VaryByParam="*" Duration="60" VaryByCustom="isMobileDevice" %>
Next, define isMobileDevice as a custom cache parameter by adding the following method override to your Global.asax.cs file:

public override string GetVaryByCustomString(HttpContext context, string custom)
{
    if (string.Equals(custom, "isMobileDevice", StringComparison.OrdinalIgnoreCase))
        return context.Request.Browser.IsMobileDevice.ToString();

    return base.GetVaryByCustomString(context, custom);
}

这将确保移动访问者在页面上不会收到桌面访问者先前放入缓存的输出。

请查看微软发布的这篇白皮书。 :)

http://www.asp.net/whitepapers/add-mobile-pages-to-your-aspnet-web-forms-mvc-application

感谢您的努力编程,继续加油!


0

我不能代表这个特定的堆栈(我还在MVC2中),但请检查您的输出缓存设置(无论是在控制器还是视图中 - 以及在您的应用程序和机器级别的web.config中)。我曾经看到它最初对前几个用户起作用,然后当ASP决定缓存时,一个桌面浏览器恰好进入,然后每个人都得到相同的视图。因此,我们避免了输出缓存,希望这个问题以后会得到解决。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接