6

Is there a way to cache MEF components graph per-application startup (WPF), like the MAF do to avoid discovering the directories and constructing the components graph every application startup. in order to speed up my application startup. MAF uses AddinsStore to store all addins, when new addin discovred the Store rebuilt and saved again. it is possible to do that with Modular application designed with MEF?

EDIT:

in My Project Architecture I have Extension, Modules and the Managed Services So i have different Exports like(IExtension, IModule, IManagedService), and i handling the start dependencies of all components, what i want precisely ex(The Extensions Directory) contains many dlls and it is may be not all dlls contains an (exports/Imports) because some of the dlls just references for some Extensions. so the default discovering behavior of MEF is searching for the exports/Imports in all assemblies in the Extension Directory, but i want to modify this behavior by looking at the first time all dlls and catch the types and their names and dlls to use them in the next startup time. from the catch directly load components(Exports) so the MEF will be know the available components and their places without loading and searching the dlls. it is seems like a dictionary of Exports and their Places and dependencies to get the instance directly from its places(dll).

Henka Programmer
  • 680
  • 6
  • 23
  • I quite dont understand that question... I've never done MAF before, but a lot of MEF. Why dont you simply move all MEF-Components to the same folder and directly load it from there? After reading [this](http://stackoverflow.com/questions/835182/choosing-between-mef-and-maf-system-addin), MAF seems like a unnecessary complicated way to get things done – lokusking Jul 13 '16 at 07:51
  • No i can't move all assemblies into one directory. I am using Directory catalogs and i have the flowing structure: (Extensions, Modules, Managed Services, SDK) Folders, the loading order important. – Henka Programmer Jul 14 '16 at 11:52
  • You can set the Load-Order too in Mef. It requires a bit of ugly code, but it works. If you are interested in this how to, let me know and i'll post an answer – lokusking Jul 14 '16 at 11:55
  • Yes pot the answer its may helps. thanks in advance. – Henka Programmer Jul 14 '16 at 14:19

1 Answers1

1

I dont know if this will help you by 100%, but with this code, im controlling the load-order of my Modules.

If you can control your load-order, you might be able to put all your *.dll's in the same Folder and save some time, finding them in subfolders:

The key to this is the usage of this additional Attribute: [ExportMetadata("Order", 1)]

Then your Plugin should look like this :

 [Export(typeof(YourContract))]
  [ExportMetadata("Order", 1)]
  public class YourPlugin: YourContract{}

To get things Loaded in the right order, you will need something like this:

Interface:

public interface IOrderMetadata {
    [DefaultValue(int.MaxValue)]
    int Order {
      get;
    }
  }

AdaptingCollection:

 public class AdaptingCollection<T, M> : ICollection<Lazy<T, M>>, INotifyCollectionChanged {
    /// <summary>
    /// Constructor</summary>
    public AdaptingCollection()
        : this(null) {
    }

    /// <summary>
    /// Constructor</summary>
    /// <param name="adaptor">Function to apply to items in the collection</param>
    public AdaptingCollection(Func<IEnumerable<Lazy<T, M>>, IEnumerable<Lazy<T, M>>> adaptor) {
      this._mAdaptor = adaptor;
    }

    /// <summary>
    /// CollectionChanged event for INotifyCollectionChanged</summary>
    public event NotifyCollectionChangedEventHandler CollectionChanged;

    /// <summary>
    /// Force the adaptor function to be run again</summary>
    public void ReapplyAdaptor() {
      if (this._mAdaptedItems == null) return;
      this._mAdaptedItems = null;
      this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }

    #region ICollection Implementation

    /// <summary>
    /// Returns whether the item is present in the collection</summary>
    /// <remarks>Accessors work directly against adapted collection</remarks>
    /// <param name="item">Item to look for</param>
    /// <returns>True if the item is in the collection</returns>
    public bool Contains(Lazy<T, M> item) {
      return this.AdaptedItems.Contains(item);
    }

    /// <summary>
    /// Copies the entire list to a one-dimensional array, starting at the specified index of the target array</summary>
    /// <remarks>Accessors work directly against adapted collection</remarks>
    /// <param name="array">The target array</param>
    /// <param name="arrayIndex">The starting index</param>
    public void CopyTo(Lazy<T, M>[] array, int arrayIndex) {
      this.AdaptedItems.CopyTo(array, arrayIndex);
    }

    /// <summary>
    /// Gets the number of items in the collection</summary>
    /// <remarks>Accessors work directly against adapted collection</remarks>
    public int Count => this.AdaptedItems.Count;

    /// <summary>
    /// Gets whether the collection is read only.</summary>
    /// <remarks>Accessors work directly against adapted collection</remarks>
    public bool IsReadOnly => false;

    /// <summary>
    /// Gets an enumerator for the collection</summary>
    /// <remarks>Accessors work directly against adapted collection</remarks>
    /// <returns>The IEnumerator</returns>
    public IEnumerator<Lazy<T, M>> GetEnumerator() {
      return this.AdaptedItems.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator() {
      return this.GetEnumerator();
    }

    /// <summary>
    /// Add an item to the collection</summary>
    /// <remarks>Mutation methods work against complete collection and then force
    /// a reset of the adapted collection</remarks>
    /// <param name="item">The item to add</param>
    public void Add(Lazy<T, M> item) {
      this._mAllItems.Add(item);
      this.ReapplyAdaptor();
    }

    /// <summary>
    /// Clear all items from the collection</summary>
    /// <remarks>Mutation methods work against complete collection and then force
    /// a reset of the adapted collection</remarks>
    public void Clear() {
      this._mAllItems.Clear();
      this.ReapplyAdaptor();
    }

    /// <summary>
    /// Remove an item from the collection</summary>
    /// <remarks>Mutation methods work against complete collection and then force
    /// a reset of the adapted collection</remarks>
    /// <param name="item">The item to remove</param>
    /// <returns>True if the item was found, otherwise false</returns>
    public bool Remove(Lazy<T, M> item) {
      bool removed = this._mAllItems.Remove(item);
      this.ReapplyAdaptor();
      return removed;
    }

    #endregion

    /// <summary>
    /// Invoke the adaptor function on the collection</summary>
    /// <param name="collection">The collection to adapt</param>
    /// <returns>The adapted collection</returns>
    protected virtual IEnumerable<Lazy<T, M>> Adapt(IEnumerable<Lazy<T, M>> collection) {
      if (this._mAdaptor != null) {
        return this._mAdaptor.Invoke(collection);
      }

      return collection;
    }

    /// <summary>
    /// Fire the CollectionChanged event</summary>
    /// <param name="e">Event args</param>
    protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e) {
      this.CollectionChanged?.Invoke(this, e);
    }

    private List<Lazy<T, M>> AdaptedItems => this._mAdaptedItems ?? (this._mAdaptedItems = this.Adapt(this._mAllItems).ToList());

    private readonly List<Lazy<T, M>> _mAllItems = new List<Lazy<T, M>>();
    private readonly Func<IEnumerable<Lazy<T, M>>, IEnumerable<Lazy<T, M>>> _mAdaptor;
    private List<Lazy<T, M>> _mAdaptedItems;

  }

OderingCollection

public class OrderingCollection<T, M> : AdaptingCollection<T, M> {
    /// <summary>
    /// Constructor</summary>
    /// <param name="keySelector">Key selector function</param>
    /// <param name="descending">True to sort in descending order</param>
    public OrderingCollection(Func<Lazy<T, M>, object> keySelector, bool descending = false)
        : base(e => descending ? e.OrderByDescending(keySelector) : e.OrderBy(keySelector)) {
    }
  }

Usage

[ImportMany(typeof(YourContract), AllowRecomposition = true)] 
    internal OrderingCollection<YourContract, IOrderMetadata> Plugins{
      get; private set;
    }

In your Constructor:

this.Plugins= new OrderingCollection<ITemplateMapper, IOrderMetadata>(
                           lazyRule => lazyRule.Metadata.Order);

My loading-code (Might differ from yours):

private void LoadModules() {
      var aggregateCatalog = new AggregateCatalog();
      aggregateCatalog.Catalogs.Add(new DirectoryCatalog(".", "*.Plugin.*.dll"));
      var container = new CompositionContainer(aggregateCatalog);
      container.ComposeParts(this);     
    }

I hope this might help you to get rid of MAF

lokusking
  • 7,000
  • 13
  • 38
  • 51
  • It is good idea but didn't help me to get my aims. I added more description in my question. have a look. – Henka Programmer Jul 15 '16 at 08:58
  • According to your edit, you could set up a small Database containing the installed plugins and their paths. After this you should be able to load them directly without crawling your Folders – lokusking Jul 15 '16 at 09:18
  • Am i right if i said: MEF when Composing a part it will try to search its Imports and Exports and dependencies then it will create the instance? – Henka Programmer Jul 15 '16 at 09:22
  • AFAIK this is correct. It never caused any performance-impact on my Applications. – lokusking Jul 15 '16 at 09:24
  • So what i want to do is: when MEF lookup all the assemblies and compose the graph of the import and export parts i want to save this graph in some way and provide it in the next time to MEF as dedicated graph. – Henka Programmer Jul 15 '16 at 09:34
  • Some interesting stuff [here](http://stackoverflow.com/questions/14274475/are-mef-exports-cached-or-discovering-every-time-on-request) – lokusking Jul 15 '16 at 10:03