June 2008 - Posts

Please read this first: About Dave’s ‘unofficial’ Framework Design Guidelines.

 

ý DO NOT return a null reference (Nothing in Visual Basic and nullptr in C++/CLI) from a string property; return an empty string instead.

Users of your class expect to be able to simply check the length of a string property without also needing to check for null.

For example, in the following code, checking the length of Label.Text should never cause a NullReferenceException to be thrown:

void SetLabelTextIfNotEmpty(Label label)
{
    if (label.Text.Length == 0)
    {
        label.Text = "User name:";
    }
}

þ DO treat a null reference (Nothing in Visual Basic and nullptr in C++/CLI) and an empty string ("") as equivalent.

For example, the following method uses the default value name when passed either a null reference or an empty string.

public string GetValue(string name)
{
    if (string.IsNullOrEmpty(name))
        name = GetDefaultName();

    // Retrieve the value
    [...]
}

An exception to this guideline is when they are both considered as invalid input. In this case, distinct exceptions should be thrown to indicate that an invalid argument was passed. For example:

public void ConnectToServer(string serverName)
{
    if (serverName == null)
        throw new ArgumentNullException("serverName");

    if (serverName.Length == 0)
        throw new ArgumentException("serverName cannot be an empty string.", "serverName");
    // Connect to the server
    [...]
}

By default, the CompositionContainer will not bind to private and internal members. For example, given the following fictional class:

public class ConnectionFactory
{
    [Import("ConnectionString")]
    private string _ConnectionString;

    public string ConnectionString
    {
        get { return _ConnectionString; }
    }

    [...]
}

 

Running the following code:

CompositionContainer container = new CompositionContainer();

container.AddValue("ConnectionString", "Data Source=.;Initial Catalog=Northwind;Integrated Security=True");

ConnectionFactory factory = new ConnectionFactory();
container.AddComponent(factory);
container.Bind();

Console.WriteLine("ConnectionString: " + factory.ConnectionString);

Outputs the following:

ConnectionString: 

As you can see, the value we added explicitly to the container was never bound to the ConnectionFactory._ConnectionString field.

To allow private and internals members to be injected, simply apply the AllowNonPublicCompositionAttribute to the assembly that contains the imports. For example:

[assembly: AllowNonPublicComposition]

 

Rerunning the above code after applying the attribute, outputs the following:

ConnectionString: Data Source=.;Initial Catalog=Northwind;Integrated Security=True

Note: This applies to both private and internal imports and exports.

This is part three in a series of posts on dependency injection in the Managed Extensibility Framework (MEF). You can find other posts in the series here:

  1. Dependency Injection in the Managed Extensibility Framework – Simple Injection
  2. Dependency Injection in the Managed Extensibility Framework – Optional Injection
  3. Dependency Injection in the Managed Extensibility Framework – Named Contracts

In part one, I showed that you can specify a contract (the thing that binds the ‘import’ and ‘export’ together) by passing a type to the constructor of the [Import] and [Export] attributes. Types are not the only way contracts can be specified; they can also be expressed using strings. For example, taking our types from the previous post, we can replace the typeof(IConfigurationStore) contract, with the literal ‘ConfigurationStore’:

public class ConfigurationService
{
    [Import("ConfigurationStore")]
    public IConfigurationStore Store
    {
        get;
        set;
    }

    public IDictionary<string, object> Settings
    {
        get;
        private set;
    }

    public void Load()
    {
        Settings = Store.LoadSettings();
    }    
}
[Export("ConfigurationStore")]
public class RegistryConfigurationStore : IConfigurationStore
{
    public IDictionary<string, object> LoadSettings()
    {
        var dictionary = new Dictionary<string, object>();

        // Load from the registry
        [...]

        return dictionary;
    }
}

String contracts come in handy when you want to export a value whose type alone isn’t enough to uniquely describe or identify it. Primitives, such as int and string, typically fall under this bucket, however, string contracts can also be used to export any type.

As an example of exporting a string contract, the following shows a class that exports a database connection string under the contract ‘ConnectionString’:

public class ApplicationSettings
{
    [Export("ConnectionString")]
    public string ConnectionString
    {
        get { return "Data Source=.;Initial Catalog=Northwind;Integrated Security=True"; }
    }
}

One thing you may have noticed, is that the above type exports the value of a property, instead of an entire type as we’ve seen previously. This is perfectly legal. You can also export fields and methods, which I’ll dig into deeper in a future post.

There are also other ways that you can export values other than via the [Export] attribute. For example, the following shows the same connection string being exported by adding it directly to the container:

CompositionContainer container = new CompositionContainer();
// Export the connection string under contract 'ConnectionString'
container.AddValue("ConnectionString", "Data Source=.;Initial Catalog=Northwind;Integrated Security=True");

This is equivalent to the following example which adds the ApplicationSettings class, defined above, as a component:

CompositionContainer container = new CompositionContainer();
container.AddComponent(new ApplicationSettings()); 

Above, I’ve shown you how to specify contracts using strings, as well as exporting values by adding them to directly to the CompositionContainer. Next time I’ll talk about exporting and importing collections.

For the past couple of years, I spent considerable time saying the same statement over and over again in forums, emails and blog posts; ‘unfortunately, as FxCop runs over compiled binaries, it cannot analyze source-based constructs, such as whitespace, braces and comments.’ While saying this, in the back of my mind I knew we had a very capable tool, StyleCop, used internally at Microsoft that could do this very thing.

When Jason Allor contacted me at the end of last year with his plan to release StyleCop to the rest of the world – I was overjoyed; no longer would I need to keep this gem of a tool to myself.

Now I know I’m a little late to the party, however, late last month Microsoft Source Analysis for C# (aka StyleCop) was released upon the unsuspecting programming community. You can download the latest version directly from http://code.msdn.microsoft.com/sourceanalysis.

Congratulations to Jason and his team!

Posted by David Kean | with no comments
Filed under: , ,

Please read this first: About Dave’s ‘unofficial’ Framework Design Guidelines.


þ DO throw ArgumentException or one of its subtypes for preventable errors.

For example, passing a null reference (Nothing in Visual Basic, nullptr in C++/CLI) for an argument when a value is expected, is an error that could have been prevented by a simple check for null by the caller.

The following method correctly throws an ArgumentNullException and ArgumentException for errors that are both preventable by the caller.

public void WriteToStream(Stream stream)
{
    if (stream == null)
        throw new ArgumentNullException("stream"); // Correct

    if (!stream.CanWrite)
        throw new ArgumentException("'stream' must be writable.", "stream"); // Correct

    // Use stream
}

ý DO NOT throw ArgumentException or one of its subtypes from a method for unpreventable errors or where prevention would be as expensive as calling the method itself.

For example, the following method incorrectly throws an ArgumentException for an error that the user is unable to prevent, without replicating the same check that the method is performing internally.

public int GetSettingAsInt32(string name)
{
    string value = GetSetting(name);

    int result;
    if (!int.TryParse(value, out result))
        throw new ArgumentException(name + " cannot be represented as a Int32.", "name"); // Incorrect

    return result;        
}

Instead, a FormatException would be more appropriate.

public int GetSettingAsInt32(string name)
{
    string value = GetSetting(name);

    int result;
    if (!int.TryParse(value, out result))
        throw new FormatException(name + " cannot be represented as a Int32."); // Correct

    return result;
}

ý DO NOT throw ArgumentException or one of its subtypes if you expect users to catch the exception.

ArgumentException is an indication of a broken contract, and when thrown, should indicate a bug in the calling code. Users should not be forced to catch ArgumentException or one of its subtypes.

For example, the following shows a method that incorrectly throws an ArgumentException to indicate that the server specified by the serverName parameter could not be found.

public void ConnectToServer(string serverName)
{
    if (!TryConnect(serverName))
        throw new ArgumentException("Unable to contact " + serverName, "serverName"); // Incorrect
}

Instead, as this is an error that user is likely to want to handle, ConnectToServer should throw an appropriate existing exception or a custom exception to indicate that it could not connect to the server.

public void ConnectToServer(string serverName)
{
    if (!TryConnect(serverName))
        throw new ServerNotFoundException("Unable to contact " + serverName); // Correct
}

The System.IO.Path class violates the last two guidelines. Its members, such as Path.GetFullPath, throw an ArgumentException when they are passed a path that is not well formed. However, as it does not provide a method to determine whether a path is invalid, it forces users to catch ArgumentException. It would have been better to design it to throw a custom exception instead, such as InvalidPathException, to indicate a bad path.


Related
Framework Design Guidelines Section: 7.3.4 ArgumentException, ArgumentNullException and ArgumentOutOfRangeException

As you are working yourself through the Managed Extensibility Framework, you’re likely to hit a CompositionException with an error similar to the following:

System.ComponentModel.Composition.CompositionException: There was at least one composition issue of severity level `error'. Review the Issues collection for detailed information.

Unfortunately, the default error message is in itself, lacking any real information, requiring you to look at the CompositionException.Issues collection to get anything useful.

To ease this, try using the following method to return a better error message as your playing around with the composition engine:

    private static string GetFriendlyCompositionFailure(CompositionException exception)
    {
        StringBuilder builder = new StringBuilder();

        builder.Append(exception.Message);

        foreach (CompositionIssue issue in exception.Issues)
        {
            builder.AppendLine();
            builder.AppendFormat("{0} : {1} : {2}", issue.Severity, issue.Id, issue.Description);
            if (issue.Exception != null)
            {
                builder.AppendFormat("\t{0}: {1}", exception.GetType(), exception.Message);
            }

            foreach (var pair in issue.Details)
            {
                builder.AppendFormat("\t{0} : {1}", pair.Key, pair.Value);
            }
        }

        return builder.ToString();
    }

This turns the above message into the much more useful:

System.ComponentModel.Composition.CompositionException: There was at least one composition issue of severity level `error'. Review the Issues collection for detailed information.
Error : CM: NoExportsThatMatchContract : There were no exports found with the contract 'IConfigurationStore'.
Error : CM: ImportFailedDetailsInPreviousFailures : A failure occurred while trying to satisfy member 'Store' on type 'ConfigurationService' while trying to import value 'IConfigurationStore'. Please review previous issues for details about the failure.

Hopefully, the error message is something we can get improved by the next CTP.

Previously, I mentioned the reference resolution changes that were made in Code Analysis for 2008 and FxCop 1.36. Unfortunately, not long after we shipped Visual Studio 2008, we found an uncommon scenario in which the strong name matching prevented Code Analysis/FxCop from analyzing a binary even though all assemblies used for compilation were present.

The scenario occurs when you have something similar to the following:

 

 

As you can see above, Assembly 1.0.0.0 has a reference to VSLangProj 8.0.0.0 and EnvDTE 8.0.0.0. Whereas, VSLangProj 8.0.0.0 has a reference to EnvDTE 7.0.3300.0.

Surprisingly, the C# compiler actually allows you to compile the above assembly against VSLangProj with a later version of EnvDTE. It allows this, by substituting the 7.0.3300.0 version of the EnvDTE types exposed by VSLangProj, with their 8.0.0.0 equivalent. However, when doing this, it stills adds a reference in the assembly manifest to both versions of EnvDTE; 7.0.3300.0 and 8.0.0.0, even though you never explicitly added a reference to the former.

In contrast to the compiler, both Code Analysis and the CLR itself by default do not consider types from a later version of an assembly to be the same as their equivalent from an earlier version. In other words, the compiler behavior and the runtime behavior are in direct conflict of each other. In fact, the only way to get such an assembly to load in the CLR, is to add a binding redirect from EnvDTE 7.0.3300.0 –> 8.0.0.0.

When you attempt to run Code Analysis/FxCop over an assembly with references similar to above, you might encounter two different errors with the same underlying cause:

The first error complains about a missing reference:

error : CA0058 : The referenced assembly 'EnvDTE, Version=7.0.3300.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' could not be found. This assembly is required for analysis and was referenced by: 'Assembly.dll'.

Whereas, the second error complains about failing to load the assembly:

error : CA0055 : Could not load Assembly.dll.
error : CA0052 : No targets were selected.

Opening the [AssemblyName]CodeAnalysisLog.xml in output directory will contain something similar to:

<Exceptions>
  <Exception Keyword="CA0055" Kind="AssemblyLoad">
   <Type>Microsoft.FxCop.Common.AssemblyLoadException</Type>
   <ExceptionMessage>Could not load Target.exe.</ExceptionMessage>
   <InnerType>Microsoft.FxCop.Sdk.InvalidMetadataException</InnerType>
   <InnerExceptionMessage>The following error was encountered while reading module 'Assembly': Could not resolve member reference: VSLangProj.BuildManager::get_ContainingProject.</InnerExceptionMessage>
   <InnerStackTrace>   at Microsoft.FxCop.Sdk.Reader.HandleError(ModuleNode mod, String errorMessage)
   at Microsoft.FxCop.Sdk.Reader.GetMemberFromRef(Int32 i, TypeNodeCollection&amp; varArgTypes, Int32 numGenericArgs)
   at Microsoft.FxCop.Sdk.Reader.GetMethodDefOrRef(Int32 codedIndex)
   at Microsoft.FxCop.Sdk.Reader.GetTypeMembers(TypeNode type, Object handle)
   at Microsoft.FxCop.Sdk.TypeNode.get_Members()
   at Microsoft.FxCop.Engines.Introspection.LoadVisitor.VisitType(TypeNode type, TargetType target)
   at Microsoft.FxCop.Engines.Introspection.BaseVisitor.VisitTypes(TypeNodeCollection types, TargetNamespaceDictionary targets)
   at Microsoft.FxCop.Engines.Introspection.LoadVisitor.VisitModule(ModuleNode module, TargetModule target)
   at Microsoft.FxCop.Engines.Introspection.BaseVisitor.VisitAssembly(AssemblyNode assembly, TargetFile target)
   at Microsoft.FxCop.Engines.Introspection.LoadVisitor.VisitAssembly(AssemblyNode assembly, TargetFile target)
   at Microsoft.FxCop.Engines.Introspection.LoadVisitor.Load(TargetFile target, Boolean buildTree, Boolean queueItems, AssemblyNode loadedAssembly)
   at Microsoft.FxCop.Engines.Introspection.IntrospectionAnalysisEngine.LoadTargets(TargetFile target)
   at Microsoft.FxCop.Common.EngineManager.LoadTargets(TargetFile target, Boolean resetCounts, String loadEngine)</InnerStackTrace>
  </Exception>
  <Exception Keyword="CA0052" Kind="Engine">
   <Type>Microsoft.FxCop.Sdk.FxCopException</Type>
   <ExceptionMessage>No targets were selected.</ExceptionMessage>
  </Exception>
</Exceptions>

Luckily, there are a couple of workarounds for those that run into this:

  1. Add a reference to the earlier version of the assembly (ie above this would be EnvDTE 7.0.3300.0), removing the reference to the later version.

    Unfortunately, if you need features only available in the later version or the earlier version of the assembly is not always available (if you are hitting this with EnvDTE, then this is most likely the case), this option will not work. In those cases, use the next option.
  2. Use an unsupported configuration setting and change Code Analysis/FxCop’s resolving logic:

    1. Using a text editor, open the FxCopCmd.exe.config file located in %PROGRAMFILES%\Microsoft Visual Studio 9.0\Team Tools\Static Analysis Tools\FxCop (Code Analysis) or %PROGRAMFILES%\Microsoft FxCop 1.36 (FxCop)
    2. Change the AssemblyReferenceResolveMode from StrongName to StrongNameIgnoringVersion
    3. Save the text file
    4. If using FxCop standalone, repeat the same procedure for FxCop.exe.config

    Once you have changed this setting, Code Analysis/FxCop will start treating the references similar to how the compiler does. Taking above as an example, types in EnvDTE 8.0.0.0 will be considered a match for their 7.0.3300.0 counterparts.

In future versions, it is likely that Code Analysis/FxCop will move to a model that closely mimics the C# compiler’s /reference switch, which will allow it to dynamically choose the correct resolving mode based on the available references.

Posted by David Kean | with no comments
Filed under: ,
Framework Design Guidelines - 2nd Edition

I just noticed that you can now pre-order the second edition of the Framework Design Guidelines. If you haven’t picked up the first edition, then I definitely recommend you grab the second edition.

Congratulations to both of my colleagues, Krzysztof and Brad!

This is part two in a series of posts on dependency injection in the Managed Extensibility Framework (MEF). You can find other posts in the series here:

  1. Dependency Injection in the Managed Extensibility Framework – Simple Injection
  2. Dependency Injection in the Managed Extensibility Framework – Optional Injection
  3. Dependency Injection in the Managed Extensibility Framework – Named Contracts

Last time I showed you how the CompositionContainer can be used to compose or bind components together to fulfill dependencies. This time, I’m going to show how you can specify optional dependences.

Now let’s take the same ConfigurationService component that we used last time, and make a small change:

public class ConfigurationService
{
    [Import]
    public IConfigurationStore Store
    {
        get;
        set;
    }

    public IDictionary<string, object> Settings
    {
        get;
        private set;
    }

    public void Load()
    {
        if (Store == null)
            Store = new DefaultConfigurationStore();

        Settings = Store.LoadSettings();
    }    
}

The new changes are in bold italics in the Load method. Basically what we’re saying is, that if we never end up being set a configuration store, we’ll fall back to using the default one.

Now based on that, let’s try and remove the registration of the RegistryConfigurationStore so that we’re no longer have a configuration store in the container, and see what happens when we bind:

    CompositionContainer container = new CompositionContainer();

    // Register imports
    ConfigurationService configurationService = new ConfigurationService();
    container.AddComponent(configurationService);
    // Match exports to imports
    container.Bind();

Attempting to bind now, throws something similar to the following:

System.ComponentModel.Composition.CompositionException: There was at least one composition issue of severity level `error'. Review the Issues collection for detailed information.       

Looking at the CompositionException.Issues collection, we get the following errors:

Error : There were no exports found with the contract 'IConfigurationStore'.
Error : A failure occurred while trying to satisfy member 'Store' on type 'ConfigurationService' while trying to import value 'IConfigurationStore'. Please review previous issues for details about the failure.

Okay, so that makes sense. We’re no longer satisfying the import on ConfigurationService, so CompositionContainer.Bind fails to bind the added components (in this case, just the single one added; ConfigurationService).

Now, because ConfigurationService can actually function without being set a IConfigurationStore, we can use the named argument IsOptional on the Import attribute to mark it as being optional:

public class ConfigurationService
{
    [Import(IsOptional=true)]
    public IConfigurationStore Store
    {
        get;
        set;
    }

    public IDictionary<string, object> Settings
    {
        get;
        private set;
    }

    public void Load()
    {
        if (Store == null)
            Store = new DefaultConfigurationStore();

        Settings = Store.LoadSettings();
    }    
}

Now let’s see what happens when we bind:

    CompositionContainer container = new CompositionContainer();

    // Register imports
    ConfigurationService configurationService = new ConfigurationService();
    container.AddComponent(configurationService);
    // Match exports to imports
    container.Bind();

And we see that the bind was successful.

The above has shown you how you can use ImportAttribute.IsOptional to indicate to the CompositionContainer that a particular import does not need to be satisfied by an export for a bind to be successful. Next time I’ll did in deeper into the [Import] and [Export] attributes, including importing collections and named imports.

Another thing that we changed in Visual Studio 2008 Code Analysis and FxCop 1.36, was the way we resolved and located references.

These changes came down to two distinct behavior changes:

Strong names of references must now match.

Assemblies must now completely match the exact strong name (name, version, culture and public key token) to be considered a match for a reference. Previous versions of Code Analysis/FxCop allowed any assembly with the same simple name to be considered a match for a reference. This could cause it to pick the wrong reference when multiple assemblies with the same name were on the search path, which lead typically to bad consequences when it failed to find members that didn’t exist in the assembly it picked.

Most users should not see any functional change in 1.36 as analysis will just work as normal. However, for those that now have FxCop complaining that it can’t find a reference even though it seems to be present – simply make sure you point FxCop to the same references that you compiled against. Visual Studio users should not need to do anything – the correct references will automatically be used.

Direct references are now required.

Direct references of the assembly being analyzed must now be present, while indirect (references of references) remain optional. Analysis of a target assembly will not begin without the same binaries that it was compiled against being present*, whereas previous versions of Code Analysis/FxCop, would error, but would attempt to continue analysis.

This was a controversial decision, but makes sense when you understand the underlying technical reasons for this change; the metadata in an assembly simply does not provide enough information to provide meaningful analysis for some rules.

For example, assemblies do not contain:

  • Information about base classes that live in another assembly. ImplementIDisposableCorrectly, IdentifiersShouldHaveCorrectSuffix and MarkAttributesWithAttributeUsage are examples of rules that need to know the base class of a type to be able to give significant analysis.
  • Information about alternative overloads that may exist for a called particular method if that method lives in another assembly. For example, SpecifyCultureInfo needs to know if there is an alternate overload for a method that takes a CultureInfo – this requires looking in direct reference.
  • Information about security attributes applied to members and types, if those member and types live in another assembly. The Code Access Security (CAS) rules such as DoNotIndirectlyExposeMethodsWithLinkDemands and MethodSecurityShouldBeASupersetOfType use this information for their analysis.

There a few other reasons not listed above, but basically the decision comes down to FxCop's analysis would be incomplete and incorrect if direct references were not required.

* Unfortunately, there are a couple of very small isolated scenarios where this is not true and FxCop could prompt for references you were never actually compiled against. I’ll talk about these scenarios and workarounds in Part 2.

Posted by David Kean | 4 comment(s)
Filed under:

One thing that we changed in FxCop 1.36, is that it now ships with the spell checker libraries out-of-the-box. This means that you no longer need to install Office on your development and build machine boxes to get the spelling and compound word rules to fire.

Unfortunately, due to some complex licensing issues – it also means that FxCop loses it support for languages other than English, even if those versions of Office are installed on the same box. :(

Hopefully, someone will cut through the red tape that also seems to be wrapped around the Live Writer team and additional languages will be added in a future version.

Posted by David Kean | 4 comment(s)
Filed under:

Peter Richie asks:

MEF allows decoupling of TypeA's dependency on TypeB, but now TypeA and TypeB have an assembly-level dependency on the MEF assemblies.  If I have a specific restriction on deployment of the assemblies required by TypeB such that they must be signed with a specific key, for example, using attributes is out of the question because I can't deploy the assemblies that contain the Attribute classes.

As well, if I have a type in an existing assembly with a property that I could "export", if that's a 3rd party assembly where I don't have the source to add an Export attribute, how can I wire up that type+member to be used as a component?

Please offer a way to define the logical relationships of types without having introduce new dependencies and modify existing source code.  Ideally this would include both a way to do it with XML configuration and in code.  This is very common for other IoC and DI containers.

Using the [Import] and [Export] attributes to define your inputs and outputs is what we are calling the 'default' programming model. That is, if you were in a green field scenario, and were starting from scratch, then this is the model we would encourage most applications adopt. In fact this model is exactly what Visual Studio and a few other applications coming out of Microsoft will be using as the basis of their extensibility models going forward.

Brown field scenarios, or where you just want to use Plain Old CLR Objects (POCO) and avoid 'dirtying' your types with attributes, is a scenario that we plan on supporting. We know that today, we are not exactly where we want to be in six months with regards to extensibility. However, in saying that, we do already provide some hooks to support this today in the CompositionContainer.

For example, customers can create their own custom resolver (by deriving ValueResolver), which probes a configuration file for available components, and feeds instances of a ComponentBinder (which provides the mechanism for providing exports and for setting imports from a component) into the CompositionContainer. The CompositionContainer has a constructor that takes a resolver as an argument - simply pass the custom resolver via that.

We don't yet provide any samples that do this, but expect some to appear on the team members blogs in the coming weeks (see the Managed Extensibility Framework Blogs and Resources announcement at the top of the forum for links).

We're also looking at what scenarios we will support out of the box; we've already heard from the community a lot that the external manifest (whether that be a configuration file, database, code or some other form) is something that they want to see supported, and as such, is something that is definitely on our radar.

This is part one in a series of posts on dependency injection in the Managed Extensibility Framework (MEF). You can find other posts in the series here:

  1. Dependency Injection in the Managed Extensibility Framework – Simple Injection
  2. Dependency Injection in the Managed Extensibility Framework – Optional Injection
  3. Dependency Injection in the Managed Extensibility Framework – Named Contracts

As its default out-the-of-box programming model, the Managed Extensibility Framework (MEF) allows users to apply attributes to types to indicate to MEF dependencies they require and the ‘values’ that they provide. By using the types in the System.ComponentModel.Composition namespace, these dependencies can be automatically determined and satisfied through the use of dependency injection (DI).

As an example, let’s start off with an interface that defines a contract for loading configuration settings:

public interface IConfigurationStore
{
    IDictionary<string, object> LoadSettings();
}

The following type, ConfigurationService, uses the above interface to load a dictionary of configuration settings. its Store property is marked with the [Import] attribute indicating that it requires an instance of IConfigurationStore to work correctly.

public class ConfigurationService
{
    [Import]
    public IConfigurationStore Store
    {
        get;
        set;
    }

    public IDictionary<string, object> Settings
    {
        get;
        private set;
    }

    public void Load()
    {
        Settings = Store.LoadSettings();
    }    
} 

Now let’s say we have another type, RegistryConfigurationStore, which represents a store that loads configuration settings from the registry. It itself is marked with the [Export] attribute, indicating that it provides an implementation of IConfigurationStore.

[Export(typeof(IConfigurationStore))]
public class RegistryConfigurationStore : IConfigurationStore
{
    public IDictionary<string, object> LoadSettings()
    {
        var dictionary = new Dictionary<string, object>();

        // Load from the registry
        [...]

        return dictionary;
    }
}

Okay, so we have two components; one that requires (‘imports’) an IConfigurationStore and one that provides (‘exports’) an IConfigurationStore. The next question is, given that they don’t know about each other, how do we export the IConfigurationStore from RegistryConfigurationStore to satisfy the import on ConfigurationService?

This is where the CompositionContainer fits in. The CompositionContainer provides a way to mesh a set of components together to match the exports on one component to the imports on another.

We start off registering the components in the container:

     CompositionContainer container = new CompositionContainer();

     // Register imports
     ConfigurationService configurationService = new ConfigurationService();
     container.AddComponent(configurationService);

     // Register exports
     container.AddComponent(new RegistryConfigurationStore());

Next, we call CompositionContainer.Bind which, using Reflection, discovers the export on RegistryConfigurationStore, and matches to the import on ConfigurationService.

    // Match exports to imports
    container.Bind();

Once Bind is called, the ConfigurationService is now ready to be used:

 

     // Load the configuration services
     configurationService.Load();

Now, you might be thinking that the matching of the export from RegistryConfigurationStore to the import on ConfigurationService.Store could have been entirely manually without the CompositionContainer, and with a lot less code:

       ConfigurationService configurationService = new ConfigurationService();
       configurationService.Store = new RegistryConfigurationStore();
       configurationService.Load();

Now, this is a fine solution for small non-extensible applications, however, this approach breaks down when building open and dynamic applications. MEF shines when components need to be loaded on demand or when they are not known statically at compile time. In future blog posts, I’ll talk about our MEF provides infrastructure to dynamically discover components and its approach to delaying the creation of a component until absolutely needed.

I’m pleased to announce that we’ve just released a Community Technology Preview (CTP) of the Managed Extensibility Framework. Krzysztof Cwalina has previously mentioned a little about what my team’s been working on and this release is a very early look at our progress so far.

The goal of this CTP is get early and actionable feedback from the community. One thing I want to make clear is that nothing is set in stone; naming, members, types and extensions points can and will likely change in the future. To have an impact on our future direction, feel free to head over to our new forum and tell us what you think.

Download the Managed Extensibility Framework from MSDN Code Gallery today.

After accidentally losing my last personal domain and blog to an ‘advertising’ company that has now become the top hit for Computer Code Integration Techniques (WTF?), I’ve decided to have another crack at a personal blog.

For those that don’t know me, I’ve posted a introduction page over here, complete with geeky looking picture. For those that do know me, it’s likely due my previous work on FxCop and on the Visual Studio Code Analysis blog, a team I worked on up until a month ago. I now work on the Managed Extensibility Framework (MEF), a new framework for building extensible managed applications.

Although I no longer work on the Code Analysis/FxCop team, I’ll still continue to post on that, as well as API Design, the Framework Design Guidelines (something that my team owns) and on MEF as more as details are released.

In the meantime, you can subscribe via one of the two following links:

 Subscribe via reader
 Subscribe via email

Posted by David Kean | with no comments