StructureMap+MEF+MVC

Jun 11, 2009 at 3:04 AM
Edited Jun 11, 2009 at 3:20 AM

Okay here is my problem,.. I have a MVC application based upon code found here

http://blogs.msdn.com/hammett/archive/2009/04/23/mef-and-asp-net-mvc-sample.aspx 

I added a database and a repository and service project(s) which I need to use structure map to allow my plugins have access to the database uncoupled along with the main MVC application

If you would like to look at what I did check out

http://www.danswatik.com/MVCandMEF.zip and http://www.danswatik.com/ForumDB.Bak (SQLEXPRESS 2008 backup - Database is named Forum)
I followed the same structure that Rob Conery did with his store front like this : http://weblogs.asp.net/shijuvarghese/archive/2008/10/10/asp-net-mvc-tip-dependency-injection-with-structuremap.aspx, only problem is the structuremapcontroller factory I couldn't do because I already have a controllerfactory that is doing the extensibility.

However I am meeting with little success I keep getting this

The composition produced a single composition error. The root cause is provided below. Review the CompositionException.Errors property for more detailed information.

1) No exports were found that match the constraint '((exportDefinition.ContractName = "MvcWithMEF.Services.IForumService") && (exportDefinition.Metadata.ContainsKey("ExportTypeIdentity") && "MvcWithMEF.Services.IForumService".Equals(exportDefinition.Metadata.get_Item("ExportTypeIdentity"))))'.

Resulting in: Cannot set import 'ForumExtension.Presentation.Controllers.ForumController..ctor (Parameter="forumService", ContractName="MvcWithMEF.Services.IForumService")' on part 'ForumExtension.Presentation.Controllers.ForumController'.
Element: ForumExtension.Presentation.Controllers.ForumController..ctor (Parameter="forumService", ContractName="MvcWithMEF.Services.IForumService") --> ForumExtension.Presentation.Controllers.ForumController --> TypeCatalog (Types="ForumExtension.Presentation.Controllers.ForumController")

Resulting in: Cannot get export 'ForumExtension.Presentation.Controllers.ForumController (ContractName="System.Web.Mvc.IController")' from part 'ForumExtension.Presentation.Controllers.ForumController'.
Element: ForumExtension.Presentation.Controllers.ForumController (ContractName="System.Web.Mvc.IController") --> ForumExtension.Presentation.Controllers.ForumController --> TypeCatalog (Types="ForumExtension.Presentation.Controllers.ForumController")

Anyhow what am I doing wrong?

 

Jun 15, 2009 at 2:24 PM

Could someone please look into this?

Developer
Jun 16, 2009 at 3:25 AM

Your source code zip link doesn't work for me. The "Export not found" error can occur for a lot of reasons. Can you post the code that you expect to export contract "MvcWithMEF.Services.IFourmService". If you are exposing that with a custom catalog then might also be helpful if you share the code for that as well.

Jun 16, 2009 at 4:10 AM

Oh doh apparently I didn't realize the links were wrong

http://mysql.netpmg.com/MVCandMEF.zip

http://mysql.netpmg.com/forumdb.zip

Rename the foumdb.zip to *.bak it's a SQLEXPRESS 2008 DB backup.

Here are the corrected links.... and here you'll need to change the forumController code to this

namespace ForumExtension.Controllers
{
    [Export, PartCreationPolicy(CreationPolicy.NonShared)]
    public class ForumController : BasePublicController
    {
        IForumService _forumService; // = ObjectFactory.GetInstance<IForumService>() ;
       // private const string ViewRoot = "~/VModules/Forums/";
       // private const string ViewRoot = "~/App_Resource/ForumExtension.dll//Views/Forum/";

        [ImportingConstructor]
        public ForumController(IForumService forumService) {
            _forumService = forumService;
        }

        public ActionResult Index()
        {
            var data = new ForumData{ LastActivity = _forumService.GetLastActivity()};
            ViewData["forums"] = data;
      
     
            return View(data);
        }
    }

Developer
Jun 16, 2009 at 5:02 AM

The problem lies here:

    [Export("ForumService",typeof(IForumService))]
    public class ForumService:IForumService {
        #region IForumService Members

        IForumRepository _repository = null;
        [ImportingConstructor]
        public ForumService(IForumRepository repository) {
            _repository = repository;

            if (_repository == null)
                throw new InvalidOperationException("Repository can not be null");
        }

        public IList<ForumActivitiy> GetLastActivity() {
            return _repository.GetLastActivity().ToList();
        }

        #endregion
    }

Your constructor import is implicitly doing an [Import(typeof(IForumService))] which is the error message you are seeing but your ForumService is doing [Export("ForumService", typeof(IForumService)] which has a contract name of "ForumService" not "MvcWithMEF.Services.IForumService". To fix this change your ForumService to use [Export(typeof(IForumService))].

Jun 16, 2009 at 5:27 AM
Nadda still the same error or maybe it's different looks the same to me
 

The composition produced a single composition error. The root cause is provided below. Review the CompositionException.Errors property for more detailed information.

1) No exports were found that match the constraint '((exportDefinition.ContractName = "MvcWithMEF.Services.IForumService") && (exportDefinition.Metadata.ContainsKey("ExportTypeIdentity") && "MvcWithMEF.Services.IForumService".Equals(exportDefinition.Metadata.get_Item("ExportTypeIdentity"))))'.

Resulting in: Cannot set import 'ForumExtension.Controllers.ForumController..ctor (Parameter="forumService", ContractName="MvcWithMEF.Services.IForumService")' on part 'ForumExtension.Controllers.ForumController'.
Element: ForumExtension.Controllers.ForumController..ctor (Parameter="forumService", ContractName="MvcWithMEF.Services.IForumService") --> ForumExtension.Controllers.ForumController --> TypeCatalog (Types="ForumExtension.Controllers.ForumController")

Resulting in: Cannot get export 'ForumExtension.Controllers.ForumController (ContractName="System.Web.Mvc.IController")' from part 'ForumExtension.Controllers.ForumController'.
Element: ForumExtension.Controllers.ForumController (ContractName="System.Web.Mvc.IController") --> ForumExtension.Controllers.ForumController --> TypeCatalog (Types="ForumExtension.Controllers.ForumController")

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.ComponentModel.Composition.CompositionException: The composition produced a single composition error. The root cause is provided below. Review the CompositionException.Errors property for more detailed information.

1) No exports were found that match the constraint '((exportDefinition.ContractName = "MvcWithMEF.Services.IForumService") && (exportDefinition.Metadata.ContainsKey("ExportTypeIdentity") && "MvcWithMEF.Services.IForumService".Equals(exportDefinition.Metadata.get_Item("ExportTypeIdentity"))))'.

Resulting in: Cannot set import 'ForumExtension.Controllers.ForumController..ctor (Parameter="forumService", ContractName="MvcWithMEF.Services.IForumService")' on part 'ForumExtension.Controllers.ForumController'.
Element: ForumExtension.Controllers.ForumController..ctor (Parameter="forumService", ContractName="MvcWithMEF.Services.IForumService") --> ForumExtension.Controllers.ForumController --> TypeCatalog (Types="ForumExtension.Controllers.ForumController")

Resulting in: Cannot get export 'ForumExtension.Controllers.ForumController (ContractName="System.Web.Mvc.IController")' from part 'ForumExtension.Controllers.ForumController'.
Element: ForumExtension.Controllers.ForumController (ContractName="System.Web.Mvc.IController") --> ForumExtension.Controllers.ForumController --> TypeCatalog (Types="ForumExtension.Controllers.ForumController")
 


 
On Tue, Jun 16, 2009 at 12:02 AM, weshaggard <notifications@codeplex.com> wrote:

From: weshaggard

The problem lies here:

    [Export("ForumService",typeof(IForumService))]
    public class ForumService:IForumService {
        #region IForumService Members

        IForumRepository _repository = null;
        [ImportingConstructor]
        public ForumService(IForumRepository repository) {
            _repository = repository;

            if (_repository == null)
                throw new InvalidOperationException("Repository can not be null");
        }

        public IList<ForumActivitiy> GetLastActivity() {
            return _repository.GetLastActivity().ToList();
        }

        #endregion
    }

Your constructor import is implicitly doing an [Import(typeof(IForumService))] which is the error message you are seeing but your ForumService is doing [Export("ForumService", typeof(IForumService)] which has a contract name of "ForumService" not "MvcWithMEF.Services.IForumService". To fix this change your ForumService to use [Export(typeof(IForumService))].

Read the full discussion online.

To add a post to this discussion, reply to this email (MEF@discussions.codeplex.com)

To start a new discussion for this project, email MEF@discussions.codeplex.com

You are receiving this email because you subscribed to this discussion on CodePlex. You can unsubscribe on codePlex.com.

Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at codeplex.com




--
Dan Swatik
http://www.danswatik.com
Jun 18, 2009 at 1:45 PM

I changed it like you said but I still get the error as stated in my reply above... please help

Jun 19, 2009 at 5:23 PM
Hey there
I changed it like you said but I still get the error... not sure what the deal is...

On Tue, Jun 16, 2009 at 12:02 AM, weshaggard <notifications@codeplex.com> wrote:

From: weshaggard

The problem lies here:

    [Export("ForumService",typeof(IForumService))]
    public class ForumService:IForumService {
        #region IForumService Members

        IForumRepository _repository = null;
        [ImportingConstructor]
        public ForumService(IForumRepository repository) {
            _repository = repository;

            if (_repository == null)
                throw new InvalidOperationException("Repository can not be null");
        }

        public IList<ForumActivitiy> GetLastActivity() {
            return _repository.GetLastActivity().ToList();
        }

        #endregion
    }

Your constructor import is implicitly doing an [Import(typeof(IForumService))] which is the error message you are seeing but your ForumService is doing [Export("ForumService", typeof(IForumService)] which has a contract name of "ForumService" not "MvcWithMEF.Services.IForumService". To fix this change your ForumService to use [Export(typeof(IForumService))].

Read the full discussion online.

To add a post to this discussion, reply to this email (MEF@discussions.codeplex.com)

To start a new discussion for this project, email MEF@discussions.codeplex.com

You are receiving this email because you subscribed to this discussion on CodePlex. You can unsubscribe on codePlex.com.

Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at codeplex.com




--
Dan Swatik
http://www.danswatik.com
Developer
Jun 20, 2009 at 2:31 AM

Sorry for the delayed response. I usually get pretty busy during the week.

Have you made sure that the assembly that the ForumService is actually loaded into the catalog? It looks like you have too copies of ForumService one in MVCandMEF\MvcWithMEF\Extensions\ForumExtension\Domain\Services which is commented out and one in MVCandMEF\MvcWithMEF\MvcWithMEF.Services\Forum which is I'm assuming is the one you fixed the Export on. So the question would be is MvcWithMef.Services.dll actually getting loaded into the catalog?

If I were to guess from looking at your Catalog setup below I don't see that MvcWithMEf.Services.dll is being loaded.

           var catalog = new CatalogBuilder().
                ForAssembly(typeof(IModuleInstaller).Assembly).
                ForMvcAssembly(Assembly.GetExecutingAssembly()).
                ForMvcAssembliesInDirectory(HttpRuntime.BinDirectory, "*Extension.dll"). // Won't work in Partial trust
                Build();

 

Jun 20, 2009 at 2:51 AM
Ok maybe stupid question... Now why would it need to be in the catalog it's not a plugin it's a IoC

On Fri, Jun 19, 2009 at 9:31 PM, weshaggard <notifications@codeplex.com> wrote:

From: weshaggard

Sorry for the delayed response. I usually get pretty busy during the week.

Have you made sure that the assembly that the ForumService is actually loaded into the catalog? It looks like you have too copies of ForumService one in MVCandMEF\MvcWithMEF\Extensions\ForumExtension\Domain\Services which is commented out and one in MVCandMEF\MvcWithMEF\MvcWithMEF.Services\Forum which is I'm assuming is the one you fixed the Export on. So the question would be is MvcWithMef.Services.dll actually getting loaded into the catalog?

If I were to guess from looking at your Catalog setup below I don't see that MvcWithMEf.Services.dll is being loaded.

           var catalog = new CatalogBuilder().
                ForAssembly(typeof(IModuleInstaller).Assembly).
                ForMvcAssembly(Assembly.GetExecutingAssembly()).
                ForMvcAssembliesInDirectory(HttpRuntime.BinDirectory, "*Extension.dll"). // Won't work in Partial trust
                Build();

 

Read the full discussion online.

To add a post to this discussion, reply to this email (MEF@discussions.codeplex.com)

To start a new discussion for this project, email MEF@discussions.codeplex.com

You are receiving this email because you subscribed to this discussion on CodePlex. You can unsubscribe on codePlex.com.

Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at codeplex.com




--
Dan Swatik
http://www.danswatik.com
Jun 20, 2009 at 3:10 AM
Ok I don't get it. what am I not understanding? You were right i added the IForumService to the catalog then I got a new error which basically said the same thing for IForumRepository then it wanted me to add the DBContext that IForumRepository uses... geez
 
Why does all that need to be exported along with my plug in... its internal to the plug in ... something isn't right

On Fri, Jun 19, 2009 at 9:51 PM, dswatik <notifications@codeplex.com> wrote:

From: dswatik

Ok maybe stupid question... Now why would it need to be in the catalog it's not a plugin it's a IoC

On Fri, Jun 19, 2009 at 9:31 PM, weshaggard <notifications@codeplex.com> wrote:

From: weshaggard

Sorry for the delayed response. I usually get pretty busy during the week.

Have you made sure that the assembly that the ForumService is actually loaded into the catalog? It looks like you have too copies of ForumService one in MVCandMEF\MvcWithMEF\Extensions\ForumExtension\Domain\Services which is commented out and one in MVCandMEF\MvcWithMEF\MvcWithMEF.Services\Forum which is I'm assuming is the one you fixed the Export on. So the question would be is MvcWithMef.Services.dll actually getting loaded into the catalog?

If I were to guess from looking at your Catalog setup below I don't see that MvcWithMEf.Services.dll is being loaded.

           var catalog = new CatalogBuilder().
                ForAssembly(typeof(IModuleInstaller).Assembly).
                ForMvcAssembly(Assembly.GetExecutingAssembly()).
                ForMvcAssembliesInDirectory(HttpRuntime.BinDirectory, "*Extension.dll"). // Won't work in Partial trust
                Build();

 

Read the full discussion online.

To add a post to this discussion, reply to this email (MEF@discussions.codeplex.com)

To start a new discussion for this project, email MEF@discussions.codeplex.com

You are receiving this email because you subscribed to this discussion on CodePlex. You can unsubscribe on codePlex.com.

Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at codeplex.com




--
Dan Swatik
http://www.danswatik.com

Read the full discussion online.

To add a post to this discussion, reply to this email (MEF@discussions.codeplex.com)

To start a new discussion for this project, email MEF@discussions.codeplex.com

You are receiving this email because you subscribed to this discussion on CodePlex. You can unsubscribe on codePlex.com.

Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at codeplex.com




--
Dan Swatik
http://www.danswatik.com
Developer
Jun 20, 2009 at 4:01 AM

MEF works by finding Exports that are defined in a Catalog. In order for MEF to find IForumService or IForumRepository they need to be registered in the Catalog as Exports. Even if you are using traditional IOC containers you would still need to register an implementation type for a given interface so that it could be resolved. In MEF that registration is the Catalog. However instead of manually registering individual interfaces to concrete types you simply mark your implementations with Exports and place either the type or more commonly the Assembly where the type lives into a Catalog.

One common approach is to have an Extensions directory where you create a DirectoryCatalog for it and it will then in turn catalog all the assemblies in that directory. As you point out in comments this may have some issues in web applications because of security but it can generally be worked around if needed (namely by registering a specific assembly list into a catalog, instead of traversing the directory).

The reason you are seeing a chain of dependencies that you marked the constructors of the Exports with the ImportingConstructor attribute which in turn makes the constructor parameters Imports and as such MEF tries to find matching Exports for those Imports in order to construct the entire object graph.

For your scenario just ensure that all the assemblies which contain types marked with Export find their way into a Catalog, either via the AssemblyCatalog directly or via a DirectoryCatalog.