kingrichard2005
kingrichard2005

Reputation: 7269

Stumped on MVC MEF application

I'm trying to implement the the Microsoft Extensibility Framework (MEF) into a sample MVC-based web app. I'm using the SimpleCalculator example solution on the MEF Overview page. My goal is an application that can dynamically load a DLL extension from another project in order to extend the capabilities of the Model, essentially I want the MVC-Application to be a framework for other extensions to plug-into. First my setup:

Project 1 (MVC-Application, MEF Component Host):

I decorate the elements in my Model as follows:

using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;

namespace ExpressionParserPOC.Models
{

    public class ExpressionModel
      {
            private CompositionContainer _container;

            [ImportMany]
            IEnumerable<Lazy<IExtensions,IExtensionName>> extensions { get; set; }

        public ExpressionModel()
        {
          var catalog = new AggregateCatalog();
          catalog.Catalogs.Add(new DirectoryCatalog("C:\\local_visual_studio\\ExpressionParserPOC\\ExpressionParserPOC\\Extensions"));

          //Create the CompositionContainer with the parts in the catalog
          _container = new CompositionContainer(catalog);
          _container.ComposeParts(_container);

          //TEST: Can we access the extensions?
          if (extensions != null)//<--NULL WHEN CONSTRUCTOR IS CALLED WHY? Expected GetMatrix() Function from DLL to be exposed
          {
            foreach (Lazy<IExtensions, IExtensionName> i in extensions)
            {
              Lazy<IExtensions, IExtensionName> foo = i;
            }
          }

        }

      }

  public interface IExtensions
  {
    double[][] GetMatrix(string matrix);
  }

  public interface IExtensionName
  {
    Char Symbol { get; }
  }

 }

To test right now, I'm just calling the constructor for the Model from the Controller:

namespace ExpressionParserPOC.Controllers
{
  public class HomeController : Controller
  {
    public ActionResult Index()
    {
      ViewBag.Message = "Enter an Expression for Octave to evaluate";

      //Instantiate an ExpressionModel composed of extensible parts
      var model = new ExpressionModel();
      return View(model);
    }

  }
}

Project 2 (DLL Project, MEF Component):

//PROJECT BUILD OUTPUTS TO THE EXTENSIONS DIRECTORY IN PROJECT 1 ABOVE
using System.ComponentModel.Composition;

namespace MyExtensions
{
  //Project 1 already added as a reference 
  [Export(typeof(ExpressionParserPOC.Models.IExtensions))]
  [ExportMetadata("Symbol", 'o')]
  public class Octave : MyExtensions.IExtensions
  {

      //Other class properties, constructors, functions, etc.

     #region FUNCTIONS
     public double[][] GetMatrix(string matrix) 
     {
         double[][] mat = new double[][];
         //Do Stuff
         return mat;
     }
     #endregion

  }

  public interface IExtensions : ExpressionParserPOC.Models.IExtensions
  {

  }

}

Problem is that the the extensions List in the host MVC application project never gets filled when I call the Model constructor from the Controller in that same project. I'm new to MEF development and am not sure what I'm doing wrong, is there something extra I need to consider since I'm working with an MVC application? Why won't the Project 1 extensions list get filled? Are my interface definitions wrong? The sample calculator application is a simple command line project and that seems to work fine, the only difference I see is that the external DLL project is in the same solution space, whereas with my example the solution spaces are independent.

Upvotes: 2

Views: 345

Answers (1)

famousgarkin
famousgarkin

Reputation: 14116

A trivial mistake you have there in the ExpressionModel constructor. You are composing the container itself :)

_container.ComposeParts(_container);

The problem should be apparent instantly, because if the composition of the ExpressionModel actually ever happened, even if no extensions were discovered, the extensions property would be initialized with 0 items, but it wouldn't be null.

You need to compose the current model instance like this:

_container.ComposeParts(this);

Everything else seems to be alright and should be working fine after this correction.

Upvotes: 1

Related Questions