我有一个类包含如下内容:
public static class Config
{
private static Lazy<ConfigSource> _cfgSrc = new Lazy<ConfigSource>(
() => { /* "ValueFactory" here... */ },
true);
public static ConfigSource ConfigSource
{
get { return _cfgSrc.Value; }
}
}
在访问
ConfigSource
属性时遇到了这个InvalidOperationException
:
ValueFactory 试图访问此实例的 Value 属性。
我在访问
Value
属性的“价值工厂”方法中看不到任何内容。还有什么可以触发此异常吗?这个问题只会间歇性地发生,但一旦发生,就需要重置 IIS 来清除异常(一旦发生就似乎被缓存了)。
事实证明,此错误仅在尝试检查 Visual Studio 调试器中的
Value
的 Lazy<>
属性时发生。这样做似乎会造成死锁,因为访问 Value
似乎会使线程挂起很长时间,直到 InvalidOperationException
最终发生。我永远无法拦截原始Exception
,所以我看不到内部堆栈跟踪。
我只是把它归结为 Visual Studio 中的错误或他们对
Lazy<>
的实现。
循环依赖也发生在我身上,所以如果这些步骤让你无处可去,请尝试仔细检查堆栈跟踪并验证没有循环依赖。
ValueFactory 试图访问此实例的 Value 属性。
它可能对某些人有帮助,我能够通过检查整个 ValueFactory 过程来修复该错误。 在我的示例中,我创建了一个简单的模型并将其与其他一些数据链接,但在链接过程中我访问了单例中的 Value 属性,这导致了错误。
所以 访问 ValueFactory 中的惰性对象的值会抛出这样的错误。 由于错误消息已经表明 ;-)
Lazy<T>
的行为是缓存ValueFactory
抛出的异常。由于 InvalidOperationException
消息中提供的信息不足,这可能会导致潜在的混淆行为。 Microsoft 通过 Connect 意识到了这个问题,但是,它被标记为 Wont Fix 因为他们认为异常本身有足够的信息来诊断问题。
如果您收到的 IOE 存在内部异常,它应该(不是说它会)包含足够的信息以继续前进。另一种可能性是你有一个
try...catch
块重新抛出异常(throw ex;
而不是 throw;
),你将失去有价值的信息。
要确保您的异常未被缓存,请使用 LazyThreadSafetyMode.PublicationOnly 作为第二个参数,而不是 true。
使用 true,你最终会得到一个 LazyThreadSafetyMode.ExecutionAndPublication。这将确保只有一个线程进入 ValueFactory 方法,同时也确保异常将被缓存。
private static Lazy<ConfigSource> _cfgSrc = new Lazy<ConfigSource>(
() => { /* "ValueFactory" here... */ },
LazyThreadSafetyMode.PublicationOnly);
有关更多信息,请参阅提供的六字母变量链接。
当延迟加载配置时,一定不要调用需要配置的方法。这将调用重新启动该过程的配置加载器,从而导致所描述的错误。
在我的例子中,我正在记录负载状态,而记录器需要配置
这确实是由嵌套的
Lazy
实例引起的,当一个人在工厂中调用另一个Value
但更具体地说,它是由这些Lazy
实例的不同发布模式引起的,例如一个是PublicationOnly
,另一个是None
。我认为这实际上是 Lazy 实现中的一个错误。
[Test]
public async Task Test()
{
var innerLazy = new Lazy<string>(() =>
{
Thread.Sleep(10);
return "lazy";
}, LazyThreadSafetyMode.None);
var outerLazy = new Lazy<string>(() => innerLazy.Value, LazyThreadSafetyMode.PublicationOnly);
var task1 = new Task<string>(() => outerLazy.Value);
task1.Start();
var task2 = new Task<string>(() => outerLazy.Value);
task2.Start();
await Task.WhenAll(task1, task2);
Assert.That(outerLazy.Value, Is.EqualTo("lazy"));
}