Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Caching MEF Components

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).

like image 200
Henka Programmer Avatar asked Jul 04 '16 13:07

Henka Programmer


1 Answers

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

like image 159
lokusking Avatar answered Sep 30 '22 15:09

lokusking