Back in October of last year, Krzysztof Cwalina talked about the multi-targeting changes in Visual Studio 2008 and .NET 3.5. He mentioned the limitations in its design that could cause an application that explicitly targets .NET Framework 2.0 and .NET Framework 3.0 to accidently take a dependency on a new member or type that did not exist in the RTM version of that framework. As a workaround, he posted the source code of a Code Analysis/FxCop rule that could detect and fire on a member that took one of these dependencies.

A little while before he wrote that post, my team at the time, the Code Analysis team, was approached to see if we could ship a similar rule out of the box. Unfortunately, we ran out of time to get it included in Visual Studio 2008 RTM and FxCop Beta 2, however, I’m pleased to announce that we’ve added the rule, Use only API from targeted framework, to Visual Studio 2008 SP1 and FxCop 1.36 RTM release.

Note: The Visual Studio integration for this rule is only available in editions that support Code Analysis; Visual Studio Team Development Edition and Visual Studio Team Suite.

 

How this rule works

In Visual Studio, this rule reads the current targeted framework stored in the C# or Visual Basic project and fires on any usage of API that was not included in the RTM version of that framework. Whereas, in FxCop, the rule reads the targeted framework stored within the FxCop project or passed via the command-line.

The following table attempts to explain this:

 

When Target Framework (in Project properties or in FxCop project options/command-line) is set to:

Code Analysis/FxCop fires on usage of:

.NET Framework 2.0

.NET Framework 3.0

.NET Framework  3.5

.NET Framework 2.0

 

 

 

.NET Framework 2.0 SP1

 

.NET Framework 2.0 SP2

 

.NET Framework 3.0

 

 

.NET Framework 3.0 SP1

 

.NET Framework 3.0 SP2

 

.NET Framework 3.5

 

.NET Framework 3.5 SP1

MSBuild prevents a user from mixing these combinations in Visual Studio, so only FxCop can fire on this.
Client-only Framework subset is not supported.

For example, when a user selects .NET Framework 3.0 as their project’s target framework, Code Analysis and FxCop will fire on any usage of members and types that were introduced in .NET Framework 2.0 SP1 and SP2, .NET Framework 3.0 SP1 and SP2, .NET Framework 3.5 and .NET Framework 3.5 SP1. This is because on a fresh install of .NET Framework 3.0 (such as on Vista RTM), none of these service packs or frameworks are installed.  

Enabling in Visual Studio

By default, Visual Studio projects with the default Code Analysis settings will already have the rule enabled. To check this, do the following:

  1. In Solution Explorer, right-click on your project and choose Properties
  2. Choose the Code Analysis tab
  3. Expand the Portability node and ensure that Use only API from targeted framework is checked

Use only API from targeted framework

Once this rule has been enabled, you need to make sure that you targeting the framework that you plan to release your application on. To verify this, do the following: 

For C# projects:
  1. In Solution Explorer, right-click on your project and choose Properties
  2. Choose the Application tab
  3. From the Targeted Framework drop down, choose the framework that your application is targeting

Target Framework in C# project properties 

For Visual Basic projects:
  1. In Solution Explorer, right-click on your project and choose Properties
  2. Choose the Build tab and click Advanced Compile Options…
  3. From the Target framework (all configurations) drop down, choose the framework that your application is targeting

Target Framework in Visual Basic project properties

Once you’ve done the above, running Code Analysis (Analyze -> Run Code Analysis) should now fire on any service pack dependencies your project has taken on. For example, using DateTimeOffset in a project that is set to target .NET Framework 2.0, will cause the following warning to output.

'Use only API from targeted Framework' warning 

Enabling in FxCop

In contrast to Visual Studio, Use only API from targeted framework does not fire by default in FxCop. This is because FxCop does not have access to the project (.csproj, .vbproj) of the assembly that it is analyzing, and hence cannot automatically determine the Framework your application is targeting. Because of this, you need explicitly specify it; either via the FxCop project or via the command-line.

Enabling using an FxCop project

By default, FxCop projects will already have the rule enabled. To check this, do the following:

  1. Open your FxCop project in FxCop
  2. Choose the Rules tab
  3. Expand the Portability node and ensure that Use only API from targeted framework is checked

Use only API from target framewor

Once this rule has been enabled, it will not fire until you tell FxCop the framework you are targeting. To do this, do the following:

  1. Open your FxCop project in FxCop
  2. Choose Project -> Options and then choose the Spelling & Analysis tab
  3. From the Target Framework drop down, choose the framework you are targeting. Typically this would match the value specified in your C#, Visual Basic or C++ project’s properties.
  4. Click OK

Target Framework in FxCop

Note: You can make this the default for all new FxCop projects and for the command-line by clicking Defaults… after you open the Project Options dialog and then following the rest of the steps above.

Once you’ve done the above, running analysis (Project -> Analyze) should now fire on any service pack dependencies your application has taken on. For example, using DateTimeOffset in a assembly that is set to target .NET Framework 2.0, will cause the following error to output:

'Use only API from targeted framework' error  

Enabling via the command-line

While the preferred method to specify the target framework is by using an FxCop project as described above, you can also specify it explicitly via the command-line. The new /targetframeworkversion switch was added for this purpose. Its valid values are described below:

Argument

Description

/targetframeworkversion:2.0

Specifies .NET Framework 2.0

/targetframeworkversion:3.0

Specifies .NET Framework 3.0

/targetframeworkversion:3.5

Specifies .NET Framework 3.5

/targetframeworkversion:none

Disables the rule.

This is the default. However, it can useful to specify this argument if you want to override the associated setting stored in an FxCop project that is passed on the same command-line.

Specifying any of these arguments on a command-line will override the target framework setting stored in the FxCop project.

An example command-line would be the following:

FxCopCmd.exe /file:MyAssembly.dll /console /targetframeworkversion:2.0

Disabling the rule or targeting specific service packs

Use only API from targeted framework does not have support for targeting specific service packs, such as .NET Framework 2.0 SP1. If you want to deliberately take a dependency on a service pack, simply disable the rule by unchecking instead of checking the rule in the Enabling in Visual Studio and Enabling in FxCop sections.

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

One of the small, but rather useful changes we made in 1.36 was to gray out privates and internals in the Targets tree. This makes it quick and easy to see the public API for a particular assembly.

image 

This is similar to the feature already available in .NET Reflector, except that FxCop also grays out any member that is not visible outside of the assembly; for example, a public method within an internal class.

Posted by David Kean | with no comments
Filed under:

After what has to be the longest beta period for a product (after Gmail) - we've finally released the final version of FxCop 1.36.

I've talked in the past about some the improvements in this release, including:

  • 200+ bug fixes that reduce noise, missing analysis and rule crashes
  • Support for analyzing anonymous methods and lambda expressions
  • New option for skipping analysis over tool generated code
  • Spell checker libraries now ship in the box
  • Changes in the way references are resolved and located
  • Better support for C++/CLI and the Compact Framework
  • Language 'friendly' API names in the UI and resolutions (ie Visual Basic syntax if running over a Visual Basic binary)
  • New globalization, design and usage rules
  • New rule for targeting specific versions of the Framework
  • Performance improvements that cut analysis by 2x and use half as much memory
  • Documentation that is now available on MSDN

    This is a great release - download it, and feel free to head over to the Code Analysis forum to tell my old team what you think.

  • Posted by David Kean | 6 comment(s)
    Filed under: ,

    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 | 3 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.

    More Posts Next page »