Proper use of the [Import] attribute in MEF

I am studying MEF and I wanted to create a simple example (application) to see how it works in action. So I thought of a simple translator. I created a solution with four projects (DLL files):

Contracts
Web
Bingtranslator
Google translator

Contracts contains the ITranslate interface. Since the name is applied, it will only contain contracts (interfaces), so exporters and importers can use it.

 public interface ITranslator { string Translate(string text); } 

BingTranslator and GoogleTranslator are exporters of this contract. They both implement this contract and provide (export) various translation services (one from Bing, the other from Google).

 [Export(typeof(ITranslator))] public class GoogleTranslator: ITranslator { public string Translate(string text) { // Here, I would connect to Google translate and do the work. return "Translated by Google Translator"; } } 

and BingTranslator :

 [Export(typeof(ITranslator))] public class BingTranslator : ITranslator { public string Translate(string text) { return "Translated by Bing"; } } 

Now, in my Web project, I just want to get the text from the user, translate it with one of these translators (Bing and Google) and return the result back to the user. Thus, in my Web application, I am dependent on a translator. Therefore, I created the controller in this way:

 public class GeneralController : Controller { [Import] public ITranslator Translator { get; set; } public JsonResult Translate(string text) { return Json(new { source = text, translation = Translator.Translate(text) }); } } 

and the last piece of the puzzle should consist in gluing these components (parts) together (to make up a common song from smaller parts). So, in the Application_Start of the Web project, I have:

  var parts = new AggregateCatalog ( new DirectoryCatalog(Server.MapPath("/parts")), new DirectoryCatalog(Server.MapPath("/bin")) ); var composer = new CompositionContainer(parts); composer.ComposeParts(); 

in which /parts is the folder in which I delete the GoogleTranslator.dll and BingTranslator.dll files (exporters are in these files) and in the /bin I just have the Web.dll file containing the importer. However, my problem is that the MEF does not populate the Translator GeneralController property GeneralController necessary translator. I read almost all questions related to MEF on this site, but I could not understand what was wrong with my example. Can someone please tell me what I missed here?

+7
source share
3 answers

OK, what you need to do (without prescribing performance, it's just to see it work)

 public class GeneralController : Controller { [Import] public ITranslator Translator { get; set; } public JsonResult Translate(string text) { var container = new CompositionContainer( new DirectoryCatalog(Path.Combine(HttpRuntime.BinDirectory, "Plugins"))); CompositionBatch compositionBatch = new CompositionBatch(); compositionBatch.AddPart(this); Container.Compose(compositionBatch); return Json(new { source = text, translation = Translator.Translate(text) }); } } 

I am not an expert in MEF, and frankly, I use it for me, it does not do much for me, since I use it only for loading a DLL, and then I have a dependency entry point, and then I use DI containers , not MEF.

MEF is a must - as far as I have seen. In your case, you need to proactively compose what you need to be MEFed, i.e. Your controller. So, your factory controller should make up your instance of your controller.

Since I rarely use MEFed components in my MVC application, I have a filter for those actions that require MEF (instead of MEFing all my controllers in my controller):

 public class InitialisePluginsAttribute : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext filterContext) { CompositionBatch compositionBatch = new CompositionBatch(); compositionBatch.AddPart(filterContext.Controller); UniversalCompositionContainer.Current.Container.Compose( compositionBatch); base.OnActionExecuting(filterContext); } } 

Here UniversalCompositionContainer.Current.Container is a singleton container initialized by directory directories.


My personal view on MEF

MEF, not the DI framework, it does a lot. So there is a lot of overlap with DI and , if you are already using DI frameworks, they should collide .

MEF is very effective at loading DLLs at runtime, especially when you have a WPF application where you can load / unload plugins and expect everything else to work as it did by adding / removing functions.

For a web application, this does not make much sense, since you really should not throw DLLs into a working web application. Consequently, its use is very limited.

I am going to write a post about plugins in ASP.NET MVC and update this post with a link.

+9
source

MEF will only populate imported objects that it creates. In the case of ASP.NET MVC, ASP.NET creates controller objects. It does not recognize the [Import] attribute, so you see that there is no dependency.

In order for MEF to build controllers, you must do the following:

You probably also need to mark most of the exported parts with [PartCreationPolicy(CreationPolicy.NonShared)] to prevent reusing the same instance in multiple requests at the same time. Any condition contained in your parts of the MEF will otherwise be subject to race conditions.

edit : this blog post contains a good example of the whole procedure.

edit2 : another problem may occur. The MEF container will contain references to any IDisposable that it creates so that it can handle these objects when the container itself has been deleted. However, this is not suitable for objects with lifespan "on request"! You really will have a memory leak for any services that implement IDisposable .

It is rather simple to use an alternative, for example AutoFac , which has a NuGet package for ASP.NET MVC integration and which supports query execution time .

+5
source

As mentioned in @Aliostad, you need to have the composition initialization code launched during / after the controller was created in order for it to work - just its presence in the global.asax file will not work.

However, you will also need [ImportMany] instead of [Import] , since in your example you can work with any number of binary ITranslator implementations that you find. The fact is that if you have many ITranslator , but import them into one instance, you will most likely get an exception from MEF, because it will not know which implementation you really want.

So instead you use:

 [ImportMany] public IEnumerable<ITranslator> Translator { get; set; } 

Quick example:

http://dotnetbyexample.blogspot.co.uk/2010/04/very-basic-mef-sample-using-importmany.html

+2
source

All Articles