视图上的 Resx 缓存问题
Resx caching issue on views
这是一个 .NET 4.5 项目,在我来到这里之前已经设置了很长时间,所以没有太多关于为什么这样做的细节。
我们有一个 Web 应用程序,其中每种语言都有自己的 resx 文件。
在每次页面加载时,值都是从 resx 文件加载的,这是通常的方式。
string strLangResourceValue = (string)HttpContext.GetGlobalResourceObject(lang, strLangResourceKey);
这很好用。可能值得一提的是,这行代码位于一个单独的项目中,因此可以与我们在该解决方案下的多个其他项目共享。
在视图 (Razor) 中,我们 @Application.GetLangResource("Some_Key");
检索我们需要的值。该语言取自存储在 HttpContext 中的会话模型。
当我们更改语言时会出现此问题,该页面上的每个部分视图都会进行相应翻译,但我们更改语言的 'user settings' 页面除外。奇怪的是,我们有两个视图,一个只读页面只显示数据,另一个包含实际表单以修改值,并且没有一个被翻译。
已知问题有:
- 我现在无法在网络应用程序中设置 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);
}
我终于弄清了我自己的问题,但我仍然不明白它是如何工作的。因此,问题在于,在第一个视图渲染时,它会从 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;
}
}
DisplayLabel
和 DisplayNameLocalizedAttribute
是自定义 属性 注释,它存储 resx 键和任何附加数据,例如(如果它是标签,那么通常我们可以添加“:” ) 最后,如果它用于输入。
Application.GetLangResource
是一个函数,它根据给定的语言从 resx 文件中获取值,如果找不到值,它会 return 一条合适的消息。
要让服务器使用创建的 DataAnnotationModelMetadataProvider
,您需要将其设置为 Global.asax
.
内的 Application_Start
事件中的 Current
要做到这一点,你会做:
LocalizedDataAnnotationsModelMetadataProvider loca = new LocalizedDataAnnotationsModelMetadataProvider();
ModelMetadataProviders.Current = loca;
重要的是(如果您在 global.asax 中遇到错误)您使用的是 System.Mvc
而不是 ModelMetadataProviders
出现的其他导入,因为它不会让您分配新的提供者。
这是一个 .NET 4.5 项目,在我来到这里之前已经设置了很长时间,所以没有太多关于为什么这样做的细节。
我们有一个 Web 应用程序,其中每种语言都有自己的 resx 文件。 在每次页面加载时,值都是从 resx 文件加载的,这是通常的方式。
string strLangResourceValue = (string)HttpContext.GetGlobalResourceObject(lang, strLangResourceKey);
这很好用。可能值得一提的是,这行代码位于一个单独的项目中,因此可以与我们在该解决方案下的多个其他项目共享。
在视图 (Razor) 中,我们 @Application.GetLangResource("Some_Key");
检索我们需要的值。该语言取自存储在 HttpContext 中的会话模型。
当我们更改语言时会出现此问题,该页面上的每个部分视图都会进行相应翻译,但我们更改语言的 'user settings' 页面除外。奇怪的是,我们有两个视图,一个只读页面只显示数据,另一个包含实际表单以修改值,并且没有一个被翻译。
已知问题有:
- 我现在无法在网络应用程序中设置 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);
}
我终于弄清了我自己的问题,但我仍然不明白它是如何工作的。因此,问题在于,在第一个视图渲染时,它会从 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;
}
}
DisplayLabel
和 DisplayNameLocalizedAttribute
是自定义 属性 注释,它存储 resx 键和任何附加数据,例如(如果它是标签,那么通常我们可以添加“:” ) 最后,如果它用于输入。
Application.GetLangResource
是一个函数,它根据给定的语言从 resx 文件中获取值,如果找不到值,它会 return 一条合适的消息。
要让服务器使用创建的 DataAnnotationModelMetadataProvider
,您需要将其设置为 Global.asax
.
Application_Start
事件中的 Current
要做到这一点,你会做:
LocalizedDataAnnotationsModelMetadataProvider loca = new LocalizedDataAnnotationsModelMetadataProvider();
ModelMetadataProviders.Current = loca;
重要的是(如果您在 global.asax 中遇到错误)您使用的是 System.Mvc
而不是 ModelMetadataProviders
出现的其他导入,因为它不会让您分配新的提供者。