MEF and ASP.NET MVC

I want to use MEF with asp.net mvc. I wrote the following factory controller:

public class MefControllerFactory : DefaultControllerFactory { private CompositionContainer _Container; public MefControllerFactory(Assembly assembly) { _Container = new CompositionContainer(new AssemblyCatalog(assembly)); } protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) { if (controllerType != null) { var controllers = _Container.GetExports<IController>(); var controllerExport = controllers.Where(x => x.Value.GetType() == controllerType).FirstOrDefault(); if (controllerExport == null) { return base.GetControllerInstance(requestContext, controllerType); } return controllerExport.Value; } else { throw new HttpException((Int32)HttpStatusCode.NotFound, String.Format( "The controller for path '{0}' could not be found or it does not implement IController.", requestContext.HttpContext.Request.Path ) ); } } } 

In Global.asax.cs I install my factory controller:

 protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RegisterRoutes(RouteTable.Routes); ControllerBuilder.Current.SetControllerFactory(new MefControllerFactory.MefControllerFactory(Assembly.GetExecutingAssembly())); } 

I have an area:

 [Export(typeof(IController))] [PartCreationPolicy(CreationPolicy.NonShared)] public class HomeController : Controller { private readonly IArticleService _articleService; [ImportingConstructor] public HomeController(IArticleService articleService) { _articleService = articleService; } // // GET: /Articles/Home/ public ActionResult Index() { Article article = _articleService.GetById(55); return View(article); } } 

IArticleService is an interface.

There is a class that implements IArticleService and exports it.

It works.

Is that all I need to work with MEF?

How can I skip installing PartCreationPolicy and ImportingConstructor for the controller?

I want to set my dependencies using a constructor.

When the PartCreationPolicy missing, I get the following exception:

One instance of the controller "MvcApplication4.Areas.Articles.Controllers.HomeController" cannot be used to process multiple requests. If you are using a custom factory controller, make sure that it creates a new controller instance for each request.

+7
asp.net-mvc mef
source share
4 answers

I decided to return to Unity.

I created a custom attribute instead of MEF ExportAttribute

 [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] public class ImplementsAttribute : Attribute { public ImplementsAttribute(Type contractType) { ContractType = contractType; } public Type ContractType { get; private set; } } 

Example:

 [Implements(typeof(ICustomerEmailService))] public class CustomerEmailService : ICustomerEmailService {...} 

And the factory user controller:

 public class MyControllerFactory : DefaultControllerFactory { private readonly IUnityContainer _container; public MyControllerFactory() { _container = new UnityContainer(); Func<Type, bool> isController = (type) => typeof(IController).IsAssignableFrom(type) && (type.IsAbstract || type.IsInterface || type.GetCustomAttributes(typeof(GeneratedCodeAttribute), true).Any()) != true; foreach (Assembly assembly in BuildManager.GetReferencedAssemblies()) { try { var types = assembly.GetTypes(); // Also register all controllers var controllerTypes = from t in types where isController(t) select t; foreach (Type t in controllerTypes) { _container.RegisterType(t); } // register all providers var providers = from t in types from export in t.GetCustomAttributes(typeof(ImplementsAttribute), true).OfType<ImplementsAttribute>() select new { export.ContractType, Provider = t }; foreach (var item in providers) { if (item.ContractType != null) { _container.RegisterType(item.ContractType, item.Provider); } else { _container.RegisterType(item.Provider); } } } catch { } } } protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) { if (controllerType != null) { var controller = _container.Resolve(controllerType) as IController; if (controller == null) { return base.GetControllerInstance(requestContext, controllerType); } return controller; } throw new HttpException((Int32)HttpStatusCode.NotFound, String.Format( "The controller for path '{0}' could not be found or it does not implement IController.", requestContext.HttpContext.Request.Path) ); } } 

It was too difficult for me to fix all the problems for the MEF factory controller :(

+2
source share

Your equipment here is quite solid and will work even with partial trust. In the nerd dinner MEF example, there are extensions that allow you to deal with the detection of controllers by convention and automatically turn them into MEF exports without putting them using MEF. But managing parts catalogs does not directly work in partial trust, so MEF methods for a botanical dinner do not work in partial trust.

If you work with complete trust and want, based on agreement with your controllers, to start with the MER Nerd Dinner example, but you probably should also read about a few serious issues with the MEF Nerd lunch example that will appear if your own application model located in a separate class of the library project. I wrote about these cases and suggested some corrections.

If you are not all interested in Congress-based materials, then an example of courting a curtain is a bit more complicated. Your decision is probably as beautiful as it is ... and also works in partial trust, which is always a bonus.

[update] I noticed one potential problem with your technique:

 var controllerExport = controllers.Where(x => x.Value.GetType() == controllerType).FirstOrDefault(); 

In the where clause here, you call .Value for each export in the parts collection ..., which will actually cause each of these exported instances to be created and created for evaluation. This can be a nasty performance issue.

You might consider decorating your controllers with named export contracts as follows:

 [Export("Home", typeof(IController))] 

Then, using this factory control instead:

 public class MefControllerFactory: IControllerFactory { private CompositionContainer _Container; public MefControllerFactory(Assembly assembly) { _Container = new CompositionContainer(new AssemblyCatalog(assembly)); } #region IControllerFactory Members public IController CreateController(RequestContext requestContext, string controllerName) { var controller = _Container.GetExportedValue<IController>(controllerName); if (controller == null) { throw new HttpException(404, "Not found"); } return controller; } public void ReleaseController(IController controller) { // nothing to do } #endregion } 
+4
source share

I recently worked with MEF / MVC and wrote about my revised MEF + MVC project. I hope to click on CodePlex soon, but for now, see if this helps you:

+1
source share

Thanks.

I noticed one potential problem with your technique:

var controllerExport = controllers.Where (x => x.Value.GetType () == controllerType) .FirstOrDefault ();

Yes it's true.

After reading this ( http://codepaste.net/yadusn ), I understood how NerdDinner with MEF was implemented.

I used the regular directory for MEF and created my MEFed factory controller (without the Export attribute on the controllers).

  public static IController GetController(CompositionContainer container, Type controllerType) { var controllers = container.GetExports<IController, IDictionary<string, object>>(); if (controllers == null) return null; var controllerExport = controllers .Where(exp => ExportMetadataContainsGuid(exp.Metadata, controllerType.GUID)) .FirstOrDefault(); return (controllerExport == null) ? null : controllerExport.Value; } 

ExportMetadataContainsGuid Method:

 public static bool ExportMetadataContainsGuid(IDictionary<string, object> metaData, Guid guid) { return metaData.ContainsKey(MetadataGuidKey) && guid == (Guid)metaData[MetadataGuidKey]; } 

I use metadata to store the type of GUID and use it to find the right controller.

0
source share

All Articles