Search code examples
c#wpfmefdisposeidisposable

MEF ExportFactory does not dispose of IDisposable ViewModels


I'm using MEF as the IoC container in WPF with Caliburn.Micro framework. The MEF container resides in the Bootstrapper. Bootstrapper has the method:

protected override void OnStartup(object sender, StartupEventArgs e)
{            
   DisplayRootViewFor<ViewModels.Main.MainViewModel>();
}

Now, the MainViewModel has this:

private IEnumerable<ExportFactory<Screen>> _screenList { get; set; }        

[ImportingConstructor]
public MainViewModel(IEventAggregator eventAggregator, [ImportMany] IEnumerable<ExportFactory<Screen>> screenList)
{
    _screenList = screenList;
    eventAggregator = eventAggregator;
    _eventAggregator.Subscribe(this);        
}

The class basically has an IEnumerable of ExportFactory. A Screen is basically a ViewModel that inherits from VMBase which in turn inherits from Screen. Now, some of those ViewModel use a Unit of Work, which, in turn, has a DbContext injected into it via the constructor.

So basically, the dependency chain is like this: DbContext -> UnitOfWork (IDisposable) -> ViewModelBase(IDisposable) -> ViewModels. These ViewModel are instantiated using the ExportFactory in the MainViewModel.

The MainViewModel is subscribed to the eventAggregator, and Handler of MainViewModel calls the ExportFactory to create a new instance of a ViewModel as necessary. The problem is, when that ViewModel is closed, the DbContext is not properly disposed of.

This is the Handle method:

public void Handle(Type message)
{            
   DeactivateItem(ActiveItem, true);           
   ActivateItem(screenList.FirstOrDefault(c => c.CreateExport().Value.GetType() == message).CreateExport().Value);           
}

This creates new instances of DbContext, but doesn't call Dispose() of ViewModelBase ever, as I verified using breakpoints.

I tried changing the Handle method to call Dispose() like this:

DeactivateItem(ActiveItem, true);
_currentLifetimeContext?.Dispose();
_currentLifetimeContext = _screenList.FirstOrDefault(c => c.CreateExport().Value.GetType() == message).CreateExport();
ActivateItem(_currentLifetimeContext.Value);

but it throws an error "The operation cannot be completed because the DbContext has been disposed."

How can I properly dispose of the DbContext in my application?


Solution

  • It is almost impossible to state why your code is failing without testing it in its entirety. However, in my limited experience with MEF, I have found that most of the disposal problems are created when the exports are not explicitly marked to be NonShared. This is one of the annoyances of MEF, where it treats all components to be singleton by default. My advice is to explicitly mark each and every export of yours to be NonShared, unless you want a singleton. Not just the ViewModels. Let us know if it works.