2

Basically, is there an easy way to dispose of the imports that are created by an ExportFactory<T>? The reason I ask is because the exports usually contain a reference to something that is still around, such as the EventAggregator. I don't want to run into the issue where I'm creating hundreds of these and leaving them laying around when they are not necessary.

I noticed that when I create the objects I get back a ExportLifetimeContext<T> which carries a Dispose with it. But, I don't want to pass back an ExportLifetimeContext to my ViewModels requesting copies of the ViewModel, hence I pass back the Value. (return Factory.Single(v => v.Metadata.Name.Equals(name)).CreateExport().Value;)

myermian
  • 31,823
  • 24
  • 123
  • 215

1 Answers1

0

When you call Dispose on an ExportLifetimeContext<T> it will call dispose on any NonShared part involved in the creation of T. It won't dispose of any Shared components. This is a safe behaviour, because if the NonShared parts were instantiated purely to satisfy the imports for T, then they can safely be disposed as they won't be used by any other imports.

The only other way I think you could achieve it, would be to customise the Dispose method to chain the dispose call to any other member properties you import with, e.g.:

[Export(typeof(IFoo))]
public class Foo : IFoo, IDisposable
{
    [Import]
    public IBar Bar { get; set; }

    public void Dispose()
    {
        var barDisposable = Bar as IDisposable;
        if (barDisposable != null) 
            barDisposable.Dispose();
    }
}

But as your type has no visibility of whether the imported instance of IBar is Shared or NonShared you run the risk of disposing of shared components.

I think hanging onto the instance of ExportedLifetimeContext<T> is the only safe way of achieving what you want.

Not sure if this helps, feels like unnecessary wrapping, but could you possibly:

public class ExportWrapper<T> : IDisposable
{
  private readonly ExportLifetimeContext<T> context;

  public ExportWrapper<T>(ExportLifetimeContext<T> context)
  {
    this.context = context;
  }

  public T Value 
  {
    get { return context.Value; }
  }

  public void Dispose()
  {
    context.Dispose();
  }

  public static implicit operator T(ExportWrapper<T> wrapper)
  {
    return wrapper.Value;
  }

  public static implicit operator ExportWrapper<T>(ExportLifetimeContext<T> context)
  {
    return new ExportWrapper<T>(context);
  }
}

Which you could possibly:

[Import(typeof(IBar))]
public ExportFactory<IBar> BarFactory { get; set; }

public void DoSomethingWithBar()
{
  using (ExportWrapper<IBar> wrapper = BarFactory.CreateExport())
  {
    IBar value = wrapper;
    // Do something with IBar;
    // IBar and NonShared imports will be disposed of after this call finishes.
  }
}

Feels a bit dirty...

Matthew Abbott
  • 60,571
  • 9
  • 104
  • 129
  • You beat me to answering my own question. I actually created an abstract `ExportFactoryController` and a IExportFactoryController` that keeps a dictionary of part's created and the `ExportLifetimeContext`s. Then, I expose Dispose(T part) which finds the context from the dictionary and disposes of it/removes it from the dictionary. I also have a DisposeAll() which disposes all contexts stored in the dictionary and clears out the dictionary cache. Only annoying thing is my `ImportMany` is bland: `[ImportMany] IEnumerable> Factories;` – myermian Jul 01 '11 at 21:14