Unity Strategy for MEF

by 22. August 2010 11:22

The Prism 4.0 CTP has support for Unity and MEF, but not both at the same time.   In my case this is a problem because I'd like to use MEF for what it is designed for (extensibility / add-ins) and use Unity for what it was designed for (dependency injection).

To achive my requirements I'm going to use the extensibility hooks within Unity, more specifically; a strategy.   Surprisingly the power of strategies are not emphasized and there is not a lot of documentation on the subject.  The "walkthrough" in the Unity 2.0 documentation gives an example of how to create an extension and strategy but I found the Unity unit test to be more complete (I used the SpyExtension as an example on how to construct my MEFStrategy).

The first test below PublicFromPublic() is a MEF unit test that I revamped to test my MEFContainer, which encapsulates and simplifies working with the MEF container and CompositionBatch within the strategy.

The second test lets the Unity container drive and depends on the strategy to add the parts to the batch within the MEFContainer.  The test verifies that the parts are not yet composed prior to executing Compose() and that they are composed following the Compose() method.

This strategy is still in its infant stages, I only started this weekend and have a ways to go before I plug it into Prism 4.0. 


using System.ComponentModel.Composition;
using MEFContrib.Base;
using MEFContrib.Strategy;
using Microsoft.Practices.Unity;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace TestStrategyToLoadMEF
{
    [TestClass]
    public class MEFStrategyFixture
    {
        [TestMethod]
        public void PublicFromPublic()
        {
            var importer = new AllPublicImportOnly { ImportA = 1, ImportB = 1 };
            var exporter = new AllPublicExportOnly { ExportA = 5, ExportB = 10 };
            new MEFContainer()
                .AddPart(importer)
                .AddPart(exporter)
                .Compose();
            Assert.AreEqual(5, importer.ImportA);
            Assert.AreEqual(10, importer.ImportB);
        }
        [TestMethod]
        public void TestStrategyToLoadMEF()
        {
            var mefContainer = new MEFContainer();
            var mefExtension = new MEFStrategyExtension(mefContainer);
 
            // Instantiate unity container and add MEF extension
            var container = new UnityContainer().AddExtension(mefExtension);
            // Initialize MEF parts
            var importer = new AllPublicImportOnly { ImportA = 1, ImportB = 2 };
            var exporter = new AllPublicExportOnly { ExportA = 5, ExportB = 10 };
            // Buildup importer/exporter (strategy kicks in)
            container.BuildUp(importer);
            container.BuildUp(exporter);
            // Register importer instance emulating use in other area of app
            container.RegisterInstance<AllPublicImportOnly>(importer);
            // Resolve MEF and Unity classes
            var importResolved = container.Resolve<AllPublicImportOnly>();
            var classThatImports = container.Resolve<TestClassThatImports>();
            // MEF parts have not yet been composed (Strategy adds them to batch)
            Assert.AreEqual(1, importResolved.ImportA);
            Assert.AreEqual(2, importResolved.ImportB);
            
            // Compose MEF parts
            mefContainer.Compose();
            // After composition exported values should be in place
            Assert.AreEqual(5, importResolved.ImportA);
            Assert.AreEqual(10, importResolved.ImportB);
            // Unity setter injection 
            Assert.AreEqual(20, classThatImports.ImportedClass.TestValue);
        }
    }
    
    public class TestClassThatImports
    {
        [Dependency]
        public TestClassToImport ImportedClass { getset; }
    }
    public class TestClassToImport
    {
        public TestClassToImport()
        {
            TestValue = 20;
        }
        public int TestValue { getset; }
    }
    public class AllPublicImportOnly
    {
        [Import("a")]
        public int ImportA { getset; }
        [Import("b")]
        public int ImportB { getset; }
    }
    public class AllPublicExportOnly
    {
        [Export("a")]
        public int ExportA { getset; }
        [Export("b")]
        public int ExportB { getset; }
    }
}
SOURCE: TestStrategyToLoadMef.zip (11.62 mb)
 

Tags: , ,

MEF | Unity

MEF Container - encapsulating and simplifying

by 21. August 2010 14:19

With MEF 2 Preview 1 released (July 2010) it is time to dig in and examine it under the hood, particularly .NET 4.0 capabilities.   Fortunately the Patterns & Practices teams have a standard of providing unit test which make the task sometimes easier than reading (or waiting on) the documentation.

I'll start from the top and work my way down - I am currently on the System\ComponentModel\AttributedModel\AllowNonPublicCompositionTests.cs

        [TestMethod]
        public void PublicFromPublic()
        {
            var container = ContainerFactory.Create();
            CompositionBatch batch = new CompositionBatch();
            var importer = new AllPublicImportOnly();
            batch.AddPart(importer);
            batch.AddPart(new AllPublicExportOnly() { ExportA = 5, ExportB = 10 });
            container.Compose(batch);
            Assert.AreEqual(5, importer.ImportA);
            Assert.AreEqual(10, importer.ImportB);
        }

Up to this point my adventures with MEF have been confined to the attributes import, export, etc.   I was encourage early in the game to see programmatic support for MEF.  My objective now is to understand MEF from this angle so that I create a Unity Strategy for Prism that will permit Prism, Unity and MEF to play nicely under one roof.  Since I learn by doing I decided to create a MEFContrib project that will simplify life (while making it easier to understand the code).   The MEFContainer and CompositionBatchExtension classes are the first classes added to this project.

For example, the following code performs the exact same process as the above code:

        [TestMethod]
        public void PublicFromPublicUsingMEFContainer()
        {
            var importer = new AllPublicImportOnly();
            var exporter = new AllPublicExportOnly{ ExportA = 5, ExportB = 10 };
            new MEFContainer()
                .AddPart(importer)
                .AddPart(exporter)
                .Compose();
            Assert.AreEqual(5, importer.ImportA);
            Assert.AreEqual(10, importer.ImportB);
        }

The above was Added to AllowNonPublicCompositionTests.cs after adding reference to MEFContrib.

The contents of MEFContrib follow:


using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
namespace MEFContrib.Base
{
    /// <summary>
    ///  MEF Container
    /// </summary>
    public class MEFContainer
    {
        private CompositionBatch batch = new CompositionBatch();
        private CompositionContainer container = new CompositionContainer();
        /// <summary>
        /// Adds the part.
        /// </summary>
        /// <param name="part">The part.</param>
        /// <returns></returns>
        public MEFContainer AddPart(object part)
        {
            batch.AddPart(part);
            return this;
        }
        /// <summary>
        /// Composes this instance.
        /// </summary>
        public void Compose()
        {
            container.Compose(batch);
        }
    }
}

namespace MEFContrib.Base.Extentions
{
    public static class CompositionBatchExtension
    {
        /// <summary>
        /// Composes the specified batch.
        /// </summary>
        /// <param name="batch">The batch.</param>
        /// <param name="container">The container.</param>
        /// <returns></returns>
        public static MEFContainer Compose(this MEFContainer container)
        {
            container.Compose();
            return container;
        }
    }
}

Tags: ,

MEF

PRISM 2.2: can't use StaticResource when resource is in App.xaml

by 22. June 2010 20:09

I lost count of the number of variations of "can't use StaticResource" that I BING'd for over the last couple of days; countless hours banging my head against the desk.

This adventure started when I created a prototype from the DirectoryLookupModule sample from the Prism 2.2 package (ref this prototype link).  As you can see from the link it went through a major transformation and it wasn't until the MVPVM infrastructure was in place (with Business and Data access layers consuming a simple service) that I started to implement a DataTemplate.

The problem was I couldn't access my DataTemplate from the merge dictionary entry for App.xaml UNLESS I made it a DynamicResource.  Where this worked just fine the pit-bull side of me had to know why....

It wasn't until the second day that I noticed that "all" of the resources in this sample application were using DynamicResource, it was this revelation that started the road to discovery.   At the end of the road was the following obsure little declaration in the constructor.   The Prism bootstrapper was being loaded during the constructor versus OnStartup!  This little oversite prevents the StaticResource from being usable in the Shell or any of its Modules.

The happy ending on this story was that while updating the Prism forum with this tidbit (it was where I started my journey to discover the cause of this odd-ball bug) I noticed that Prism 4 had a Drop 2 so  naturally  I downloaded it and at first was somewhat dismayed that Unity was no where in the picture but was shortly taken aback by MEFs angle at the problem; it was very cool to say the least and I am now in the process of moving my MVPVM prototype to Prism 4.

 

CASK - MEF / Unity - Chicken before the egg....

by 23. December 2008 10:59

Problem Statement:  The Community Starter Kit, while very powerful and dynamic, is not extensible due to it's complexity; although CASK "is" extensible in the technical sense the CSK has not taken off with the C# Community because the learning curve is very steep.    

Vision Statement: Use MEF to permit programmers to easily create modules that users can upload, configure and use (isolating them from the complexity of the system).   Currently I have successfully plugged in the MEFContrib's MEFContrib.Library.Web project.  


Code review request - email comments to Bill@global-webnet.com or post them HERE in the MEF forum.   The current design will permit MEF to be used to load MEFModules so that they are available when the page attempts to dynamically instantiate them.   Unity will be used for dependency injection, event aggregation, etc.   In summary: MEF usage will have to be limited to loading external modules (uploaded modules) and Unity will be used for internal use. 
 

CASK HIGH-LEVEL OVERVIEW (within the context of this story)

The Community (Advanced) Starter Kit dynamically instantiates classes (see first arrow in figure B) and is a data driven application; there is only one page (Community_Default.aspx) that serves up all pages for the entire site.  

Currently there is an IModule interface that allows modules to be programmatically added via the Admin | Modules section (the Calendar and Update module are currently functional).  The problem is these modules have to be compiled into the application otherwise there are issues that MEF is here to eliminate.  

The "dynamic instantiation" of the applicable class requires that the module/assembly be loaded in memory prior to the CreateInstance statement - MEF modules must be loaded prior to the ASP.NET Page_Init().    Currently this is accomplished in the IHttpModule.Page_Init() method.

This forced me to modify the MEFContrib project so that MEF objects were composed prior to the pages Page_Init().   Since all controls are not added until after Page_Init(), reference second arrow in Figure B, this means those dynamically modules cannot have MEF Imports/Exports. 

Modified UnityHttpModule.cs from MEFContrib project: 

using System;

using System.Web;

using System.Web.UI;

using System.Collections.Generic;

using System.ComponentModel.Composition;

using Microsoft.Practices.Unity;

 

namespace MEFContrib.Library.Web.Unity {

public class UnityHttpModule : IHttpModule {

 

private HttpApplication context;

private IUnityContainer Container {

    get { return HttpContext.Current

        .Application.GetContainer(); }

}

 

/// <summary>

/// Ensure any MEF Imports/Exports are processed/available

/// for Page_Init

/// </summary>

/// <param name="sender"></param>

/// <param name="e"></param>

private void page_Init(object sender, EventArgs e)

{

    CompositionContainer compositionContainer =

        ((CompositionContainer)Container

            .Resolve<ICompositionService>());

 

    compositionContainer.Compose();

}

 

/// <summary>

/// Build up each control in the page's control tree - done during

/// post init to ensure dynamically loaded controls get processed

/// </summary>

/// <param name="sender"></param>

/// <param name="e"></param>

private void OnPageInitComplete(object sender, EventArgs e)

{

    Page page = (Page)sender;

    foreach (Control c in GetControlTree(page))

        Container.BuildUp(c.GetType(), c);

}

 

/// <summary>

/// Get the controls in the page's control tree excluding the page

/// itself

/// </summary>

/// <param name="root"></param>

/// <returns></returns>

private IEnumerable<Control> GetControlTree(Control root)

{

    foreach (Control child in root.Controls)

    {

        yield return child;

        foreach (Control c in GetControlTree(child))

            yield return c;

    }

}

 

public void Init(HttpApplication context)

{

    this.context = context;

    context.PreRequestHandlerExecute +=

        OnPreRequestHandlerExecute;

}

 

public void Dispose()

{

    context.PreRequestHandlerExecute -=

        OnPreRequestHandlerExecute;

}

 

private void OnPreRequestHandlerExecute(

    object sender, EventArgs e)

{

 

    IHttpHandler handler = HttpContext.Current.Handler;

    HttpContext.Current.Application.GetContainer()

        .BuildUp(handler.GetType(), handler);

 

    Page page = HttpContext.Current.Handler as Page;

    if (page != null)

    {

        page.InitComplete += OnPageInitComplete;

        page.Init += new EventHandler(page_Init);

    }

}

 

 

}

}

 

 

Below we see that when we hit the breakpoint in Page_Init (figure b) that we have both a reference to the IUnityContainer and MEFModule - breakpoint watch window follows: 


Figure A. 


Figure B.

CASK full source available HERE Changeset 45207

Modified MEFHttpApplication.cs from MEFContrib.Library.Web 

using System;

using System.ComponentModel.Composition;

using System.Web;

using MEFContrib.Library.Web.Unity;

using Microsoft.Practices.Unity;

 

namespace MEFContrib.Library.Web.UI {

public class MEFHttpApplication : HttpApplication {

 

protected IUnityContainer Container

{

    get { return HttpContext.Current

            .Application.GetContainer(); }

}

 

protected void Application_Start(object sender, EventArgs e)

{

    Container

        .AddNewExtension<MEFContainerExtension>();

 

    var catalog =

        new DirectoryPartCatalog("bin", "*.dll", false);

 

    Container

        .RegisterInstance<ComposablePartCatalog>(catalog);

 

    ApplicationStart(sender, e);

}

 

protected void Application_End(object sender, EventArgs e)

{

    ApplicationEnd(sender, e);

    Container.Dispose();

}

 

protected void Application_BeginRequest(

    object sender, EventArgs e)

{

    var catalog = Container.Resolve<ComposablePartCatalog>();

    CompositionContainer compositionContainer =

        new CompositionContainer(catalog);

    Container.RegisterInstance<ICompositionService>(

        compositionContainer);

 

    ApplicationBeginRequest(sender, e);

}

 

protected void Application_EndRequest(

    object sender, EventArgs e)

{

    ApplicationEndRequest(sender, e);

}

 

protected void Application_Error(

    object sender, EventArgs e)

{

    ApplicationError(sender, e);

}

 

protected virtual void ApplicationStart(

    object sender, EventArgs e) { }

protected virtual void ApplicationEnd(

    object sender, EventArgs e) { }

protected virtual void ApplicationBeginRequest(

    object sender, EventArgs e) { }

protected virtual void ApplicationEndRequest(

    object sender, EventArgs e) { }

protected virtual void ApplicationError(

    object sender, EventArgs e) { }

}

 

}

 

Tags: ,

MEF

MEF - CompositeWPF under one roof

by 29. September 2008 10:36

MEF and the CompositeWPF both offer distinct and very valuable services.  MEF allows us to easily create Add-in components and the CompositeWPF makes it easy to decouple our services and views.   Today I started integrating the two (reference MEF forum message).

Note: I am currently working on the Proof-Of-Concept (POC), when I have it completed it I will start TDD and refactor it as applicable.  See FLASH DEMO HERE   FULL SOURCE HERE (7meg) see notes under image.

2010.02.06 - Updated blog entry - was notified by reader that the "Full Source Here" link was invalid.  Updated to point to the last update I made while I was running the MEFContrib project (had to turn it over since contract requirements was no longer permitting me to maintain it). 

--------------------------------------------------------------------------------------------------

My objective will be to make it as simple as possible to implement the integration without making any changes to the CompositeWPF or MEF libraries - all of the wiring up will be done in the MEFContrib.Library project.

Below you'll see the only code required in the App.xaml.cs code behind file:

Notice that I have the MEF, CompositeWPF and Unity source code under SRC - these are the complete source code packages.  When the smoke settles I'll only upload DLLs, for now it will be helpful to step into code when it comes time to refactor.

Above the Parts folder contains MEF parts, they are compiled to the bin\Extensions folder.  The bootstrapper references our MEFContrib.Library.

The bootstrapper code that pertains to the CompositeWPF follows below.  The LoadReferencedModules  class below tells the CompositeWPF to load all modules referenced by the BasicWPFApp - in our case it will be the RSSFeed module that I copied/pasted from a CompositeWPF demo I wrote earlier.

namespace MEFContrib.Library.Base
{
    public class MEFBootStrapper : UnityBootstrapper
    {
        private Assembly _assembly;

        private Application _application;
        private MEFCompositionContainer _container;
 
/// <summary>
/// CompositeWPF
/// </summary>
/// <returns></returns>
protected override DependencyObject CreateShell()
{
    _application.MainWindow.Show();
    return _application.MainWindow;
}
 
/// <summary>
/// CompositeWPF
/// </summary>
/// <returns></returns>
protected override IModuleEnumerator GetModuleEnumerator()
{
    Container.RegisterType<ILogger, TraceToolLogger>();
 
    return LoadReferencedModules.GetModules(
        _application.MainWindow.GetType());
}
 
 
/// <summary>
/// MEF
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public MEFBootStrapper(object sender, BootStrapperEventArgs e)
{
    _application = sender as Application;
    _assembly = e.Assembly;
 
    _application.Exit += new ExitEventHandler(_application_Exit);
 
    if (Compose())
        Run();
    else
        _application.Shutdown();
}

The Window1.xaml.cs code-behind file contains an earlier POC so I left it as is.  Note there are no references to the MessageBoxLogger nor DebugLogger in our BasicWPFApp.  MEF will load whatever it finds in the bin\extensions folder which happens to contain these two logging features.

The Windows XAML below has two region names "MainRegion" and "MainView". 

<Window x:Class="BasicWPFApp.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:cal="http://www.codeplex.com/CompositeWPF"
    Title="Window1" Height="400" Width="600">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="25"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
 
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="2*" />
        </Grid.ColumnDefinitions>
 
        <StackPanel Orientation="Horizontal" Grid.ColumnSpan="2">
            <TextBox Name="textBox1" Width="120">Hello World</TextBox>
            <Button Name="button1"  Width="57" Click="button1_Click">Debug</Button>
            <Button Name="button2" Width="55" Click="button2_Click">MB</Button>
        </StackPanel>
        <GridSplitter Grid.Row="1" Width="10"/>
        <ItemsControl Grid.Row="1" Name="MainRegion"  
                      cal:RegionManager.RegionName="MainRegion" Margin="0,0,6,0">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <DockPanel/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
 
        <ItemsControl Grid.Row="1" Grid.Column="1" Name="MainView"  
                      cal:RegionManager.RegionName="MainView">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <DockPanel/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
    </Grid>
</Window>

When the RSSFeed module loads it will populate the MainRegion with the RSSFeedView, which in turn will populate the MainView RSSViewer.

namespace RSSFeed
{
    [Module(ModuleName = "RSSFeedModule")]
    public class RSSFeedModule : ModuleBase
    {
        /// <summary>
        /// Injection Constructor
        /// </summary>
        /// <param name="regionManager">Registered IRegionManager</param>
        /// <param name="container">Registered IUnityContainer</param>
        /// <param name="presenter">Registered IMainPresenter</param>
        public RSSFeedModule(IRegionManager regionManager,
            IUnityContainer container)
        {
            RegionManager = regionManager;
            Container = container;
        }
 
        protected override void RegisterViewsAndServices()
        {
            base.RegisterViewsAndServices();
 
            // Add view to region
            IRegion mainRegion = RegionManager.Regions["MainRegion"];
 
            // Name it so we can move it later in MainController.cs
            mainRegion.Add(Container.Resolve<RSSFeedView>(), "RssFeed");
        }
 
 
        public override void Initialize()
        {
            base.Initialize();
        }
    }
}

MEF - WPF QuickStart

by 16. September 2008 10:16

Currently the Managed Extensibility Framework (MEF) does not have documentation/steps for creating a new WPF Application (there is documentation for a console application) so I figured I'd document the steps I take here; there are plenty of samples to get me going.

  1. Create New WPF Application
  2. Remove the StartupUri="Window1.xaml" from the App.xaml file
  3. Add a reference to the Projects | ComponentModel project
  4. Add the following code to the App.xaml.cs code-behind file:

        /// <summary>
        /// Interaction logic for App.xaml
        /// </summary>
        public partial class App : Application
        {
            private CompositionContainer _container;

            [Import("MainWindow")]
            public new Window MainWindow
            {
                get { return base.MainWindow; }
                set { base.MainWindow = value; }
            }

            protected override void OnStartup(StartupEventArgs e)
            {
                base.OnStartup(e);

                if (Compose())
                {
                    MainWindow.Show();
                }
                else
                {
                    Shutdown();
                }
            }
            protected override void OnExit(ExitEventArgs e)
            {
                base.OnExit(e);

                if (_container != null)
                {
                    _container.Dispose();
                }
            }
            private bool Compose()
            {
                var catalog = new AggregatingComposablePartCatalog();
                catalog.Catalogs.Add(new AttributedAssemblyPartCatalog(Assembly.GetExecutingAssembly()));

                _container = new CompositionContainer(catalog.CreateResolver());
                _container.AddPart(this);

                try
                {
                    _container.Compose();
                }
                catch (CompositionException compositionException)
                {
                    MessageBox.Show(compositionException.ToString());
                    return false;
                }
                return true;
            }

        }
     

  5. Add an [Export("MainWindow")] attribute to the Window1.xaml.cs file's Window1 class.



  6. Compile and run the application - your Window1.xaml file should load.  If you get two windows then you failed to comply with step #2 above

  
A less conventional route is to use a "BootStrapper" class

The BootStrapper code follows:

namespace MEFContrib.Library.Base
{
    public class BootStrapper
    {
        private Assembly _assembly;
        private Application _application;
        private CompositionContainer _container;

        public BootStrapper(object sender, BootStrapperEventArgs e)
        {
            _application = sender as Application;
            _assembly = e.Assembly;

            _application.Exit += new ExitEventHandler(_application_Exit);

            if (Compose())
                _application.MainWindow.Show();
            else
                _application.Shutdown();
        }

        void _application_Exit(object sender, ExitEventArgs e)
        {
            if (_container != null)
                _container.Dispose();
        }

        private bool Compose()
        {
            var catalog = new AggregatingComposablePartCatalog();
            catalog.Catalogs.Add(new AttributedAssemblyPartCatalog(_assembly));
            ((IMefApplication)_application).OnAddCatalog(catalog.Catalogs);

            _container = new CompositionContainer(catalog.CreateResolver());
            _container.AddPart(_application);

            try
            {
                _container.Compose();
            }
            catch (CompositionException compositionException)
            {
                MessageBox.Show(compositionException.ToString());
                return false;
            }
            return true;
        }
    }
}

MEF_QuickStart.zip (2.10 mb)

Tags:

MEF

Hello MEF (Managed Extensibility Framework)

by 12. September 2008 01:13

It was a tough decision to put another new framework on the burner for the Solution Development Management System (http://www.CodePlex.com/SDMS)....  It seems just as I get a framework under my belt, i.e., Composite Application Block (CAB), Smart Client Software Factory (SCSF), Web Client Software Factory (WCSF) and CompositeWPF (aka Prism) a new, and better one comes out to replace them before they have had a chance to mature - they are labeled "alternate tools" but the reality is they are tools that have the potential to become obsolete in the wake of the new framework. 

To be the best I can be (for those that rely on me) I have to stay on the bleeding edge because these will be tomorrows tools; perhaps literally.  The day after tomorrow they could be replaced.   This reality use to stress me out; I always felt like I was behind, at least until I read Rocky Lhotka's article "Keeping sane in the wake of technologies pace"; I can't articulate why "Misery loves company" but this article changed my way of thinking (forever) - as Rocky noted I now have "chosen to embrace and revel in the change".

An excerpt from Rocky's article follows: 

In 2007 I found myself becoming increasingly unhappy, feeling more and more overwhelmed by the rate of change. I felt like I was slipping farther and farther behind. However, the rate of change is outside the control of any of us individually. So this year, rather than fight or despair about it, I’ve chosen to embrace and revel in the change.

The problem with "revel in change" is that you can have no loyalties, particularly to communities or to things that you wanted to do, e.g., I had Contrib projects planned for both the SCSF and CompositeWPF that had to be abandoned.   This part leaves me with a deep sense of regret; feeling like I abandoned the "community team".  I rationalize the feeling by remembering that I have a higher priority to my current and future contracts (it doesn't always help)....

So tomorrow's tool is the Managed Extensibility Framework (MEF).  Since it is Glenn Block's baby I am not concerned that it doesn't even offer a CTP or Beta.   I'm sure I'll take a hit for this  - I'll have to constantly refactor (which is fine for my Open Source project).   For clients it can be more risky and has to be weighed heavily... I remember convincing my clients, when .NET 2.0 was in beta, that I should write their applications using it.  This ended up being a smart decision but I took some serious black-eyes when .NET 2.0 was released; security was increased from beta to release and the sites went down and I was facing problems that there was no googling for - it brought new meaning to the term "Bleeding Edge" because I bled profusely (I lived).

My current client does have a golden rule that I can bring anything in-house, and train the team on it, as long as it is released.  Should my next contract have a similiar rule (which I can respect) then I hope to have an infrastructure in place, and understanding of the framework, so that no time is spent on infrastructure - only Use Case.

It's time to bleed....

MEF and CompositeWPF will co-exist Cool

Notice

Blog videos and references to CodePlex projects are no longer valid