我的WPF应用程序包含一个主TabControl
,它通过MEF加载多个插件。从技术上讲,插件集合的ICollectionView
绑定到主ItemsSource
的{{1}} - 属性。 TabControl
- 属性也设置为IsSynchronizedWithCurrentItem
,以跟踪当前选定的标签项。
要初始化插件,我订阅{strong> True
- CurrentChanged
事件,通过延迟加载初始化所选插件。由于很多初始化任务都是异步,我还将事件处理程序声明为异步。
ICollectionView
private ICollectionView pluginsCv;
public ICollectionView PluginsCv
{
get
{
if (pluginsCv == null)
{
pluginsCv = CollectionViewSource.GetDefaultView(Plugins);
pluginsCv.CurrentChanged += async (sender, args) =>
{
await LoadCurrentSection();
};
}
return pluginsCv;
}
}
方法的任务:
LoadCurrentSection
- 方法
LoadCurrentSection
private async Task LoadCurrentSection()
{
// If no tab is selected, select the first one
if (PluginsCv.CurrentItem == null)
{
// Comment for stackoverflow: Another raise of the CurrentChanged-event does not seem to be the cause. Tried to prevent another execution of LoadCurrentSection via a bool flag, an stayed in this method (no return statement). Same result.
PluginsCv.MoveCurrentToFirst();
return;
}
// Get the ViewModel of the currently selected section/plugin
var pluginViewModel = ((IPlugin)PluginsCv.CurrentItem).PluginElement.DataContext as ISettlementScheduleSection;
// Initialize currently selected section/plugin
if (pluginViewModel != null && !pluginViewModel.DataLoaded)
await pluginViewModel.LoadData();
}
- 插件的方法:
LoadData
每个插件都拥有自己的crud操作服务类实例。这些一次性服务类包含db-Context的实例。
小例子:
public override async Task LoadData()
{
// ...
Costs = await costsService.GetCosts(SubProjectId, UnitTypeId);
// ...
}
db-context(public class CostsService : ServiceBase
{
public CostsService()
: base()
{ }
public CostsService(MyDbContext db)
{
this.Context = db ?? throw new ArgumentNullException(nameof(db));
}
public async Task<IEnumerable<CostsDefinition>> GetCosts(Guid subProjectId, Guid unitTypeId)
{
return await Context.CostsDefinitions.Where(cd => cd.SubProjectId == subProjectId
&& cd.UnitTypeId == unitTypeId)
.OrderBy(cd => cd.Year).ThenBy(cd => cd.Month)
.ToListAsync();
}
}
- property)不是静态的,也不在插件之间共享。
问题:有时,但并非总是如此,这种初始化插件的方式导致Context
WPF似乎多次引发该事件。对我而言,目前尚不清楚它会在什么情况下被提出来。 当System.NotSupportedException: A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe.
首次绑定到ICollectionView
并且第一项被自动选中时?当我加载并插入更多的插件到源集合? ... 的
错误的频率:在这种情况下,时间似乎起作用。在没有调试或在不同网络条件下运行时,该错误的频率会发生变化。
解决方法尝试:我尝试通过bool标志过滤TabControl
- 处理程序的其他/不必要的调用,但在所有情况下都没有帮助。我只是减少了一点频率。我还试图将对PluginsCv的每次访问委托给Dispatcher,但它没有帮助。另外,当pluginsCv为null时,我写了调试信息。我只获得了一次该信息,因此CurrentChanged
- 事件也必须只订阅一次。
例外详情:
例外来源:“EntityFramework”
堆栈跟踪:
CurrentChanged
有什么想法可以解决这个问题吗?
答案 0 :(得分:0)
典型的比赛条件。
让我们考虑一下这个场景
Thread1 - Access PluginsCv
PluginsCv为null,因此注册CurrentChanged
Thread2 - Access PluginsCv
PluginsCv为null所以注册CurrentChanged
...后来 CurrentChanged引发
Thread1 - 等待LoadCurrentSection
Thread2 - 等待LoadCurrentSection
Thread2“等等!Thread1也正在执行LoadCurrentSection并且尚未完成!错误!错误!”