【问题标题】:Resx caching issue on views视图上的 Resx 缓存问题
【发布时间】:2018-01-09 11:54:30
【问题描述】:

这是一个 .NET 4.5 项目,在我来这里之前已经设置了很长时间,所以没有太多关于为什么这样做的细节。

我们有一个 Web 应用程序,其中每种语言都有自己的 resx 文件。 在每次页面加载时,都会以通常的方式从 resx 文件中加载值。

string strLangResourceValue = (string)HttpContext.GetGlobalResourceObject(lang, strLangResourceKey);

这很好用。可能值得一提的是,这行代码位于一个单独的项目中,因此可以与我们在该解决方案下的多个其他项目共享。

在视图(Razor)中,我们使用@Application.GetLangResource("Some_Key"); 来检索我们需要的值。语言取自 HttpContext 中存储的会话模型。

当我们更改语言时会出现问题,除了我们更改语言的“用户设置”页面之外,该页面上的每个部分视图都会相应地进行翻译。奇怪的是,我们有两个视图,一个只读页面仅显示数据,另一个包含修改值的实际表单,并且没有一个被翻译。

已知问题有:

  • 我现在无法在 Web 应用程序中设置 CultureInfo,我需要使用会话模型来获取此信息并使用上面的代码行来获取数据。
  • 强制刷新浏览器、清除 cookie 和缓存并不能解决问题。
  • 只有重新启动 IIS 服务器才能解决问题(重新编译 resx 文件)

我知道 resx 文件是在运行时编译的并且是静态的。它们在应用程序运行期间不会在代码中更改,只有用户会话会更改。上面的列表试图找出问题的根源,但每次有人更改语言时都重新启动应用程序不是一种选择(正如您可能猜到的那样)。

我是否缺少明显的解决方案? 没有 resx 错误,它只是两个特定页面上的 IIS 缓存(或者至少看起来是这样)。它们的构建方式与所有其他的相同,只是不切换语言。这适用于所有用户更改语言时。

我已尝试使用以下代码行手动清除缓存,但未完成作业问题仍然存在。

Resources.AppSettings.ResourceManager.ReleaseAllResources();
Resources.English.ResourceManager.ReleaseAllResources();
Resources.Dutch.ResourceManager.ReleaseAllResources();
HttpResponse.RemoveOutputCacheItem("/Views/User/userViewDetails.cshtml");

foreach(System.Collections.DictionaryEntry entry in HttpContext.Cache) {
    HttpContext.Cache.Remove((string) entry.Key);
}

【问题讨论】:

  • 请参阅ASP.NET MVC 5 culture in route and url 了解不涉及会话或缓存的替代方法。
  • @NightOwl888 这是建议,我尝试了另一种解决方案。在进一步调试之后,似乎不再从 resx 中获取值后第一页加载。我目前正在测试另一种方法,如果一切顺利的话,到目前为止似乎可行,我将在这里发布我的解决方案。再次感谢。

标签: c# asp.net asp.net-mvc razor resx


【解决方案1】:

我终于弄清了自己的问题,但我仍然不明白它是如何工作的。因此,问题在于,在第一次视图渲染时,它会从 resx 中获取相应语言的值,但不会在任何后续渲染中获取(就像它会缓存这些值以缩短对 resx 的访问时间,这很棒)。

我最终实现了自己的 DataAnnotationsModelMetadataProvider,它做得很好。

所以对于任何对我的解决方案感兴趣的人来说,它看起来类似于:

public class LocalizedDataAnnotationsModelMetadataProvider : DataAnnotationsModelMetadataProvider
{

    protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
    {
        var meta = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);  //  Get metadata of the property

        string resXKey = string.Empty;
        object attribute = attributes.OfType<DisplayLabel>().FirstOrDefault();  //  Try to get the DisplayLabel annotation

        //  If DisplayLabel is found then...
        if (attribute != null)
        {
            resXKey = ((DisplayLabel)attribute).Key;    //  Grab the resx key added to the annotation
            meta.DisplayName = Application.GetLangResource(resXKey) + meta.DisplayName; //  Retrieve the language resource for the key and add back any trailing items returned by the annotation
        }

        //  DisplayLabel is not found...
        if (attribute == null)
        {
            attribute = attributes.OfType<DisplayNameLocalizedAttribute>().FirstOrDefault();    //  Try to get the DisplayNameLocalizedAttribute annotation

            //  If annotation exists then...
            if (attribute != null)
            {
                resXKey = ((DisplayNameLocalizedAttribute)attribute).Key;   //  Grab the resx key added to the annotation

                string resXValue = Application.GetLangResource(resXKey);    //  Retrieve the language resource for the key
                string finalValue = resXValue;  //  Create a new string

                if (((DisplayNameLocalizedAttribute)attribute).IsLabel) //  Check if property annotation is set to label
                {
                    finalValue = resXValue + meta.DisplayName;  //  Add back any trailing items returned by the annotation
                }


                meta.DisplayName = finalValue;  //  Set the DisplayName of the property back onto the meta
            }
        }

        return meta;
    }

} 

DisplayLabelDisplayNameLocalizedAttribute 是自定义属性注释,它们存储 resx 键和任何其他数据,例如(如果它是标签,所以通常我们可以在末尾添加“:”),如果它用于输入。

Application.GetLangResource 是一个函数,它根据给定的语言从 resx 文件中获取值,如果未找到值,它将返回合适的消息。

要使服务器使用创建的DataAnnotationModelMetadataProvider,您需要将其设置为Current 中的Application_Start 事件Global.asax

要做到这一点,你会这样做:

LocalizedDataAnnotationsModelMetadataProvider loca = new LocalizedDataAnnotationsModelMetadataProvider();

ModelMetadataProviders.Current = loca;

重要的是(如果您在 global.asax 中遇到错误)您使用的是 System.Mvc,而不是出现在那里的 ModelMetadataProviders 的其他导入,因为它不允许您分配新的提供程序。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-19
    • 2018-07-27
    相关资源
    最近更新 更多