В классе ViewModel имеется два булевых свойства, на которые происходит Binding из xaml:
public bool CanPublish => Document.State.CanPublish();
public bool CanCreateToolData => Document.State.CanCreateToolData();
Разметка из xaml:
Visibility="{Binding CanCreateToolData, Converter={StaticResource BooleanToVisibilityConverter}}"
Visibility="{Binding CanPublish, Converter={StaticResource BooleanToVisibilityConverter}}"
Поскольку вычисление статусов достаточно долгий процесс, то у меня в модели внутри методов CanPublish() и CanCreateToolData() используется инициализация некоторых полей через Lazy. Дебаггер показывает следующее:
1) Идём инициализировать свойство CanPublish, натыкаемся на Lazy;
2) Пока Lazy-метод выполняется, начинается инициализация второго свойства CanCreateToolData, которое тоже обращается к тому же самому Lazy-методу. В итоге:
System.InvalidOperationException
HResult=0x80131509
Сообщение = ValueFactory пытается получить доступ к свойству Value этого экземпляра.
Источник = mscorlib
Трассировка стека:
в System.Lazy`1.CreateValue()
в System.Lazy`1.LazyInitValue()
в System.Lazy`1.get_Value()
в "путь к моему классу"
Вопрос заключается в следующем: почему инициализация свойств производится асинхронно? Это из-за Lazy-методов? Ошибка, как я понимаю, заключается в том, что идёт обращение к одному и тому же Lazy-методу повторно, пока он ещё не закончил своё выполнение. Есть ли возможность как-то явно указать, что эти свойства должны инициализироваться синхронно?
Спасибо за внимание.
Дополню код Lazy-методом: Тут обращение к свойству ClassConfig
public bool CanCreateToolData()
{
..
if (ver.Services.Interpretator.ClassConfig == null) { return false; }
...
}
public bool CanPublish()
{
..
if (ver.Services.Interpretator.ClassConfig == null) { return false; }
...
}
А тут вызов метода Lazy:
// поле класса, InvalidOperationException происходит при обращении к mClassDefiner.Value
public IClassDefiner ClassConfig => mClassDefiner.Value;
public Interpretator()
{
mClassDefiner = new Lazy<IClassDefiner>(GetClassDefiner);
}
private IClassDefiner GetClassDefiner()
{
// ресурсоёмкая операция
}
Рассмотрим пример:
Есть класс с Lazy полем
public class Foo
{
public Lazy<int> Lazy = new Lazy<int>(() =>
{
Console.WriteLine($"{DateTime.UtcNow} - {Thread.CurrentThread.ManagedThreadId} - LAZY ENTER");
Thread.Sleep(5000);
Console.WriteLine($"{DateTime.UtcNow} - {Thread.CurrentThread.ManagedThreadId} - LAZY EXIT");
return 10;
});
}
Мы логгируем входы и выходы из функции создания для поля. Далее, запустим параллельно 10 тредов и попробуем обратиться к нашему полю
Console.WriteLine($"{DateTime.UtcNow} - {Thread.CurrentThread.ManagedThreadId} - START");
var target = new Foo();
var threads = Enumerable.Range(0, 10).Select(x =>
{
var thread = new Thread(() =>
{
Console.WriteLine($"{DateTime.UtcNow} - {Thread.CurrentThread.ManagedThreadId} - TASK {x} ENTER");
var value = target.Lazy.Value;
Console.WriteLine($"{DateTime.UtcNow} - {Thread.CurrentThread.ManagedThreadId} - TASK {x} EXIT");
});
thread.Start();
return thread;
}).ToArray();
foreach (var t in threads) t.Join();
Console.WriteLine($"{DateTime.UtcNow} - {Thread.CurrentThread.ManagedThreadId} - END");
Вывод в консоль предсказуем: все треды ждут функцию сооздания, и после этого завершаются без исключений
31.05.2018 15:05:55 - 33 - START
31.05.2018 15:05:55 - 32 - TASK 0 ENTER
31.05.2018 15:05:55 - 31 - TASK 1 ENTER
31.05.2018 15:05:55 - 32 - LAZY ENTER
31.05.2018 15:05:55 - 30 - TASK 2 ENTER
31.05.2018 15:05:55 - 29 - TASK 3 ENTER
31.05.2018 15:05:55 - 28 - TASK 4 ENTER
31.05.2018 15:05:55 - 27 - TASK 5 ENTER
31.05.2018 15:05:55 - 26 - TASK 6 ENTER
31.05.2018 15:05:55 - 25 - TASK 7 ENTER
31.05.2018 15:05:55 - 19 - TASK 8 ENTER
31.05.2018 15:05:55 - 18 - TASK 9 ENTER
31.05.2018 15:06:00 - 32 - LAZY EXIT
31.05.2018 15:06:00 - 32 - TASK 0 EXIT
31.05.2018 15:06:00 - 31 - TASK 1 EXIT
31.05.2018 15:06:00 - 29 - TASK 3 EXIT
31.05.2018 15:06:00 - 30 - TASK 2 EXIT
31.05.2018 15:06:00 - 28 - TASK 4 EXIT
31.05.2018 15:06:00 - 25 - TASK 7 EXIT
31.05.2018 15:06:00 - 26 - TASK 6 EXIT
31.05.2018 15:06:00 - 19 - TASK 8 EXIT
31.05.2018 15:06:00 - 27 - TASK 5 EXIT
31.05.2018 15:06:00 - 18 - TASK 9 EXIT
31.05.2018 15:06:00 - 33 - END
А та ошибка, что вы указываете - это да, это именно проблема, когда вы в процессе подсчета значения, прямо внутри вашей ресурсоемкой операции, пытаетесь рекурсивно это значение получить.
Воспроизвести очень просто.
Lazy<int> value = null;
value = new Lazy<int>(()=>value.Value);
Console.WriteLine(value.Value);
на выходе будет исключение ValueFactory пытается получить доступ к свойству Value этого экземпляра.
, так как невозможно посчитать, чем равно Value, если вы внутри этого подсчета пытаетесь его использовать.
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
Стратегії та тренди в SMM - Технології, що формують майбутнє сьогодні
Выделенный сервер, что это, для чего нужен и какие характеристики важны?
Современные решения для бизнеса: как облачные и виртуальные технологии меняют рынок
Хочу сделать свои HtmlHelper, но так что бы первый екстеншен, после Html, был мой, а после этого уже HtmlHelper, как в Kendo
url содержит кириллицу, изначально закодированную в HEX - %20%2B%A0 и тдТак вот если сделать WebRequest
Была взята за основу готовая база данных и по ней создан контекст и модели таблицСвязал таблицы в конструкторе таблиц таким образом: countrylanguage...
Как сохранить график, построенный с помощью Chart в PDF формате?