Phew that blog post title sure is a mouthful! (As you can see I wanted to get all the key words into the title!)
So what's this post really about? I am making an ASP.NET Forms Application composable using the new Managed Extensibility Framework (MEF). By composable I mean that I have for instance a Page that hosts dynamically loaded UserControls. Page and Controls may all communicate with one another but I want to do it without having to hook them up to each other manually. Also you don't want to set compile time references between any different assemblies (often called modules in composable web applications).
Note: This blog post covers several rather advanced topics and one might rightfully ask does it have to be this complex? The answer is; composable web applications is an advanced application and the goal of enabling the things I describe below is to isolate the complex behavior from the day to day coding of the average developer. It should be easy and powerful to work with modularity and user interactions built from composable parts.
Update: The code I attached had some flaws and poor Glenn Block tried to use it. I've updated the files in the zip so that they should be OK now. Just unzip, open the solution and hit F5! If it doesn't work please contact me! ;~)
First a bit of background, then a bit of MEF and finally some MEF/Unity/Web application. There are plenty of code samples below (after all the initial ramblings)!
Background
I postulate that enterprise web applications (Composable Web Clients) need to have modules of product functionality and dynamic parts that can communicate with each other without having to know where all the other parts were originally compiled.
I am a long time user of Patterns & Practices' ObjectBuilder (as of late the Unity Application Block), Enterprise Library and the Web Client Software Factory (WCSF). The latest published version of the WCSF starts to focus on Composite Web Applications under a namespace called Microsoft.Practices.CompositeWeb. Unfortunately this project has been silent for a Microsoft minute. I think this is a pity. There are loads of us working on Enterprise Level ASP.NET sites and we need all the help we can get with these hairy tasks of type modularity and composability. What has happened lately is that the Ent. Lib. has moved forward making a discrepancy between the current version of WCSF and the latest version of the Ent. Lib. Consequently the latest version of P&Ps' DI container - Unity - does not work with WCSF. Michael Puleio has posted quite a few blog posts on how to Convert the Composite Web Application Block to Unity - Using the UnityCompositionContainer (samples posted here CWAB & Unity). I've followed his guide and managed to complete his findings a little bit. It's a really valid approach but I am not going to use CWAB here in this post. Instead I will use another spike by Micael Puleio: Proof of Concept: a simple DI solution for ASP.NET WebForms. I am simplifying this sample a bit since I only wan to show quickly that this works and works fine!
"Way back when" I posted a web EventBroker at the Web Client Software Factory Contrib site to tackle the problem of dynamic controls communicating between controls on a web page. Here is the classic case: You have a master list and a detail view. When you click the master list the detailed data for the item is displayed in the details view. Though the example is trivial it raises the question if the page has to have full knowledge at compile time of which control that makes up the master and which control makes up the detail or if there is a better approach? Of course there is a better approach! Dynamic loading of content is anything but trivial yet it is a very common requirement. My approach in the EventBroker was annoyingly complex due to the fact that I used events to communicate between the different objects. The code worked but I had to dynamically connect events and handlers between objects at runtime. This is tricky business and many crazy things can happen if an exception, for instance, is uncontrollably thrown from an event handler. Besides C#, my language of choice, does not have relaxed delegates which I happen to miss a LOT from the language. If you don't care where an event came from you want to be able to skip the standard object parameter. Some times you want to skip the EventArgs too. In VB you can. And in my EventBroker for the web you could too. I danced around the problem by assigning an internal event handler to my event and forwarding the call appropriately to my relaxed methods using reflection. It worked but it had it's limitations and drawbacks. For our scenario it was sufficient.
MEF recent developments
Note: If you are familiar with the basics of MEF and just want to read about how to use MEF in a web app; skip ahead! However I am also discussing some changes to the MEF that I'd like to see before it's release!
The MEF recently published by Glenn Block and the rest of the team at Codeplex: MEF Making its debut on Codeplex! I knew right away that I wanted to get my fingers in the jar of jam and apply this to the web! It has taken me quite a few days to find the time to put it all together and in the process I've found a few points of feedback on the beta product that I also want to suggest! MEF + Web app naturally replaces my humble little EventBroker with better and more flexible functionality. Thank god I am rid of the events from the coding!
What you need on the web is a base context for the application, with shareable services that all users may use, and a sub context for each user-request to the server. MEF at it's present state does not offer exactly this but you may still use it for a proof of concept. The thing that is missing is that you cannot register global application shared imports and exports separate from request based imports and exports which makes the usage of MEF for web composability a bit clunky today. Hopefully some of my suggestions makes it passed the idea stage!
Basics of MEF
Note: MEF uses a few concepts that I will assume below you are familiar with. You can read about MEF concepts in the MEF Overview.
At it's core MEF is a catalog and a composable container. The catalog keeps records of two things: All objects that expose themselves or parts of themselves to other objects (Export). All objects that consume exported behavior (Import). An exporting or importing object is simply named a "Part" in MEF. The thing that is exported and imported is always a contract - an interface.
You begin by creating a catalog from the types in your domain. If your type exports it has an ExportAttribute and this is later mapped to types that have an ImportAttribute. The catalog can be created from named types, a named assembly or all assemblies in a folder, with or without file system monitor. Catalogs may be aggregated into one another. In the end you have a catalog of all exports and imports in your domain - that may be changed in run-time.
From the catalog you create something called a resolver and use it to create a container for your instances. The container can register instances that may or may not have exports and/or imports. When you feel you're done with adding parts to your container all you have to say is the magic word ".Compose()" and everything comes together. Exports are mapped to Imports and injected in all the right places. If there is a problem in the container, that causes ambiguity in the mappings or a part is missing, the whole thing blows up!
I have a few MEF unit tests below that I wrote to showcase a couple of features.
In several of my samples I've used the following contract, exporting and importing parts:
public interface IFoo { }
[Export(typeof(IFoo))]
private class Foo : IFoo
{ }
[Export(typeof(IFoo))]
private class FooToo : IFoo
{ }
private class BarWithSingleFoo
{
[Import]
public IFoo Foo { get; set; }
}
private class BarWithManyFoos
{
[Import]
public IEnumerable<IFoo> Foos { get; set; }
}
You can see that the contract is IFoo with two classes offering it as an export and two other classes importing the same contract.
Here is the first and most basic test:
[Test]
public void AddingToAContainer()
{
var catalog = new AttributedTypesPartCatalog(typeof(Foo), typeof(BarWithSingleFoo));
var container = new CompositionContainer(catalog.CreateResolver());
var bar = new BarWithSingleFoo();
var foo = new Foo();
container.AddPart(bar);
container.AddPart(foo);
container.Compose();
Assert.IsNotNull(bar.Foo);
Assert.AreSame(foo, bar.Foo);
}
The AttributedTypesPartCatalog is initiated with an enumeration or a params of Type. The simplest way to create a small catalog. From the Catalogs' Resolver a CompositionContainer is created. To this container a new BarWithSingleFoo and a Foo are added. After .Compose() we can see that the bar now has a foo - The foo part was injected into the bar parts .Foo property by matching the export with the import.
If I just add the bar instance but no foo instance and try to .Compose() I get a CompositionException:
[Test]
[ExpectedException(typeof(CompositionException))]
public void AddingOnlyOneOfThePartsFails()
{
var catalog = new AttributedTypesPartCatalog(typeof(Foo), typeof(BarWithSingleFoo));
var container = new CompositionContainer(catalog.CreateResolver());
var bar = new BarWithSingleFoo();
container.AddPart(bar);
container.Compose();
}
I see a potential problem here. The throwing of exceptions for a missing part is potentially a problem.
Above I listed two importing parts (types that import) where the second - BarWithManyFoos is slightly different from the first in that it imports an enumeration of IFoo rather than a single IFoo. This is to me very similar to declaring an event on the type BarWithManyFoos and then subscribing to it with a relaxed eventhandler. There big difference from my other type BarWithSingleFoo is that there does not have to exist any exporting part in the container to satisfy the import of a collection when you call .Compose()!
[Test]
public void AddingNoExportPartsForImportingCollectionIsOK()
{
var catalog = new AttributedTypesPartCatalog(typeof(Foo), typeof(BarWithManyFoos));
var container = new CompositionContainer(catalog.CreateResolver());
var bar = new BarWithManyFoos();
container.AddPart(bar);
container.Compose();
}
The collection of IFoos may be empty without causing an exception.
Note: I have to change to using typeof(BarWithManyFoos) in the AttributedTypesPartCatalog.
The MEF team has chosen to make single imports mandatory and enumerations of imports optional. I have a request for the MEF team on this:
***
Request #1) Optional single imports
In the current (beta) version of MEF if you have an import defined in a part it has to be satisfied at "compose time" - when you stitch parts together. I would like to be able to declare my imports of single instances as optional, bypassing the CompositionException. If I can specify that your import is optional to you then you gain a bit more flexibility in the scenarios where things are really dynamic and you are not always sure if all parts are present.
***
OK - let's move on to a second basic issue. One that complicates things for web developers:
In a web scenario a users connections to the server application is request based and not constant. A Page (usually) is requested, built up and all of it's content is built up too. When you have processed the request there is a response sent back and then all instances of all objects needed for the request are dismantled and thrown away. However there are also application wide instances of services that live in the app on the server and are shared between users. In MEF it has been made possible, for instance, to read a configuration value and export it to consuming importers that need the setting. This is a good example of a piece of data that is sharable and constant between requests and users. Herein lies my problem: You want global services without having to re-instantiate them on each request. At the same time you want session and user isolation between requests. The latter statement means that each request has to have a container of parts that has to be thrown away between each request. Otherwise there would be a risk for one of "my instances" containing my data to spill over to subsequent requests from other users. (My old EventBroker used a similar approach though my container was called a RequestEventBroker but that's besides the point).
OK this is complex but what is the problem really? Well if you have to create a new container for each request you want it to carry as little overhead as possible. Either you have to create it and add the global services each time or you have to have another option. The other option can be a ChildContainer - much like the IUnityContainer interface has a .CreateChildContainer() method. The ChildContainer needs to be able to fetch parts from the global parent container (and even perhaps export parts to the parent?). The ChildContainer has to be possible to dismantle and throw away after each request. One perf issue could be that it takes quite a few clock cycles to tear down all export/import relationships after each request. This is the trade off we have to make in order to 1) Get composability for the web, 2) have a ChildContainer instead of just one Container created from scratch each time. Performance testing will have to iron out the way go proceed here. But wait... looking at the source code of MEF I note that there IS a concept of a child container! The CompositionContainer has an internal private List<CompositionContainer > _childContainers;. This tells me that the following request cannot be so far fetched:
***
Request #2) A lightweight ChildContainer
A MEF Child Container is required that may be used for each web request. The parent container is application level and shared between users and requests. Parts in the parent are reused as services for all requests from Child Container(s). The Child Container is created and used for one request only composing request based resources such as dynamic controls in a page together with each other and with parent container parts. (The trick is to remove the children from the parents when a child container is disposed!)
***
A third issue that I noted was that if I add the same instance to the container twice it is used twice! Equality in the container is not implemented to ignore duplicate additions of the same instance (reference equality). There are four tests below to show this:
- The first test adds one foo instance to the bar version that takes a collection of foos. This works great.
- The second test adds two different instances of the same part. Here I get the behavior I expect. There are two foos in the bar collection each referencing it's own instance of foo.
- The third test adds two different part types both exporting IFoo to the container. Again two instances are exported to the bar collection.
- The fourth and final test however adds the same foo instance twice to the container! This produces a strange result! The foo is used as if it were two different instances. It is used twice! In my mind this is an oversight that needs to be corrected!
[Test]
public void AddingOneFooToBarWithManyFoos()
{
var catalog = new AttributedTypesPartCatalog(typeof(Foo), typeof(BarWithManyFoos));
var container = new CompositionContainer(catalog.CreateResolver());
var bar = new BarWithManyFoos();
var foo = new Foo();
container.AddPart(bar);
container.AddPart(foo);
container.Compose();
Assert.AreEqual(1, bar.Foos.Count());
Assert.AreSame(foo, bar.Foos.First());
}
[Test]
public void AddingTwoInstancesOfSameFoosToBarWithManyFoos()
{
var catalog = new AttributedTypesPartCatalog(typeof(Foo), typeof(BarWithManyFoos));
var container = new CompositionContainer(catalog.CreateResolver());
var bar = new BarWithManyFoos();
var foo = new Foo();
var foo2 = new Foo();
container.AddPart(bar);
container.AddPart(foo);
container.AddPart(foo2);
container.Compose();
Assert.AreEqual(2, bar.Foos.Count());
Assert.IsTrue(bar.Foos.Contains(foo));
Assert.IsTrue(bar.Foos.Contains(foo2));
}
[Test]
public void AddingDiffrentFoosToBarWithManyFoos()
{
var catalog = new AttributedTypesPartCatalog(typeof(Foo), typeof(BarWithManyFoos));
var container = new CompositionContainer(catalog.CreateResolver());
var bar = new BarWithManyFoos();
var foo = new Foo();
var foo2 = new FooToo();
container.AddPart(bar);
container.AddPart(foo);
container.AddPart(foo2);
container.Compose();
Assert.AreEqual(2, bar.Foos.Count());
Assert.IsTrue(bar.Foos.Contains(foo));
Assert.IsTrue(bar.Foos.Contains(foo2));
}
[Test]
public void AddingSameFooTwiceToBarWithManyFoos_WeirdResult()
{
var catalog = new AttributedTypesPartCatalog(typeof(Foo), typeof(BarWithManyFoos));
var container = new CompositionContainer(catalog.CreateResolver());
var bar = new BarWithManyFoos();
var foo = new Foo();
container.AddPart(bar);
container.AddPart(foo);
container.AddPart(foo); // Ref equal objects get added twice to collections!
container.Compose();
Assert.IsNotNull(bar.Foos);
Assert.AreEqual(2, bar.Foos.Count());
bar.Foos.ForEach(f => Assert.AreSame(foo, f));
}
Therefor:
***
Request #3: Instances are added in a reference unique fashion
Items added to the container twice should be used only once! (Reference equality!)
***
Alright! That was sort of a lengthy intro. Now let's go and do this thing for the web!
There are a few steps you have to go through in order to use MEF (inside Unity) on your ASP.NET Forms site
- Extend Unity to use MEF
- Add Unity to your web app and build all requests using this DI container
- Use extended unity to get EventBroker behavior
- Use MEF in web app to get composability
Extend Unity to use MEF
First we put MEF inside Unity! All you need to know about this to make it work is that all instances created and returned from Unity are passed along an internal chain of strategies from one end to the other and back. Each link in the chain, Stratetgy, is a point where some kind of behavior is added - some code executed. The default behaviors (out of the box when you say new UnityContainer()) are type mapping, life time management and creation (of course) including constructor injection, property injection and method injection.
Site note 1: For the web back in WCSF there was also a neat session binding strategy (that was lacking a bit where performance was concerned).
Side note 2: The new ObjectBuilder (aka. ObjectBuilder2) that is the engine of Unity is very much performance improved compared to it's version 1.0.51206.0 predecessor. In ObjectBuilder2 a build plan for each creation is generated as a piece of dynamic code at run time effectively bypassing the performance consuming reflection based approach the old version implemented. I will be speaking about Dynamic Code at this year's edition of Øredev in Malmo, Sweden in November 2008: Magnus Mårtensson, Dynamic Code!
What we need to do, in order to put MEF inside Unity, is add a link to the chain - a new Strategy - that takes each instance created by Unity and adds it to the request based MEF container.
The way to insert a link in the chain in Unity is to use a Unity Container Extender:
public class MEFContainerExtension : UnityContainerExtension
{
protected override void Initialize()
{
Context.Strategies.AddNew<MEFStrategy>(UnityBuildStage.Initialization);
}
}
The extender is called from the app start like so:
unityContainer = new UnityContainer();
unityContainer.AddNewExtension<MEFContainerExtension>();
The .AddNewExtension call instantiates the extension and calls its .Initialize() method. As you can see initialize has access to the chain of strategies inside the ObjectBuilder inside of Unity. This simple MEFStrategy is added after creation of the instances - that's what the UnityBuildStage.Initialization parameter means. Here is the the MEFStrategy:
public class MEFStrategy : BuilderStrategy
{
public override void PreBuildUp(IBuilderContext context)
{
ILifetimePolicy policy = context.Policies.Get<ILifetimePolicy>(new NamedTypeBuildKey(typeof(ICompositionService)));
ICompositionService compositionService = policy.GetValue() as ICompositionService;
compositionService.AddPart(context.Existing);
base.PreBuildUp(context);
}
}
When a strategy chain link (BuilderStrategy) is executed it's PreBuildUp method is processed. As you can see a global policy is extracted that contains the request based container. Each request is initiated by adding a new instance of (what ever implements) ICompositionService to the policy fetched.
Note: Regarding the unboxing of the service above. The next version of Unity tackles a bit of the remaining non generic issues in Unity. I'd love this to be refactored away. The ILifetimePolicy could as well have been ILifetimePolicy<ICompositionService>!
In my web application at the very top there is an app level begin request handler where a new instance of our container is registered:
protected void Application_BeginRequest(object sender, EventArgs e)
{
var resolver = catalog.CreateResolver();
CompositionContainer compositionContainer = new CompositionContainer(resolver);
unityContainer.RegisterInstance<ICompositionService>(compositionContainer);
}
A resolver is created from the global MEF catalog and a new container is created from it and stored as a global service. .RegisterInstance() is by default global in scope and has a permanent lifetime (an instance of a LifetimeManager that makes the instance application lifetime permanent.
***
Request #4: Make ICompositionService inherit IDisposable
If I want to explicitly .Dispose() my container I have to cast my ICompositionService to CompositionContainer and then call .Dispose(). The current declaration of a regular CompositionContainer is this:
public partial class CompositionContainer : ICompositionService, IDisposable
{
[...]
}
I need the ICompositionService to be IDisposable else I have to unbox the interface to the concrete CompositionContainer after each request!
***
Add Unity to Web app
Note: Depencency Injection frameworks are anything but a simple concept. If you are not familiar with it just move along knowing that a factory pattern is applied here; rather than saying new Foo(); you always ask a factory to produce Foo instances for you saying something like unity.Resolve<Foo>(); Jacob Jenkov has a good tutorial abut DI here: What is Dependency Injection?
OK so now we have a UnityContainer extended to use MEF. What we now have to do is make Unity run in my web app so that I will use it's Dependency Injection capabilities (and now MEF) to build and inject all my objects in each request. Rather than me explaining here how to do this I'll just refer you to the post by Michael Puleio I mentioned above: I like that approach to Unity in web apps the most and I feel it is the most viable. It is not 100% real factory pattern since it is actually still the IIS that instantiates the instances of the pages and controls requested and not Unity. Unity is applied to instances after they have been created. From the web app coder's perspective the whole thing looks real using Puleios approach and that is good enough for me. It is, however, not a great solution to iterate all controls on a page just to use injection. Preferably you'd want to create the controls directly using a factory. After all this is not the ASP.NET MVC Framework! Another note is that if controls are dynamically added like in an event handler you'd have to build them up too. The code in Puleios proof of concept builds up controls at the InitComplete event of the page. Here is the link to the blog post again: Proof of Concept: a simple DI solution for ASP.NET WebForms.
Use extended unity to get EventBroker behavior
Now we have Unity enabled in the web application! We also have a MEF extension in place inside Unity!
Now what? Why did we go through all this? ;~)
The reason is now I can write code that dynamically inserts controls on a page like in the following simplified sample:
I have implemented one UserControl directly in my web application and another in a separate web application. Then use both dynamically loaded from one of my web apps. All you have to do to get the one into the other is to compile the Web Extension 1 application and copy it's artifacts over into the MEF Web application and start it up. The MEF Web Application dynamically loads all UserControls it can find into the page!
This is what the sample looks like when you fire it up:
The buttons are on my Default.aspx page. The Lorem ipsum texts are each a UserControl. When I click a button say "Green" I get this:
One UserControl changes font color the other changes background color. Not very impressive but the call is transmitted over MEF automagically! The page talks to the controls and the wiring is automtic.
The real fun begins when I click on the second usercontrols "Talk to my brother!" button:
The lower usercontrol calls the upper to set a new background color (and actually the text turns black too).
Note: My two user controls do not have a strong reference to each other in design time and I have written no code to get them to communicate with each other! The explanation how MEF does this follows here below;
Here is the contract I use:
public interface IMayBeColored
{
CssColor Color { get; set; }
}
public enum CssColor
{
Black = 1,
Red,
Green,
Blue,
White
}
public static class CssColorExtensions
{
public static string ConvertToColorName(this CssColor cssColor)
{
switch (cssColor)
{
case CssColor.Red:
return "red";
case CssColor.Green:
return "green";
case CssColor.Blue:
return "blue";
case CssColor.White:
return "white";
default:
return "black";
}
}
}
Here is the page where I add my controls dynamically:
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server"/>
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
These buttons are on the page:<br />
<asp:Button ID="ButtonBlack" runat="server" Text="Black" OnClick="ButtonBlack_Click" />
<asp:Button ID="ButtonRed" runat="server" Text="Red" OnClick="ButtonRed_Click" />
<asp:Button ID="ButtonGreen" runat="server" Text="Green" OnClick="ButtonGreen_Click" />
<asp:Button ID="ButtonBue" runat="server" Text="Blue" OnClick="ButtonBue_Click" />
<br />
User controls below (each in a black box):
<asp:PlaceHolder ID="PlaceHolder1" runat="server"/>
</ContentTemplate>
</asp:UpdatePanel>
</form>
public partial class _Default : Page
{
[Import]
public IEnumerable<IMayBeColored> ColorTargets { get; set; }
private void SetColorToTargets(CssColor cssColor)
{
ColorTargets.ForEach(ct => ct.Color = cssColor);
}
protected override void OnPreInit(EventArgs e)
{
string[] controls = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.ascx");
foreach (var fullControlName in controls)
{
FileInfo controlInfo = new FileInfo(fullControlName);
Control control = LoadControl(controlInfo.Name);
PlaceHolder1.Controls.Add(control);
}
base.OnPreInit(e);
}
protected void ButtonBlack_Click(object sender, EventArgs e)
{
SetColorToTargets(CssColor.Black);
}
protected void ButtonRed_Click(object sender, EventArgs e)
{
SetColorToTargets(CssColor.Red);
}
protected void ButtonGreen_Click(object sender, EventArgs e)
{
SetColorToTargets(CssColor.Green);
}
protected void ButtonBue_Click(object sender, EventArgs e)
{
SetColorToTargets(CssColor.Blue);
}
}
The form has a PlaceHolder that is loaded with all UserControls in the current directory. I know crazy oversimplified but it works! All you have to do is insert your own dynamic solution here!
The Magic is in the [Import] statement! As you might guess all my UserControls in this sample implement IMayBeColored. When we call .Compose() from our container in the app we will get an export from all containers over into the ColorTargets property. When I click on a button in my page to colorize the controls I get to call methods 'just like that' on the child user controls.
That doesn't seem very impressive right? I have a list of the controls I can fetch them from my PlaceHolder and cast them to execute methods on them and so on... Hey! That's not very easy or convenient either is it!?
The best part is where my first UserControl also implements IBrother and is called by the other one:
public interface IBrother
{
CssColor BackgroundColor { set; }
}
My second UserControl calls it:
[Export(typeof(IMayBeColored))]
public partial class LoremIpsumUserControl2 : UserControl, IMayBeColored
{
[Import]
public IBrother Brother { get; set; }
protected void Button1_Click(object sender, System.EventArgs e)
{
Brother.BackgroundColor = Color;
}
[...]
}
Aha! My first control and second do not know each other at all! They are not implemented or compiled together. The one does not reference the other. All they mutually know is the IBrother contract. One Exports, the other Imports!
And (presto) we have solved our Master Detail scenario using MEF (and Unity) in a web application!
Use MEF in web app to get composability
There was one more thing I wanted to complete but since I ran into problems (and also already have a really long post here that I want to get out the door) I'll end this post instead with a theoretical argument. I want to be able to have a running web application where I just drop a new assembly in the bin and some new UserControls and/or Pages and get new behavior in the runtime of the web application! When I try to do this I get an error when MEF tries to load the assembly. I haven't been able to figure out the root cause of this yet. Perhaps I will and if I do I will post it here!
Note: We only used the simple AttributedTypesPartCatalog above. If you want to have an application that is standing by in run time to be extended you instead use a DirectoryPartCatalog and instruct it to monitor a directory for changes. In this dir you can toss a new assembly and have it loaded in run time!
If I am correct there is some sort of collision here with the IIS that also tries to load this new assembly somehow? It doesn't really mater for the first part of the scenario, the dynamic part, since you can start up a web application and read in all assemblies at startup. The only thing you cannot do is add an extension in run time. (Or rather I did not get that to work in my sample here!)
Conclusion
MEF can be activated inside of a web application to enable communication between dynamic parts (pages and usercontrols) on your site!
Finale
One final request... I am a contracts man. Way back when I bloged Let your interfaces carry your intent. I really like for contracts to play the central part in your applications. Perhaps that is also why I posted on the COP (Composite Oriented Programing) concept from Rickard Öberg and Qi4j! Here is my post on COP: Composite Oriented Programming spike on Unity Application Block
What I want is to be able to declare contracts intended only for MEF and to put attributes on them rather than the implementing types! I want MEF to read attributes from the interfaces of the parts in the container. I intend the following contract to be used for import and export:
[ImportExport]
public interface IFoo { }
Something like the above would be preferable. Anyone that implements are automatically exporting and anyone who declares a public property of the type are importing! Sweet! ;~)
***
Request #5) Export and Import implicitly using attributes on the contract
If the contract is intended for MEF functionality then the contract mind as well carry it's own attributes signaling to MEF that import/export behavior is inferred!
Request: Init extension behavior! When an extension is added to a folder then run some init code...
***
Oh... I lied! Sorry! HERE is the final request:
***
Request #6) Init extension behavior
If I use a DirectoryPartCatalog to dynamically watch a directory and I toss in a new assembly. I want to be able run some sort of code as a "module initializer" (old CWAB concept) code that acts as an initializer for the new extension. We are almost there with the .Changing() and .Changed() events on the DirectoryPartCatalog but the event args (ComposablePartCatalogChangedEventArgs) are not enough!
***
It took some time to finish this post! I wonder if anyone apart from Glenn ;~) will ever make it to the end... Call out with your comments if you have any! ;~)
Cheers,
/Magnus
posted @ Monday, September 15, 2008 2:45 PM