Friday, April 30, 2010
My Managed Extensibility Framework (MEF) slides from the Iowa Code Camp
Wednesday, April 14, 2010
Using MEF with Signed Part Assemblies
Over the past few months, I've been giving a number of presentations over the Managed Extensibility Framework. Every time I've given the presentation, the same question has came up - "How was signed assemblies and security factor into MEF?". Ultimately, I didn't have an answer at that time since my experience with the need to do assembly signing has been limited, and my perception is very trusting when it comes to plug-ins and extensibility. Regardless, I don't like not having an answer on a subject I'm presenting/teaching. So, in this post, we'll look at the challenges that exist to ensure that any assemblies that contains parts is signed and how to control composition of those assemblies in your MEF-enabled application.
Important Links
How To Check If An Assembly Is Signed?
While I have said that my experience with signed assemblies has been limited, I have always created or consumed signed assemblies. This was great when adding references but the point of using MEF is to ensure we don't have to add references and recompile. How do I check if an assembly is signed without having to add a reference and recompile the code? After doing a little digging, I wrote the following unit test:
1: [Test]2: public void Check_Unsigned_Assembly()
3: { 4: var assembly = Assembly.GetExecutingAssembly(); 5: 6: Assert.IsEmpty(assembly.GetName().GetPublicKey()); 7: } 8: 9: [Test]10: public void Check_Signed_Assembly()
11: { 12: var assembly = Assembly.LoadFile(ASSEMBLY_ONE_PATH); 13: 14: Assert.IsNotEmpty(assembly.GetName().GetPublicKey()); 15: }In the first test we are loading the test assembly which isn't signed; however, the second test uses a signed assembly. In these tests we are checking the assemblies' AssemblyName by calling GetName() and then GetPublicKey(). If the PublicKey values, which is a byte array (byte[]), is empty, then the assembly is not signed. That's easy; however, if 2+ assemblies are signed with the same .snk file are compared, do they have the same PublicKey value?
1: [Test]2: public void Check_Signed_Assemblies_For_Same_Key()
3: { 4: var assembly1 = Assembly.LoadFile(ASSEMBLY_ONE_PATH); 5: var assembly2 = Assembly.LoadFile(ASSEMBLY_TWO_PATH); 6: 7: CollectionAssert.AreEqual(assembly1.GetName().GetPublicKey(), 8: assembly2.GetName().GetPublicKey()); 9: }Short answer....yes, the keys are the same. Now that we know how to test for signed assemblies and know that assemblies that are signed with the same key have the same PublicKey byte array value, let's look at how we can push this into our MEF-ed out application.
Validating Part Assemblies Are Signed
There are a few ways we can apply the logic we explored using the unit tests above. We could loop through a directory of assemblies and validate the assemblies prior to adding each one as the target of an AssemblyCatalog for an AggregateCatalog. Ideally, we would probably want to wrap that logic as a custom, extended catalog in order to make it reusable if this is something you'll need more often. For this demo though, let's just keep everything local to our application through the below method:
1: private void Compose()
2: { 3: var key = Assembly.GetExecutingAssembly().GetName().GetPublicKey(); 4: 5: var catalog = new AggregateCatalog();
6: catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
7: 8: var dir = new DirectoryInfo("Plugins");
9: Assembly assembly; 10: 11: foreach (var file in dir.GetFiles("*.dll"))
12: { 13: assembly = Assembly.LoadFile(file.FullName);14: byte[] assemblykey = assembly.GetName().GetPublicKey();
15: 16: // custom compare method
17: if (ArraysEqual(key, assemblykey))
18: {19: catalog.Catalogs.Add(new AssemblyCatalog(assembly));
20: } 21: } 22: 23: var container = new CompositionContainer(catalog);
24: container.ComposeParts(this);
25: }In this method, we're adding AssemblyCatalogs into an AggregateCatalog only if a located assembly is signed. If the assembly is not signed, we're not going to load it for this particular composition. Other parts of the application may use all assemblies but for this method, we only want to be signed assemblies that have the same key as the executing assembly. Sounds simple enough, right? Well, there are a few things you need to do to make this work.
A Caveat to Implementation
The first thing I ran into when I tried to implement this with MEF Preview 9 out on Codeplex was that the MEF assembly isn't signed. This wouldn't be an issue except for the fact that any signed assemblies cannot reference unsigned assemblies. So, if you expose a public assembly that contains a custom ExportAttribute or a base class/interface that is decorated with the InheritedExportAttribute, you'll need to have that assembly signed so it can be used by those extending the application. Since that assembly is going to be signed, then it needs a signed version of MEF. It's a vicious cycle. Once you have your core application signed (since it needs to referenced your signed public assembly), you're good to go. In the accompanying C# project, I created a signed version of the MEF Preview 9 source and referenced it in each project that needed such.
Summary
While this is more proof of concept code, it hopefully illustrates what can be done in order to verify not only how to selectively load assemblies that contain MEF parts based on if they are signed or not as well as dependent on the key too. Like I said before, the above code could be refactored into a custom and/or extended catalog in order to simplify the logic in the Compose() method as well as advocate reusability.
Update:
After passing this post around, I was informed that checking to see if the assembly is signed with a public key and/or that if the key matches an existing key is not enough for security sake. To take it another step further, please check out the below blog post by Andreas Håkansson (The Code Junkie) that describes how to test for a strong name as well.
Wednesday, February 24, 2010
Presentation: Extending Your Applications With MEF
Demo Project Listing:
- Demo 1: On the Fly MEF Implementation of just Import and Export attributes
- Demo 2: Using MEF's attributes at the class level and using labels and declarative type mappings.
- Demo 3: Using the Type Catalog
- Demo 4: Using the Directory Catalog
- Demo 5: Using the Aggregate Catalog
- Demo 6: Using untyped Metadata
- Demo 7: Using Strongly Typed Metadata
- Demo 8: Creating a Custom Export Attribute that contains the Metadata as well.
Lastly, this talk was registered under SpeakerRate and can be reviewed at http://speakerrate.com/talks/1922
Sunday, January 31, 2010
A Look at the New DotNetMigrations
One aspect of .Net that a lot of people struggle with is how to manage database changes. If anyone has looked beyond .Net into the realms of Ruby on Rails or Python's Django project, database management is one of the first things that becomes obviously different. Thankfully, there's been a number of migration utilities that have been ported or recreated in the .Net world. After trying out a couple of them, I began to notice that there was a wide variety of how each attempt to accomplish this pain point. Some require IronRuby scripting, many use a custom C# DSL, and some use external build utilities like NAnt to fully manage the changes. One that I was introduced to by a friend and began to really enjoy was DotNetMigrations - an OSS project over at CodePlex.
An Overview of DotNetMigrations
DotNetMigrations started as an OSS project over at CodePlex by Joshua Poehls in early 2008 when he liked what he saw in Ruby on Rails and wished there was a sql script-based version of such a utility in .Net. Unlike Rails' migration strategy which used a DSL written in Ruby to do all of the database object changes, DotNetMigrations was focused around native Sql scripts that would be read and executed by the migration engine. As the project evolved, new commands were added and updates to the Rails platform's migration strategy were also made to DotNetMigrations. Soon, it was obvious that DotNetMigrations had a lot of potential when it comes to new commands and other things. I ended up joining the project in mid 2009 to make some updates and add some commands that I required for a project I was using it on.
After my project was finishing up, I ended up beginning to think of ways to make the application easier to maintain. After some conversations with Joshua about this, we decided to go ahead with rewrite with the goals of making it easier to maintain and extend while keeping the same spirit which it was started in. This past weekend, the finishing touches were placed on the rewritten code and a new release was established with some great features.
Extending DotNetMigrations Thanks to MEF
Any reader of this blog knows that I've been addicted to MEF these past few months. When I looked at the architecture of DotNetMigrations and the goals of such, the decision to use MEF was an obvious one. MEF provides the ability to not only extend but also compose internal components of an application. One of the things we wanted to do was allow other people to create new commands that fit their needs without requiring them to recompile the application's source. MEF allowed us to do such for external commands and because of such, we also turned all internal commands to conform to the same contracts.
In addition to the command aspect of DotNetMigration, we also changed the way messages could be logged. In the older versions of the application, it acted like a standard console application with all messages being output to the console application window. This works for many scenarios; however, if you needed a "silent" program for your task scheduler or wanted to log all messages to a text file, you'd have to do a lot of additional work with the source code or pipe the command to the text file at the command line. Through using MEF and the custom configurable type catalog, creating new logs becomes easy as well as being able to utilize one or many different logging mechanisms.
What Else Is New?
In addition to the core code being rewritten to leverage MEF, we also launched the DotNetMigrations-Contrib project. The focus of this project was to allow others to suggest and share new logs and commands they have written. The components found in this project also will help allow companies who are locked into specific versions of software upgrade with new commands without having to upgrade the core app. As time passes in, some elements from the Contrib project may find their way into the core application; however, things are just starting for now.
As new features and such are added to each project, I'm sure I'll be talking about them here. Until then, feel free to head over to the CodePlex projects' sites and check it out for yourself.
Tuesday, January 26, 2010
Making Part Declarations Easier with InheritedExports
After playing around with MEF for a while, you begin to find more and more locations where it could be used to extend your application. It works great for a large number of scenarios and provides a method for others to extend your apps in a way as simple or complex as you want to make it for them. However, anyone who has sought community participation can tell you that the best way to get such is to make thing as easy as possible for a member of the community to participate. If you have the greatest, extensible application in the world but require 50 steps to get a part added to the application, the user community may not be very prone to build extensions for such. Alternatively, if the work required to extend the application was whittled down to only a few steps, the probability of participation rises. In this blog post, we'll look at this pattern and show how using MEF's InheritedExport attribute can be used to make part creation extremely simple for their authors.
Reviewing Previous Examples:
In previous examples of this series, we created parts that would be placed into a directory and imported into the app based on how the classes were decorated. By explicitly decorating our parts with the Export attribute works well since it gives us the ability to say which classes to import and which not to; however, in order to use the Export attribute we also had to include a reference to the System.ComponentModel.Composition assembly & namespace. Depending on how we setup our part contacts, we would also need to add a reference to our application as well. While that's not a lot of setup, it adds up since it has to be done every time a new part has to be created. If the parts were created as part of a framework or engine, then it would be in a person's best interest to create a VS template or some other form of code generation or snippets to automate as much of that ceremony as possible. But what if there was a simpler way to address this from within the application we're trying to extend?
Simplification through Inherited Exporting:
One of the lesser-documented items contained within MEF is the InheritedExport attribute. This attribute flags all derived classes as parts that fit the contract defined by it. So instead of requiring every part that implements a particular interface or base class to use the Export attribute and settings, you can just decorate the interface or base class with the InheritedExport attribute and then every class is considered to be an Exported part by default. If you are familiar with WCF, this is similar to the practice of creating an Interface for declaring your data contracts instead of directly decorating concrete classes. Below is an comparison between the way that has been show thus far and using the InheritedExport attribute.
Previous Example:
1: [Export("commands", typeof(ICommand))]
2: public class Command1 : ICommand
3: {4: // Implementation of the Interface
5: } 6: 7: [Export("commands", typeof(ICommand))]
8: public class Command2 : ICommand
9: {10: // Implementation of the Interface
11: }Inherited Example:
1: [InheritedExport("commands",typeof(ICommand))]
2: public interface ICommand
3: {4: // Interface Declaration
5: } 6: 7: public class Command1 : ICommand
8: {9: // Implementation of the Interface
10: } 11: 12: public class Command2 : ICommand
13: {14: // Implementation of Interface
15: }Without the requirement of marking all of the parts with the Export attribute, the developers who are creating extensible parts for your application will no longer need to reference the System.ComponentModel.Composition namespace. In addition, the risk of mistyping the contract information in the Export attribute is removed. By using the InheritedExport attribute, the only requirement for authoring parts becomes just a reference to the assembly containing the decorated base class or assembly.
A Useful Pattern of Inherited Exporting:
While the InheritedExport attribute provides a simplified and safer method of handling part creation, it is an All-or-Nothing mechanism. While there are ways to exclude parts using different catalogs, there is no way to explicitly exclude a part like you could when we were using the Export attribute on all parts. A good way around this issue is to base your part contract/type on an interface and add the InheritedExport attribute to an abstract base class that implements said interface. This provides the blanketed support and convenience of using InheritedExport while providing authors an advanced mechanism by directly implementing the interface and adding the Export attribute manually if needed. Below is an example of this pattern.
1: public interface ICommand
2: {3: // Interface Declaration
4: } 5: 6: [InheritedExport("commands", typeof(ICommand))]
7: public abstract class CommandBase : ICommand
8: {9: // Abstract implementation of the Interface
10: } 11: 12: public class Command1 : CommandBase
13: {14: // Implementation of the Abstract Class
15: } 16: 17: public class Command2 : CommandBase
18: {19: // Implementation of the Abstract Class
20: }Summary:
In this post, we looked at how to streamline and simplify our part definitions by using the InheritedExport method instead of just the Export attribute. We looked at the benefits of this simplicity and how it can help make things easier for part authors as well as identified a possible fault with using the InheritedExport attribute at the wrong level of abstraction. In the corresponding example project linked below, the concepts discussed above are applied to a previous example project in order to illustrate some of the simple benefits that can be used within your application.
Resources:
Tuesday, January 12, 2010
A Configurable Type Catalog for MEF
Looking in the Box and at MEF-Contrib
So far in my MEF posts, I've really been focused on simple solutions that utilize the AssemblyCatalog and DirectoryCatalog objects. These specific catalogs provide a very fast and easy way of composing your various parts together. Part of their simplicity comes from how they handle the parts that it finds in the assemblies you provide it; namely, they take an All-or-Nothing approach. If they find parts that meet the defined contract criteria, they assume that they are going to be part of some composition. But what if you only wanted to use only a few parts located in a loaded assembly?This question was half of the problem I was trying to solve for my project. Looking around what's been done so far with MEF, I saw that I could address this part of my problem using a TypeCatalog. A TypeCatalog differs from other ComposablePartCatalogs in that it allows you to explicitly specify which Types you want to use as Parts. For example, in previous posts we showed "Help Text" of various parts that represented different commands/arguments of a command line application. If you didn't want Command #2, you could simply load all other commands into a collection explicitly and then pass the collection into a TypeCatalog.
Great, one problem solved, right? I can now explicitly load which parts I want to import; however, the second part of my problem was that I didn't want to recompile the code every time I wanted to change which parts were imported. At this point, I started to look around at what other people might have already done that would allow this type of configuration. I ended up looking at the Provider Model for MEF that was a part of the MEF-Contrib project out on CodePlex. What the Provider Model from MEF-Contrib does is move the part declarations (i.e. the Import and Export Attributes) to a custom configuration section. This held a lot of promise; however, ran into some trouble while working with it. After about a day of working with the MEF-Contrib provider model, I decided to try my hand at a different approach.
Adding a Layer on top of MEF
While I'm still determined to look into the MEF-Contrib provider model deeper in the near future, I also began to feel that mixing the attributed model and a provider model in the same application felt a bit dirty or smelly. I wanted the leverage the power and simplicity MEF's attributed model but be able to push it to the level of configuration that comes with a provider model. In order to reach this goal, I decided to add another layer of abstraction onto of the great foundation MEF provides for extensibility.In order to do this, I needed 2 things:
- A Custom Configuration Section to Identify the specific Types to import
- A ComposablePartCatalog that will grab these parts.
Creating the Configuration Section
I'm not going to go into a large amount of information on how to create a custom configuration section. There's a large amount of resources out on the Internet that can assist with such. What I needed was a simple section that took a collection of types. I wanted to truly keep things simple. What came out mapped to the following:1: <configuration>
2: <configSections>
3: <section
4: name="mef.configurableTypes"
5: type="MEFExample8.Core.Provider.ConfigurableTypeSection, MEFExample8.Core" />
6: </configSections>
7:
8: <mef.configurableTypes>
9: <parts>
10: <part type="MEFExample8.TestCommand1, MEFExample8" />
11: <part type="MEFExample8.Commands.TestCommand3, MEFExample8.Commands" />
12: </parts>
13: </mef.configurableTypes>
14: </configuration>
Creating the Catalog
Now that I have the section how I needed it, I had to create a catalog that will utilize it. While I could have create a custom catalog using the ComposablePartCatalog base class, I decided to just extend the TypeCatalog that comes with MEF. The TypeCatalog works great for such and the only thing I had to do was bring in the Types from the config file, load them into an IEnumerable<Type> collection, and then pass it to the TypeCatalog base. Simple right?As it turned out, I looked beyond my original needs and found two issues. The first was addressing what to do if the configuration section was named differently. The second issue was dealing with what to do if the catalog is instantiated more than once. Luckily, a simple solution to both was to allow the developer to pass in the section name into the catalog's constructor. This addressed the renaming concern while allowing multiple catalogs to point to different configuration sections.
Below is the catalog's code:
1: public class ConfigurableTypeCatalog : TypeCatalog
2: {3: public ConfigurableTypeCatalog()
4: : base(GetTypes())
5: {6: }
7:
8: public ConfigurableTypeCatalog(string sectionName)
9: : base(GetTypes(sectionName))
10: { 11: }
12:
13: private static IEnumerable<Type> GetTypes()
14: {15: return GetTypes("mef.configurableTypes");16: }
17:
18: private static IEnumerable<Type> GetTypes(string sectionName)
19: {20: var config = GetSection(sectionName);
21:
22: IList<Type> types = new List<Type>();
23:
24: foreach (ConfigurableTypeElement p in config.Parts)
25: {26: types.Add(Type.GetType(p.Type));
27: }
28:
29: return types;
30: }
31:
32: private static ConfigurableTypeSection GetSection(string sectionName)
33: {34: var config = ConfigurationManager.GetSection(sectionName) as ConfigurableTypeSection;
35:
36: if (config == null)
37: {38: throw new ConfigurationErrorsException(string.Format("The configuration section {0} could not be found.", sectionName));39: }
40:
41: return config;
42: }
43: }
As I stated before, I decided to extend the TypeCatalog class. In order to extend it and meet the needs of my project, I overloaded the constructor so that I could set the default configuration section name (used in the configuration file snippet above) while allowing the second constructor to specify the section name. Since the constructor of the TypeCatalog class takes either a single Type or a collection of Types, I turned the code that retrieves that information from the configuration section into a static value. Since I'm not overriding any aspects of the TypeCatalog other than the constructors, I needed a way to pass the information to it as such and the static methods provided.
Notes about the Catalog:
At this point, I have a fully functional catalog that allows me to specify which types to include into a certain piece of my application. After I played around with it a bit I found that it worked well but had a few shortcomings that may cause people to wonder what's going on.The first scenario I came across was working with the catalog to reference parts in a subdirectory. This issue doesn't exist if the types you are referencing are in the same assembly or an assembly that's in the same directory as the executing program; however, if the assembly is in a subdirectory, you'll need to probe the directory as described in my previous MEF post - Using MEF and Custom Configuration Sections.
The second scenario came with the need for Type or Part exclusions. The code above uses the same attributed model that all out of the box catalogs use. This means that if you created an assembly with 3 attributed parts in it and used an AssemblyCatalog or similar one, all 3 parts would be identify and consumed. The custom catalog outlined in this post focuses on Type inclusion and not exclusions. Because of this, if the above catalog is combined in an AggregateCatalog, all of the parts that meet the contract will still be imported. Through this fact, I highly recommend using the above catalog separately from the others.
So What's Next?
As for this catalog, I need to do a bit more refining and possibly add a few more catalogs that follow a similar manner. Ultimately, I'm going to be wrapping some unit tests around it and packaging it up separately. Beyond that, there's a lot of MEF content coming in the future so stay tuned.Resources:
Tuesday, December 22, 2009
Using MEF and Custom Configuration Sections
Up to this point, I've dove into some of the core fundamentals of using the Managed Extensibility Framework. Starting with this post, we're going to dive into how to apply it under certain circumstances. Some of the scenarios in the upcoming posts will focus on using MEF with different .Net technologies, applications that use MEF, or tricks in .Net that can assist when working with MEF. In this post, we'll start with this last category of scenarios by looking at how to utilize a custom configuration section defined in an assembly used as a MEF part and not located in the same directory as the executable. Using this method, it will allow even greater power to your application and freedom to the parts consumed by such.
Setting the Stage
For this post, we'll look at a simple code base we worked on earlier in the series which imported MEF parts from a separate assembly. Let's extend the code base by defining our simple custom configuration section from within the same assembly that define our external part. For simplicity sake, let's call the custom configuration section MEFCustomConfigSection. Below is the code for our custom configuration section:
1: public class MEFCustomConfigSection : ConfigurationSection
2: {3: private static MEFCustomConfigSection settings = ConfigurationManager.GetSection("MEFCustomConfigSection") as MEFCustomConfigSection;
4: 5: 6: public static MEFCustomConfigSection Settings
7: {8: get { return settings; }
9: } 10: 11: [ConfigurationProperty("message", IsRequired = true)]
12: public string Message
13: {14: get { return this["message"].ToString(); }
15: set { this["message"] = value; }
16: } 17: }This configuration section is very simple in that it only defines one property/attribute called Message. This property will contain a string value in which we'll be displaying on the screen when the application consumes our part.
Consuming Our Section
Now that our configuration section has been defining, let's go ahead and create the code to consume such. In previous examples, each of our parts had a property called HelpText that is used by the consuming application to display command line help documentation for each part. What we will do with this new part is using our configuration section's Message property to define what the value of the HelpText property should be. Thankfully, the code for this is extremely simple as shown below:
1: [Export("command", typeof(IHelp))]
2: public class CustomCommand : IHelp
3: {4: public string CommandName
5: {6: get { return "Custom"; }
7: } 8: 9: public string HelpText
10: {11: get { return MEFCustomConfigSection.Settings.Message; }
12: } 13: }Setting Up the Configuration File
Now that we have the custom configuration section created and code that needs it, we're now ready to update our consuming application's App.config file.
1: <configuration>
2: <configSections>
3: <section name="MEFCustomConfigSection"
4: type="MEFExample7.Commands.MEFCustomConfigSection, MEFExample7.Commands" />
5: </configSections>
6: 7: <MEFCustomConfigSection message="Hello From a Custom Section" />
8: 9: <runtime>
10: <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
11: <probing privatePath="Plug-ins;" />
12: </assemblyBinding>
13: </runtime>
14: </configuration>
Setting up the custom configuration section is the same as any other configuration section that would need to be setup. We first define our section within the <configSections> node and then we're able to insert our section; however, what's that last section?
The <runtime> section in the above snippet is needed to tell .Net to scan the Plug-ins subdirectory for the assembly that contains our configuration section. There are two ways of doing this. The example above uses the <probing> node which is great for little examples like this; however, can lead to performance issues since it will scan all assemblies in the paths that are provided. Another issue with the <probing> node is that it doesn't provide a way for targeting specific, strongly named assemblies nor specific assembly versions. If you need stronger control of what assemblies gets loaded, you can use the <codeBase> node instead.
For more information on using the <probing> and <codeBase> nodes for loading assemblies this way, feel free to check out the following links:
NOTE: One final item I need to mention about using <probing>, it can only look in subdirectories of the location the executable is in. So if the assembly is in /bin, your path to probe for assemblies cannot be in a parent directory.
Once the configuration section is setup, and the Plug-ins directory is a child directory of the location of the executable, our new command is now displayed when the application runs.
Summary
In this post, we briefly looked at some things to be aware of when defining a custom configuration section from with in an assembly used as an external MEF part. In future posts, we'll be looking at other scenarios on using MEF including consuming parts written in F# by a C# application as well as take a look at an open source project that will soon be releasing a new version that uses MEF.
Resources
Tuesday, December 1, 2009
Managing Composition Through Lazy Loading Parts
So far in this post series, we've been looking at various aspects of working with MEF in the context of a single level of composition. One interesting thing about MEF is that its composition is recursive based on the assemblies and types identified in the catalogs within the container. What this means is that if one of our parts also has imports defined for parts of its own, the composition container will continue loading parts for the initial type as well as all parts loaded until no more parts are found or all imports are fulfilled. This is a really nice feature since it will ensure everything is ready for you once compose the initial type; however, this eager loading can greatly cause a performance issue if the parts are not constructed properly. In this post on our ongoing series about MEF, we'll look into the concept of parts of parts and how to apply lazy loading principles towards them.
Putting the Pieces of Pieces Together
To start this example off, we'll add to the code we last used in the fourth post of this series (Playing Nice with Other Assemblies using MEF Catalogs). In order to enhance the code to illustrate a part of parts, let's add a new field to our IHelp interface that will represent subcommands of our parts. Since we're going to assume that any of our commands could have multiple subcommands, let's make it of type IEnumerable<IHelp>. Since it's an enumeration of IHelp, the amount of embedding could be infinite in theory (which sounds like fun but maybe another time). After we update our IHelp interface and our current commands, let's mark only our ExampleCommand class to have its Subcommands property to be a MEF Import point as shown below.
IHelp.cs
1: public interface IHelp
2: {3: string CommandName { get; }
4: string HelpText { get; }
5: IEnumerable<IHelp> Subcommands { get; set; } 6: }ExampleCommand.cs
1: [Export("Commands", typeof(IHelp))]
2: public class ExampleCommand : IHelp
3: {4: private string _helpText = "Lorem ipsum dolor sit amet, c ..." +
5: "Nulla molestie erat rhon ..." +
6: "amet dolor. Aliquam rhon ..." +
7: "vel est. Vestibulum et u ..." +
8: "id tellus. Fusce lectus ..."
9: 10: public string CommandName
11: {12: get { return AppName + " Example1"; }
13: } 14: 15: public string HelpText
16: {17: get { return _helpText; }
18: } 19: 20: [ImportMany("Subcommands", typeof(IHelp))]
21: public IEnumerable<IHelp> Subcommands { get; set; }
22: 23: [Import("AppName")]
24: public string AppName { get; set; }
25: }With this done, let's modify our Program.cs code slightly as well. In order to ensure the we can infinitely loop through our subcommands, let's change our output information slightly by using recursion as shown below.
1: void Run()
2: { 3: Compose(); 4: 5: OutputHelp(Commands, 0); 6: Console.ReadKey(); 7: } 8: 9: private void OutputHelp(IEnumerable<IHelp> helpCommands, int padding)
10: {11: foreach (var help in helpCommands)
12: {13: Console.WriteLine(string.Empty.PadLeft(padding, '-') + FormatCommandOutput(help));
14: 15: if (help.Subcommands != null && help.Subcommands.Count() > 0)
16: { 17: OutputHelp(help.Subcommands, ++padding); 18: } 19: } 20: }In the above code, we're passing our recursive function, OutputHelp, two parameters. The first parameter represent the collection of IHelp instances to be outputted to the screen while the second is to provide depth of the recursion. On line 13 of the snippet above we use this depth parameter to append our formatted command name with hyphens equal to the depth. Lastly, we check to see if the Subcommands collection has values and call our OutputHelp method again passing the collection and incrementing the depth.
Next, let's create a new class called ExampleSubcommand.cs. This will represent a subcommand for our ExampleCommand.cs class. Once our ExampleSubcommand is all set, we can run the application to see the following output.
In the above image, we can see our Subcommand being displayed after the Example1 command as expected. In addition, we've applied the hyphens prior to the command name to indicate the depth correctly. Alternatively, we could have added an export to our ExampleCommand class similar to that of our AppName property to send the "MEFExample6 Example1" down the tree as well.
Working with Lazing Parts
Now that we have our example prepped, it's time to see what we can do to optimize the code slightly through lazy loading. For those who may not be familiar with the concept, lazy loading ultimately means that you do not load the objects or data until they are needed. What this translates to is that when we call container.ComposeParts(this) in our Program.cs class, we want only our initial commands to be instantiated but not any subcommands it has in order to ensure only the objects that are currently being used are the ones currently in memory.
in order to accomplish lazy loading of exports in MEF, we need to use the System.Lazy<T> type in place of the contract type of our exports. This type (built into the MEF assembly for .Net 3.5) tells MEF to delay the instantiation of the value until it's actually called. This is exactly what we're looking for to prevent a full line of instances from being created as we compose our parts. Before we dive into the nuances of Lazy<T>, let's look at a simplified snippet below:
1: public class LazyExample
2: { 3: [Import()]4: public System.Lazy<IHelp> command { get; set; }
5: }In the above code, we created an example class called LazyExample which contains a singular import of type IHelp. Because we wanted to delay the instantiation of this instance, we changed the type from IHelp to System.Lazy<IHelp> and we are all set from a definition standpoint. While this looks easy, there are two things to be aware of when working with Lazy<T>.
- System.Lazy<T> requires you to grab instances of the underlying type using the Value property. In the example snippet above, we would need to call command.Value in order to get the instance of IHelp instead of just calling the command property like we've been doing up to this point.
- System.Lazy<T> is not a collection type. System.Lazy<T> does not implement nor inherit any class that implement IEnumerable<T>. What this means is that just wrapping Lazy<T> around your currently defined properties that are decorated with ImportMany() won't work. In order to apply lazy loading to collections of parts, you must change the type to IEnumerable<Lazy<IHelp>>, keeping inline with the above example. This means any looping within the collection must be item.Value now instead of just item.
Applying Lazy Loading to Our Exports
Since we now see how to apply lazy loading to our exports, let's modify our code to implement such. To do this, we'll need to modify our IHelp interface to change the Subcommands property we added to be of type IEnumerable<Lazy<IHelp>> and propagate such between the various parts. Next, we need to copy our recursive OutputHelp command to create a new method called OutputSubcommandHelp which will address the output. Once this is done, our code should look something like this:
IHelp.cs
1: public interface IHelp
2: {3: string CommandName { get; }
4: string HelpText { get; }
5: IEnumerable<Lazy<IHelp>> Subcommands { get; set; } 6: }ExampleCommand.cs
1: [ImportMany("Subcommands", typeof(IHelp))]
2: public IEnumerable<Lazy<IHelp>> Subcommands { get; set; }
Program.cs
1: [ImportMany("Commands", typeof(IHelp))]
2: public IEnumerable<IHelp> Commands { get; set; }
3: 4: [Export("AppName")]
5: public string AppName { get { return "MEFExample6"; } }
6: 7: void Run()
8: { 9: Compose(); 10: 11: OutputHelp(Commands, 0); 12: Console.ReadKey(); 13: } 14: 15: private void OutputHelp(IEnumerable<IHelp> helpCommands, int padding)
16: {17: foreach (var help in helpCommands)
18: {19: Console.WriteLine(string.Empty.PadLeft(padding, '-') + FormatCommandOutput(help));
20: 21: if (help.Subcommands != null && help.Subcommands.Count() > 0)
22: { 23: OutputSubcommandHelp(help.Subcommands, ++padding); 24: } 25: } 26: } 27: 28: private void OutputSubcommandHelp(IEnumerable<Lazy<IHelp>> helpCommands, int padding)
29: {30: foreach (var help in helpCommands)
31: {32: Console.WriteLine(string.Empty.PadLeft(padding, '-') + FormatCommandOutput(help.Value));
33: 34: if (help.Value.Subcommands != null && help.Value.Subcommands.Count() > 0)
35: { 36: OutputSubcommandHelp(help.Value.Subcommands, ++padding); 37: } 38: } 39: }Adding Another Subcommand Layer
The last thing we will do in this example is to further extend the recursion tree by adding a new subcommand onto our current ExampleSubcommand. We'll call it ExampleSubcommand2 and give the contract a label of "Subcommands2". We'll decorate our ExampleSubcommand class to apply Import such into its own Subcommand collection and we'll run our application to see the following:
Summary:
Hopefully after this post you can see the benefits of lazy loading exports as well as how easy it is to implement such. Given the pattern that we have outlined above, the true depth can open a number of possibilities without over-utilizing our memory. In the next section, we're going to mix things up a bit by looking at what it may take to have an export be used to defined and interact with a custom configuration section while the assembly is not in the same directory as the calling assembly.
Resources:
Wednesday, November 25, 2009
Looking Around at Circular References in MEF
In the last post of this series, we created a new example code base used to display help text for various "commands". This was a simple code base that extended previous examples by using external assemblies and different catalogs to identify all of the parts that can be imported and mapped. This example covers a lot of scenarios when applied beyond the means of console-based text output since each imported "command" could literally be a functional piece of code by itself. However, what happens when the imports require something from our main application? When one object has a dependency with another object of another type; only for the dependent have a dependency towards the initial type; this is called a circular dependency. In this post, we're going to look at the condition of a circular dependency and see how MEF encounters such issues.
A Closer Look at Circular Dependencies in General
To reiterate what was said a moment ago, a circular dependency is where two objects depend on each other for different things in somewhat of a symbiotic relationship. Let's look at an example. Let's say we have an application that contains two special types. One type is used to read configuration information and implements the IConfig interface. The second type is used to log any errors that may occur in the system and implements the IErrorManager interface.
In this illustration, our configuration manager requires an instance of an error manager in case there was an error reading the configuration information. In addition, our error manager requires an instance of a configuration manager to know how the system is configured for logging errors. Since each type has a required dependency on the other, a common pattern is to place the required dependencies into a constructor so that the proper dependencies are provided and the type is instantiated in a valid state. Below is an example what our constructors may look like.
1: class ConfigurationManager : IConfig
2: { 3: private IErrorManager _errMgr;
4: 5: public ConfigurationManager (IErrorManager errorManager)
6: { 7: _errMgr = errorManager; 8: } 9: } 10: 11: class ErrorManager: IErrorManager
12: { 13: private IConfig _configMgr;
14: 15: public ErrorManager(IConfig configManager)
16: { 17: _configMgr = configManager; 18: } 19: }If we have to pass in a valid instance of IErrorManager to our configuration manager, we would first have to instantiate an instance of IConfig for our error manager. Since there's not a way based on the above code to instantiate a type without the other, the code has to be changed to allow for each to be created in an invalid state and the dependency to be passed to it via a property like below.
1: class ConfigurationManager : IConfig
2: { 3: public IErrorManager ErrMgr { get; set; }
4: 5: public ConfigurationManager() { }
6: } 7: 8: class ErrorManager : IErrorManager
9: { 10: public IConfig ConfigMgr { get; set; }
11: 12: public ErrorManager() { }
13: }The issue with this pattern is that it requires the developer to now remember to always inject the proper dependencies after the types have been instantiated through their constructors. Ultimately, it's risky because we're all human and tend to forget from time to time. IoC containers help here a little bit, but can still confuse people. An alternative solution which many people prefer is to used a bootstrapped version of one of the two objects that doesn't depend on the other. This pattern also has issues due to what may not be available in a bootstrapped version. In our example, a bootstrapped IConfig object may not contain any error management code with in it.
Looking At a Simple MEF Circular Reference
Now that we, hopefully, understand a bit more about what a circular reference is and where it can occur, let's see about recreating our example with MEF. Let's create a program that has a property for a IErrorManager and IConfigMgr instances. To build the circular reference, let's inject the dependencies through the constructors of our ErrorManager and ConfigManager classes and mark the constructors as Imports. Below is our Circular Reference implementation.
Program.cs
1: namespace MEFExample5
2: { 3: class Program
4: { 5: [Import(typeof(IConfigManager))]
6: public IConfigManager ConfigurationManager { get; set; }
7: 8: [Import(typeof(IErrorManager))]
9: public IErrorManager ErrorManager { get; set; }
10: 11: static void Main(string[] args)
12: { 13: var prog = new Program();
14: prog.Run(); 15: } 16: 17: void Run()
18: { 19: Compose(); 20: Console.WriteLine(ConfigurationManager.TestText); 21: Console.WriteLine(ErrorManager.TestText); 22: Console.ReadKey(); 23: } 24: 25: void Compose()
26: { 27: var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
28: var container = new CompositionContainer(catalog);
29: container.ComposeParts(this);
30: } 31: } 32: }ConfigMgr.cs
1: namespace MEFExample5
2: { 3: public interface IConfigManager
4: { 5: string TestText { get; set; }
6: } 7: 8: [Export(typeof(IConfigManager))]
9: public class ConfigMgr : IConfigManager
10: { 11: 12: public string TestText { get; set; }
13: 14: public ConfigMgr()
15: { 16: TestText = "Config";
17: } 18: 19: [ImportingConstructor] 20: public ConfigMgr(IErrorManager errorManager) : this()
21: { 22: _errorManager = errorManager; 23: } 24: } 25: }ErrorManager.cs
1: namespace MEFExample5
2: { 3: public interface IErrorManager
4: { 5: string TestText { get; set; }
6: } 7: 8: [Export(typeof(IErrorManager))]
9: public class ErrorManager : IErrorManager
10: { 11: public string TestText { get; set; }
12: 13: public ErrorManager ()
14: { 15: TestText = "Error";
16: } 17: 18: [ImportingConstructor] 19: public ErrorManager(IConfigManager configManager) : this()
20: { 21: _configManager = configManager; 22: } 23: } 24: }When we try to run this, we get the following error message:
As we can see, MEF doesn't completely remove issues inherit from constructor injection-based circular references. Like we discussed above though, we can move our dependencies into properties instead of a constructor. What's nice about doing such with MEF though is that we can don't have to truly remember to wire up the dependencies manually. With the ability to declaratively set our dependent properties as Imports, we won't need any additional code. Below is the updated code to that addresses the circular reference.
ConfigMgr.cs
1: namespace MEFExample5
2: { 3: public interface IConfigManager
4: { 5: string TestText { get; set; }
6: } 7: 8: [Export(typeof(IConfigManager))]
9: public class ConfigMgr : IConfigManager
10: { 11: private IErrorManager _errorManager;
12: 13: public string TestText { get; set; }
14: 15: [Import(typeof(IErrorManager))]
16: public IErrorManager ErrorMngr
17: { 18: get { return _errorManager; }
19: set { _errorManager = value; }
20: } 21: 22: public ConfigMgr()
23: { 24: TestText = "Config";
25: } 26: 27: public ConfigMgr(IErrorManager errorManager) : this()
28: { 29: _errorManager = errorManager; 30: } 31: } 32: }ErrorManager.cs
1: namespace MEFExample5
2: { 3: public interface IErrorManager
4: { 5: string TestText { get; set; }
6: } 7: 8: [Export(typeof(IErrorManager))]
9: public class ErrorManager : IErrorManager
10: { 11: private IConfigManager _configManager;
12: 13: public string TestText { get; set; }
14: 15: [Import(typeof(IConfigManager))]
16: public IConfigManager ConfigManager
17: { 18: get { return _configManager; }
19: set { _configManager = value; }
20: } 21: 22: public ErrorManager ()
23: { 24: TestText = "Error";
25: } 26: 27: public ErrorManager(IConfigManager configManager) : this()
28: { 29: _configManager = configManager; 30: } 31: } 32: }Summary:
So in this post we looked at Circular References and and how they are handled through MEF. In the next post of this series, we'll dive into how we can use lazy loading towards imported parts and where they could be applied at.