# Monday, August 16, 2010

Episode 110

Jeremy Likness on MEF

Monday, August 16, 2010 11:42:22 AM (GMT Daylight Time, UTC+01:00)
# Monday, February 8, 2010

I gave a talk on MEF a numbe of times during January. You can get the slides and demo from the link below

or you can view the slides below:

Monday, February 8, 2010 3:36:13 AM (GMT Standard Time, UTC+00:00)
# Sunday, January 31, 2010

I smiled as I drove across the state line into Michigan Friday morning. I was returning home from spending most of the week in Ohio, speaking at user groups throughout the state.

I spoke about Managed Extensibility Framework at four user groups over three days in four different cities.

Tuesday, I spoke at an internal user group of the Cincinnati Financial Corporation, before heading over to the Cincinnati .Net User Group in Mason, OH. Wednesday I drove up to Dayton to speak at the Dayton .Net Developers Group. Thursday I presented to a packed house in Columbus at the Central Ohio .Net Developers Group.

The trip was a great success. At each stop, the crowd was larger than their average meeting.  Everywhere I went I heard probing questions that indicated that I was communicating the concepts of MEF and loosely-coupled architecture. This was gratifying as most people had no idea what MEF was when they arrived at my talk.

The best of the trip was that I had a chance to see old friends. I spent ten years living and working in the Cincinnati area and many of my former colleagues came out to hear me. Some I hadn't seen in years. I once worked for a Columbus-based company, and through them I got to know much of the developer community in that area and I saw many familiar faces in Central Ohio this week. Tuesday and Thursday night, we went out for drinks after the meeting, which gave me a chance to talk one-on-one with a lot of smart people.

I also got a chance to see the inside of the Sogeti offices in Cincinnati, Dayton and Columbus and talk with some of the team in these offices.

I had a great time on this tour and I'd love to do another one.

Thank you to all who came out to hear my talk. Thank you especially to Mike Wood, Jim Holmes and James Bender, who allowed me to stay at their homes on my trip.

Sunday, January 31, 2010 5:07:49 AM (GMT Standard Time, UTC+00:00)
# Monday, January 11, 2010

Juanary is offically MEF month. I have scheduled "Extending Your Application with the Managed Extensibility Framework (MEF)" at least 6 times this month. The first time was last week, when I delivered a Grok Talk at Sogeti that was available via Live Meeting.

The following presentations will all be about MEF

  • Fri Jan 15 at 3PM, I will be delivering a vendor session at CodeMash in Sandusky, OH.
  • Tue Jan 26 at Noon, I will deliver a presentation to Financial Corp User Group in Cincinnati, OH.
  • Tue Jan 26 at 6PM, I will present at the Cincinnati .Net User Group in Mason, OH
  • Wed Jan 27 at 6PM, I will present at the Dayton .Net User Group in Dayton, OH
  • Thu Jan 28 at 6PM, I will present at the Central Ohio .Net User Group in Columbus, OH

In addition, I will deliver two presentations Fri Jan 22 at the State of MI Developer Briefing in Lansing, MI.

  • At 1PM, I will deliver my MEF presentation (of course).
    At 2:30PM, I will present on "Speeding your application with Microsoft Velocity".
Monday, January 11, 2010 1:48:24 PM (GMT Standard Time, UTC+00:00)
# Saturday, August 29, 2009

In Preview 6 of Microsoft's Managed Extensibility Framework (MEF), the framework changed the rules on matching multiple exports to a single import.

In previous versions of MEF, the attribute syntax was identical whether we were matching a single item or multiple items to an Import. Both scenarios used the [Import] attribute to tell MEF to find exports with a matching contract.

For example, if your application is using MEF to match a string variable, based on a string contract, you would use code similar to the following

[Import("MyMEFString")]
string SomeString { get; set; }

This works if MEF finds exactly one matching export string, as in the following code.

[Export("MyMEFString")]
string ThatExportedMefString
{
    get
    {
        return "This string was provided by an MEF contract.  It is from  an external assembly.";
    }
}

If there is a chance MEF might find multiple Exports that satisfy the above contract, you would need (in previous versions) to modify the imported type, so that it implements IEnumerable, as in the following example

[Import("MyMEFString")]
IEnumerable<string> SomeStringList { get; set; }

Beginning with MEF Preview 6, the rule for the attribute becomes more strict. If you are matching a number of items into an IEnumerable set of items on your import, you must replace the Import attribute with the ImportMany attribute. In the above example, the Import declaration becomes

[ImportMany("MyMEFString")]
IEnumerable<string> SomeStringList { get; set; }

The main advantage of this change is that ImportMany will not blow up if MEF finds no matching export for the contract. Import throws an exception if it cannot find a matching export.

Of course, your code will need to handle cases in which there are 0 matches, 1 match, or many matches when MEF seeks exports to match this contract. In the above example, that code might look like

foreach (string s in SomeStringList)
{
    Console.WriteLine(s);
}

In my opinion, when you are writing an Import and you don't have control over the Export (for example, if you are allowing third-party vendors to supply the matching Exports), you should always use the ImportMany attribute. The only time you should use the Import attribute is if you are only looking for contract matches in assemblies that you have written and you can guarantee that there will always be exactly one match.

MEF
Saturday, August 29, 2009 8:41:51 PM (GMT Daylight Time, UTC+01:00)
# Monday, August 24, 2009

Episode 43

Kathleen Dollard is the only person I've met who is building a production application using Managed Extensibility Framework (MEF). In this interview, she describes how to use MEF and shares her vision of how it will affect the way we architect applications in the future.

17 mins, 37 secs

Monday, August 24, 2009 4:24:28 AM (GMT Daylight Time, UTC+01:00)
# Friday, July 3, 2009

CodeStock 2009 is in the books. I didn't attend last year, but this year, the Knoxville developer conference expanded to two days and more than doubled the number of attendees.

This conferences ranks high on the important value-per-dollar scale. As a speaker, the $25 conference fee was waived; I shared a ride to Tennessee with five other attendees; and I used the last of my Marriott points for the hotel. All told, I had 2 days of great content for under a hundred bucks in meals and gas. By far, my biggest cost was the two vacation days I had to spend in order to attend.

I saw some very good sessions and met a lot of bright people. In one spontaneous open space, I solicited feedback on the layout of my blog. As a result, I've removed some unneeded links, moved the RSS feed to the top of the main page and enabled Google analytics.

I even had a chance to talk with CodeStock organizer Mike Neel near the end of the conference.

I presented a session on Microsoft Managed Extensibility Framework. It was well-received and many in the audience were actively engaged, asking questions that indicated they grasped all the concepts I was trying to communicate. 

You can download the slides for my MEF presentation from the link below.

The content and samples of this presentation are covered in the articles below.

Friday, July 3, 2009 1:59:07 PM (GMT Daylight Time, UTC+01:00)
# Monday, June 15, 2009

In previous articles, I showed how to create a simple MEF contract based on a string and a contract based on an Interface.

Recall that MEF uses a contract that matches Import and Export components at runtime.  Contracts are defined by Import and Export attributes applied to declarations and definitions, respectively.

In this article, I'll show how to add metadata to your export data and to read that metadata at runtime.

We'll start with the sample created in my article about Interfaces.  In this sample, we created three projects:

  • MEFInterface contains the IToDo interface.
  • MEFConsoleApp1 is our console application.  It contains the Import property based on the IToDo interface.
  • MEFComponent1 is a class library containing an exported property implementing the IToDo interface.

We can add metadata to an Export with the ExportMetaData attribute.  The ExportMetaData attribute accepts two parameters: the name and the value of metadata applied to that export.  When MEF imports this export, the metadata is imported as well and is accessible from code.  Below is an Export from our sample with the ExportMetaData attribute applied.

    [Export(typeof(IToDo))]
    [ExportMetadata("Priority", 2)]
    public class FirstTask : IToDo
    {
        ...
    }

We apply a similar attribute to the other Export in MEFComponent1

    [Export(typeof(IToDo))]
    [ExportMetadata("Priority", 1)]
    public class ImportantTask : IToDo
    {
        ...
    }

MEFConsoleApp1 contained an Import that declared a collection of IToDo objects. To access the metadata of this collection, we should change the declaration to an ExportCollection. An ExportCollection implements the IEnumerable interface, but also exposes MEF metadata.

        [Import(typeof(IToDo))]
        public ExportCollection<IToDo> ToDoList { get; set; }

The code telling MEF to match up contracts remains the same; but the code to access the data and metadata changes to loop through the ExportCollection, as shown below.

            foreach (Export<IToDo> exTd in ToDoList)
            {
                IToDo td = exTd.GetExportedObject();
                Console.WriteLine(td.TaskName);
                int priority = Convert.ToInt32(exTd.Metadata["Priority"]);
                Console.WriteLine("Priority=" + priority.ToString());
            }

In the above code, exTd is an Export object.  The Export object contains not only the IToDo object we imported (via the GetExportedObject method); it also allows us to retrieve metadata.  Since metadata is a set of name-value pairs, we can retrieve a value by passing the name to the Metadata collection.  In this case, we pass get the value of Metadata["Priority"].

In this article, we showed how to apply metadata to an MEF Export and how to retrieve that metadata at runtime.

Code: MEFDemo3.zip (574.93 KB)

Note: The code in this article uses MEF CTP 5.

MEF
Monday, June 15, 2009 12:10:38 PM (GMT Daylight Time, UTC+01:00)
# Saturday, June 13, 2009

In my last article, I showed how to create a Managed Extensibility Framework (MEF) contract using the [Import] and [Export] attributes.  The code in that article created a contract based on a string and imported only a string variable.  I chose that sample because it was the simplest I could think of to illustrate the concepts of a contract.  But if all you are doing is swapping strings at runtime, there are simpler ways to accomplish this than MEF.

In this article, we will create a contract based on an interface.  Interfaces describe public properties and methods of a class without providing any implementation of those properties and methods.  This gives developers the flexibility to decide later which class to instantiate.  With MEF, that flexibility is increased even more because developers do not need to set a reference to classes at compile time.

Recall that MEF uses a contract that matches Import and Export components at runtime.  Contracts are defined by Import and Export attributes.

In our sample, we create three projects:

  • MEFInterface contains the IToDo interface.
  • MEFConsoleApp1 is our console application.  It contains the Import property based on the IToDo interface.
  • MEFComponent1 is a class library containing an exported property implementing the IToDo interface.

MEFConsoleApp1 and MEFComponent1 each have a reference to MEFInterface because each contains a class that implements the IToDo interface.  However, MEFConsoleApp1 does not contain a reference to MEFComponent1, even though the console app will consume classes in the class library.  An MEF contract replaces the tight coupling of a typical application.

Creating the Contract 

To accomplish this, we do the following
1. Create the IToDo interface in the MEFInterface project.  This interface defines two properties: TaskName and HoursRequired, as shown below

    public interface IToDo
    {
        string TaskName { get; }
        double HoursRequired { get; }
    }


2. In the console application, declare a variable of type IToDo and decorate this variable with the Import attribute.  The Import attribute accepts a parameter that defines the contract.  Assign the contract type of the import attribute as TypeOf (IToDo) as shown below

        [Import(typeof(IToDo))]

public IToDo MyTask {get; set;}

3. In the MEFComponent1 project, create a class that implements IToDo.  Return a value from each property's getter and decorate this class with the Export attribute.  Assign the contract type parameter as TypeOf(IToDo).  We now have a contract because we have an import and an export with the same contract parameter.  MEF will match these up when we tall it where to compose its parts.

    [Export(typeof(IToDo))]
    public class FirstTask : IToDo
    {
        public string TaskName
        {
            get
            {
                return "Get out of bed";
            }
        }
        public double HoursRequired
        {
            get
            {
                return .10;
            }
        }
    }

4. As in the code from the last article, we must tell MEF where to find all the parts to compose.  Add the following code to the MEFConsoleApp1.

            string folderToWatch = 
System.Configuration.ConfigurationSettings.AppSettings["MEFExportFolder"];
var catalog = new DirectoryCatalog(folderToWatch); var container = new CompositionContainer(catalog); var batch = new CompositionBatch(); batch.AddPart(this); container.Compose(batch);

5. The above code gets the folder location from App.Config file, so you will need to add to the MEFConsoleApp1 project an application configration file with the following contents. (Set the directory to the location of the compiled DLL for the MEFComponent1 project).

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="MEFExportFolder"
         value="C:\Development\MEF\DGCode\MEFDemo2\MEFComponent1\bin\Debug" />
  </appSettings>
</configuration>

6. Finally, add code in MEFConsoleApp1 to output values of our variable.

            Console.WriteLine
(
string.Format
(
"It takes {0} hours to do the task: {1}.",
MyTask.HoursRequired,
MyTask.TaskName
)
);

What if there are Multiple Exports matching an Import? 

In this case, MEF was able to match a single IToDo Export with a single IToDo import. But what would happen if MEF found two Exports to match just one import? 

In our example, we might add to MEFComponent1 a second exportd class that implements IToDo.

    [Export(typeof(IToDo))]
    public class ImportantTask : IToDo
    {
        public string TaskName
        {
            get
            {
                return "Make the donuts";
            }
        }
        public double HoursRequired 
        { 
            get
            {
                return .25;
            }
        }

Which Export would MEF choose to match the Import?  Both satisfy the contract.  The answer is that MEF would choose to throw an exception.  If we can't be sure of how many Exports MEF will find to match our Import contract, we should replace our IToDo declaration with an collection of IToDo objects for the Import, as shown below.

        [Import(typeof(IToDo))]
        public IEnumerable<IToDo> ToDoList { get; set; }

Then, we can loop through this collection and output all the properties:

            foreach (var td in ToDoList)
            {
                Console.WriteLine
(
string.Format
(
"It takes {0} hours to do the task: {1}.",
td.HoursRequired,
td.TaskName
)
); }

In this article, we showed how to create an MEF contract based on an interface and how to handle mutliple exports for a single import in an MEF contract.

Code: MEFDemo2.zip (574.36 KB)

Note: The code in this article uses MEF CTP 5.

MEF
Saturday, June 13, 2009 4:19:00 AM (GMT Daylight Time, UTC+01:00)
# Wednesday, June 10, 2009

Microsoft Managed Extensibility Framework (MEF) is a framework for building applications composed of parts.  By constructing an application of parts, any part can be replaced at runtime. 

In order to use this framework in your project, you will need to set reference to System.ComponentModel.Composition.dll (which you can download from http://codeplex.com/MEF) and add the following statements to the top of any module with MEF code or attributes

using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;

The composition of MEF parts is very loosely coupled.  Coupling occurs using contracts.  A contract consists of an Import and one or more Exports.

An Import is a place in your application that defines a variable but does not explicitly state how that variable is implemented.  Instead, a declaration is decorated with the [Import] attribute.  A parameter of the [Import] attribute describes the implementation that the variable is seeking.  This parameter can be a string, a type or an interface.  When MEF encounters an Import, it looks for an Export that matches the Import's contract parameter.

An Export is a class decorated with the [Export] attribute. This attribute tells MEF that the class is available to satisfy any matching Import request.  Just as with the [Import] attribute, the [Export] attribute accepts a parameter that is a string, a type or an interface.

For example, the following variable declaration

        [Import("MyMEFString")]
        string SomeString { get; set; }


defines an import with a contract "MyMEFString". That contract is satisfied by the following export

        [Export("MyMEFString")]
        string ThatExportedMefString
        {
            get
            {
                return "This string was provided by an MEF contract.  It is from  an external assembly.";
            }
        }

Simply declaring this contract isn't enough.  We need to tell MEF to match up the imports and exports.  The following steps are necessary for MEF to do its thing
1. Create a catalog.  A catalog tells MEF where to find the Imports and Exports.
2. Create a container that uses the catalog.
3. Create a batch and call the Compose method of that batch.  The Compose method tells MEF to find the Imports and Exports that make up each contract and match them up.

If you want to compose parts that all exist in the currently executing assembly, this is done with the following code

 var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
var container = new CompositionContainer(catalog);
var batch = new CompositionBatch();
batch.AddPart(this);
container.Compose(batch);

The code above assumes that the contract's Exports and Imports are in the same project. 

Notice the first line of code, in which we declare a variable named 'catalog'.  This line instantiates a Catalog object.  A Catalog object defines where MEF looks for parts.  MEF supports the following types of Catalogs 

  • An Assembly Catalog retrieves MEF components compiled into a given assembly.
  • A Directory Catalog retrieves all MEF components in all assemblies in a given directory.
  • An Aggregating Catalog is used when you want to retrieve MEF components from multiple places - for example, from a specific assembly and from a directory; or from two different directories.

Of course it doesn't make much sense to go to the trouble of using a loosely-coupled framework like MEF and getting your parts from the current assembly. In fact, it often doesn't make sense to get parts from an assembly that is known at design time.  If we know the assembly name and location, we might as well set an explicit references to it.

A more likely scenario is to import all MEF assemblies found in a specific folder. To do this, we use a Directory Catalog.  We replace the declaration/initialization of the catalog with the following line

 string folderToWatch = @"C:\Development\MEF\DGCode\MEFDemo1\MEFComponent1\bin\Debug";
var catalog = new DirectoryCatalog(folderToWatch);

By setting our catalog to a DirectoryCatalog, we tell MEF to search for imports in all assemblies found in a well-known folder.  We can just drop DLLs into this folder and MEF will find them and use them at runtime, even if our projects do not set a reference to them.

The MEFDemo1 solution is a very simple sample that uses this code. 

In MEFDemo1, a console application declares a string variable named "SomeString" which is decorated with the Import attribute and defined by the contract "MyMEFString". 

At runtime, MEF determines the The value of SomeString by searching for the Export half of the contract.  It finds it in ThatExportedMefString variable, declared in the Class1 class of the MEFComponent1 project.  MEF knows to look here because we pointed a DirectoryCatalog at the folder where MEFComponent1.dll is compiled.  (If you copy this solution to a different directory, you will need to change the path defined in folderToWatch and recompile the MEFComponent1 project. In a real-world application, we would probably pull the folder name from a configuration file or otherwise set it at runtime to make it even more flexible.) 

The important point is that we don't need to set any references between these projects in order for them to communicate.  The contract and MEF take care of this for us.

In this article, we showed a very simple MEF contract, based on a string.  In the next article, we'll show how to create a contract based on an interface.

Code: MEFDemo1.zip (563.48 KB)

Note: The code in this article uses MEF CTP 5.

 

MEF
Wednesday, June 10, 2009 11:43:53 AM (GMT Daylight Time, UTC+01:00)
# Saturday, May 30, 2009
 #
 

Microsoft Managed Extensibility Framework (MEF) is a framework for building extensible applications. Using MEF, you can build extensible applications constructed of loosely-coupled composable parts.  By constructing an application of parts, any part can be replaced at runtime, without recompiling or redeploying the entire application. 

One use would be to create an extensible application with a plug-in architecture, allowing users to extend it or to replace parts of it, without recompiling.  As such, you would not need to release the source code along with your application. 

Microsoft already has several technologies to accomplish similar things.  Visual Studio 2008 and Microsoft Office 2007 each has a plug-in framework that allows users to extend the application.  MEF promises a single extensibility framework that can be used across all Microsoft applications.  This frees developers from the need to learn a different framework to extend each application.  In fact, the editor in the upcoming Visual Studio 2010 (now in beta) is built on top of MEF, so that developers can use MEF to add plug-ins to the IDE.

Of course, there are simpler technologies built into the .Net framework that allow you to extend applications at runtime. 

In the current version of .Net, we can code to interfaces, instead of concrete classes.  Doing so gives us the ability to defer to runtime which class to instantiate.  Our code is flexible enough to accept any class, as long as that class implements the expected interface.  However, we must decide at compile time all possible classes that might be instantiated at runtime.  This is because, in most cases, we cannot instantiate a class without setting a reference to the assembly in which that class resides.  And setting references is something done prior to compiling.  Using MEF, we can instantiate classes even if there is no explicit reference set.  MEF takes care of that for us.

Managed Extensibility Framework promises to solve the problem of building loosely-coupled, extensible applications without forcing developers to learn a new skill set for each application.  It does so without the disadvantages of forcing a recompile and loading classes into memory unnecessarily when an application is extended at runtime.

Note: As of this writing, MEF is in Community Technology 5 and is planned to be released as part of .Net 4.0

.Net | MEF
Saturday, May 30, 2009 6:35:37 PM (GMT Daylight Time, UTC+01:00)
# Tuesday, May 19, 2009

SPEAKING

I am scheduled to speak at the following upcoming events

CodeStock 2009

The CodeStock conference will be held in Knoxville, TN on June 27.  I will be presentingon Microsoft Managed Extensibility Framework (MEF).  You can find information on this event and register at http://codestock.org/

West Michigan .Net User Group

I will speak again on MEF at the July 14 meeting of the West Michigan .Net User Group in Grand Rapids.  You can find more information at http://www.wmdotnet.org/

LISTENING

I'll quietly spend all of Wednesday May 20 at the Microsoft office in Southfield so that I can attend a series of events throughout the day.  The details are below:

ArcReady
Time: 9:00-11:45AM
Topics:
Trends and patterns on the client tier
Applying Microsoft technology on the client tier
Register: http://msevents.microsoft.com/CUI/EventDetail.aspx?culture=en-US&EventID=1032408649
 
MSDN Unleashed
Time: 1:00-3:00PM
Topics:
Internet Explorer 8 for Developers
Developing on Microsoft Windows 7
Register: http://msevents.microsoft.com/CUI/EventDetail.aspx?culture=en-US&EventID=1032409548
 
Technet Unleashed
Time: 3:10-5:00PM
Topics:
Windows Server 2008 R2 – Optimize Your Time
Windows 7 – Maximize Your Potential
Register: http://msevents.microsoft.com/CUI/EventDetail.aspx?culture=en-US&EventID=1032410548

Southeast Michigan .Net User Group
Time: 3:10-5:00PM
Topic: RIA
More information:
http://migang.org/

 

Tuesday, May 19, 2009 6:13:25 PM (GMT Daylight Time, UTC+01:00)
# Friday, May 15, 2009

Today I gave a presentation (again) on the Microsoft Managed Extensibility framework.  Below are the slides and demos used for this presentation.

ProjetListDemo A simple demo showing the syntax for MEF imports and exports 

DemoAccounting shows how to dynamically add modules to an application at runtime, without recompiling.

Friday, May 15, 2009 3:26:06 AM (GMT Daylight Time, UTC+01:00)
# Monday, November 10, 2008
 #
 

The slides for my MEF presentation are now available on Slideshare.  I have embedded it below.

I delivered this presentation at the ann arbor Day of .Net in October and at a Sogeti grok talk in November.

I just signed up for Slideshare and I like the concept but it doesn't seem to support any of the animations or transitions in my slides.  I may need to go to a video sharing service for more dynamic slideshows.

Managed Extensibility Framework
View SlideShare presentation or Upload your own. (tags: mef .net)

Monday, November 10, 2008 12:23:06 PM (GMT Standard Time, UTC+00:00)
# Thursday, October 23, 2008

Saturday I had the pleasure of speaking at the ann arbor Day of .Net

The event drew presenters and attendees from Michigan, Ohio and Indiana, demonstrating what an impressive software development community we have here in the Midwest.

My friend Nino drove up Friday night to stay at my place and we met other out-of-towners for dinner Friday night.

I delivered a presentation on Microsoft's Managed Extensibility Framework ("MEF").  The presentation was well-received.  The audience had many questions about the technology afterwards and I noticed a few people from the audience posting on Twitter about MEF in the days following the event.

A new job and a tight project deadline kept me from working on my presentation until a couple days prior to the event.  The good news is that I had presented on MEF three times in the past.  The bad news was that the API had changed radically since I developed my original presentation.  So I not only had to expand the presentation to fit the time allotted, I had to completely rewrite my demo to match the current API.  I was up most of Friday night and missed all the morning sessions of the conference to finish on time for my 1PM presentation.   Luckily I finished successfully and the demos went off without a hitch.  

I discovered a blog entry by Brad Abrams that helped immensely.  Brad wrote a set of samples using MEF that I loved for their simplicity.  Don't tell Brad, but I borrowed liberally from his samples to populate one of my demos.

After my presentation, I was able to settle in and enjoy the conference.  I attended two sessions, both in the same room which suited my tired body.  Jennifer Marsman showed a bunch of new features in .Net 3.5.  Next Brian Prince discussed the role of an architect on a project.  I've heard Jennifer and Brian speak many times in the past, so I knew they would be good and I was not disappointed.

I did have time to poke my head into a few presentations long enough to snap a photo or two.  If you heard a clicking coming from the doorway, that was me.

After the event, many of us met at a local watering hole for some food, drink and fellowship.  My new employer Sogeti was kind enough to spring for the food and drinks.  I was well worn down but it was great to reconnect with people who share many of my passions.

I actually volunteered to be something called a "Venue Coordinator" for this event.  But, as this was the fourth time the event was held, the folks at Washtenaw Community College knew everything that needed to be done and delivered to perfection.  I ended up doing no work for this role, so I may volunteer as venue coordinator next year as well.

I took some photos at the event, which you can see here.

I also put together a slideshow with a Warren Zevon soundtrack that you can see and hear below: 

Thursday, October 23, 2008 6:11:42 PM (GMT Daylight Time, UTC+01:00)