我认为这真的是意外的,因为ILocalizedStringManager
是IDependency
,这意味着应该始终为每个请求创建它。
我在实现类中放置了一些日志,如下所示:
public class LocalizedStringManager : ILocalizedStringManager
{
private readonly ITranslator _translator;
public LocalizedStringManager(ITranslator translator)
{
_translator = translator;
//we defined an OID on ITranslator to help debug this
Debug.Print($">>>>>>>>> Init LocalizedStringManager ... ({_translator.OID})");
}
public string GetLocalizedString(string scope, string text, string cultureName)
{
return _translator.Translate(text).Text;
}
}
public interface ITranslator : IDependency, IDisposable {
string Translate(string text);
Guid OID {get;}
}
public class Translator : ITranslator {
public Guid OID {get;}
public Translator(){
OID = Guid.NewGuid();
}
public string Translate(string text){
//...
}
public void Dispose(){
Debug.Print($">>>>>>>>> Disposing ... {OID}");
}
}
对于第一个请求,我可以看到它已打印出来
>>>>>>>>> Init LocalizedStringManager ... 086a1487-bad9-481d-92f0-4615b9c6c5fd
然后最后处理引用的ITranslator
,并打印以下内容:
>>>>>>>>> Disposing ... 086a1487-bad9-481d-92f0-4615b9c6c5fd
所以这里一切都很好,但是以某种方式(来自浏览器)的下一个请求通过LocalizedStringManager
触发了NullLocalizer
,奇怪的是我没有看到像>>>>>>>>> Init LocalizedStringManager ...
这样的行被打印,但是在引发异常之前,我有一条日志行打印出ITranslator
OID,它与值086a1487-bad9-481d-92f0-4615b9c6c5fd
相匹配,真令人困惑。
这是第二个连续请求运行的代码:
public interface ISomeService : IDependency {
SomeModel LoadData();
}
public class SomeService : ISomeService {
public Localizer T {get;set;}
public SomeService(){
T = NullLocalizer.Instance;
}
public SomeModel LoadData(){
//use the Localizer T here, such as to translate some text
var model = new SomeModel();
model.SomeText = T("SOME_KEY");
//...
return model;
}
}
因此T(...)
将触发一些LocalizedStringManager
,实际上我希望它创建一个新的(内部引用了新的ITranslator
),但是以某种方式 重用 (之前已在请求中创建的那一行)(因为这次我没有看到任何类似>>>>>>>>> Init LocalizedStringManager ...
的行被打印出来。
最后,它还将调用重用的ITranslator
(其Translate
方法),但此方法已在处理之前(在请求之前)处理,现在它抛出了一个异常,内容如下:
抛出异常:Autofac.dll中的'System.ObjectDisposedException'
Autofac.dll中发生了'System.ObjectDisposedException'类型的异常,但未在用户代码中处理 无法解决实例,并且无法从此LifetimeScope创建嵌套生命周期,因为该LifetimeScope已被处置。
一切似乎都无法控制。全部由Orchard
框架设置。我真的不明白如何LocalizedStringManager
可以这么容易地重用?永远不要那样重复使用它。现在(在尝试通过多语言支持使我们的网站全球化之后),我们相当频繁地遇到这种异常。
尤其是这种情况并非总是可重复的(我是说它间歇性地发生)。我希望有人对Orchard
有足够的经验,至少可以给我一些好的建议,以解决此问题。谢谢!
更新:
我无法提供确切的实际代码(逐个单词),但是主要逻辑可能如下所示(此处最不同的是IDisposable
未实现,IRepository
在ITranslator
中使用)实施):
public interface ITranslator : IDependency {
string Translate(string text);
LanguageInfo GetCurrentUserLanguage();
}
public class LanguageInfo {
public string Name {get;set;}
public string DisplayName {get;set;}
}
public class Translator : ITranslator {
readonly IRepository<LanguageRecord> _languageRepo;
readonly IOrchardServices _orchardServices;
public Translator(IRepository<LanguageRecord> languageRepo,
IOrchardServices orchardServices){
_languageRepo = languageRepo;
_orchardServices = orchardServices;
}
public LanguageInfo GetCurrentUserLanguage() {
if(_orchardServices.WorkContext.CurrentUser == null){
//obtain the language for not-logged-in user from his browser's cookie
var langCode = _orchardServices.WorkContext.HttpContext.Request.Cookies["LangCode"] ?? "en";
//get full language info
//this is where the exception thrown (because of using the disposed repository)
var fullInfo = _languageRepo.Table.FirstOrDefault(e => e.Code == langCode);
return new LanguageInfo { Name = fullInfo.Name, DisplayName = fullInfo.DisplayName };
} else {
//obtain from DB the settings for the current user
//however my test is for the case user not logging in
//So this branch is not important in this specific question.
//...
}
}
public string Translate(string text){
//get current user language
var lang = GetCurrentUserLanguage();
//do the translation … (not important in this specific question)
//...
}
}
同样,我必须说这种情况 间歇地 发生,没有办法确保它可以很容易地重现(这是最难找出原因的原因),尤其是在您可以重现一次。就我而言,在开始首页后,GetCurrentUserLanguage
被调用了几次(对于每个翻译),一切都很好。但是所有呼叫均来自T(…)
剃刀内部视图 。
但是,在单击一个链接(调用api ISomeService
)后会引发异常,其他链接确实使用了翻译,但是它们将T
用作Orchard的WebPage库的一部分(在razor视图中呈现) ,这些至少在我第一次尝试浏览网站时不会造成异常。现在很奇怪,因为重新访问该链接(正如我通过间歇性地使用 所描述的那样),不会引发异常。从那时起,实际上我几乎无法复制它,但是对于生产网站,我们仍然可以看到相当多的日志行显示间歇性事件(而且我敢肯定,其中许多是在剃刀视图中使用T(…)
,而不仅仅是从ISomeService
中获得,就像我第一次可以轻松重现异常时一样。
请注意,上面的代码不好,不是我最初写的(我们有几个成员组成的团队)。而且我知道应该对其进行重构,但这不是正确的时机,即使我已经解决了这个问题,也只是一个快速的解决方案。)