我正在开发一种多语言ASP.NET MVC应用程序(MVC4)。
我想使我的资源文件字符串在运行时可编辑,而无需重新编译应用程序且不回收应用程序池,而且.resx
文件看起来不太可能,因此我迁移到数据库中存储字符串资源。
我必须从数据库中获取每个标签/字符串资源,因此对于每个请求,数据库的命中都会增加。 该如何解决?
我到处搜索,有人建议将资源加载到字典中,并在登录/登录页面上将其存储为应用程序变量,然后将该字典用作资源而不是数据库命中。
我很困惑,什么是有效的方法?有人可以指导我正确使用以避免更多数据库访问吗?
任何帮助/建议将不胜感激。
谢谢
最佳答案
使用.resx文件进行本地化时,我遇到了同样的问题。当从事翻译的人不是程序员时,他们的表现就不好。现在,我们在管理区域中有一个翻译页面。效果很好。
我们仍然没有一个好的解决方案的领域是数据注释,它仍然使用.resx文件。我整理了以下源代码,以删除对我们实际数据库结构或表的任何引用。
如果数据库中不存在某个条目,则使用基础.resx文件会有一个回退。如果.resx文件中没有条目,则每当找到一个大写字母时,我都会拆分该单词(CamelSpace是字符串扩展方法)。
最后,根据您的实现,您可能需要删除上下文缓存,尤其是在缓存超出流程的情况下。
用法示例:
@LanguageDb.Translate("Enter your first name below","FirstNamePrompt")
@LanguageDb.Me.FirstName
@String
.Format(LanguageDb
.Translate(@"You can be insured for
{0} down and {1} payments of {2}"),
Model.Down,Model.NumPayments,
Model.InstallmentAmount)
public class LanguageDb : DynamicObject
{
public static dynamic Me = new LanguageDb();
private LanguageDb() { }
public static string Translate(string englishPhrase, string resourceCode = null)
{
return GetTranslation(englishPhrase, resourceCode) ?? englishPhrase;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = GetTranslation(binder.Name);
return true;
}
private static string GetTranslation(string resourceName, string resourceCode = null)
{
resourceCode = resourceCode ?? resourceName;
if (resourceCode.Contains(" ") || resourceCode.Length > 50)
{
resourceCode = resourceName.GetHashCode().ToString(CultureInfo.InvariantCulture);
}
var lang = (string)HttpContext.Current.Request.RequestContext.RouteData.Values["lang"] ?? "en";
// cache entries for an hour
var result = Get(subagent + "_" + lang + "_" + resourceCode, 3600, () =>
{
// cache a datacontext object for 30 seconds.
var context = Get("_context", 30, () => new YourDataContext()) as YourDataContext;
var translation = context.Translations.FirstOrDefault(row => row.lang == lang && row.Code == resourceCode);
if (translation == null)
{
translation = new Lookup {
Code = resourceCode,
lang = lang,
Value = Language.ResourceManager.GetString(resourceName, Language.Culture)
?? resourceName.CamelSpace()
};
context.Translations.Add(translation);
context.SaveChanges();
}
return translation.Value;
});
return result;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
// ignore this
return true;
}
public static T Get<T>(string cacheId, int secondsToCache, Func<T> getItemCallback) where T : class
{
T item = HttpRuntime.Cache.Get(cacheId) as T;
if (item == null)
{
item = getItemCallback();
if (secondsToCache > 0)
{
HttpRuntime.Cache.Insert(
cacheId, item, null, Cache.NoAbsoluteExpiration,
new TimeSpan(0, 0, secondsToCache), CacheItemPriority.Normal, null);
}
else
{
HttpRuntime.Cache.Insert(cacheId, item);
}
}
return item;
}
}