MEFfing Disappointed

Working on a highly modular service offering this week, and I took a looksy at the Microsoft Extension Framework as a way to wireup the service components.  I made the choice to pass MEF over, but it was a tough call.  It’s not that MEF doesn’t offer enough features; quite to the contrary I’m pleased with the focus of its feature set.  The reason I didn’t use it was simply that is rubs me the wrong way.  Lemmie splain, and yes this post is a little persnickety, but maybe someone can set me straight…

Things I Don’t Like

MEF requires decoration of dependency objects, and I don’t see the point to that.  Here’s an example of what I mean from the MEF wiki:

1 [Export(typeof(IMessageSender))] 2 public class EmailSender : IMessageSender { 3 ... 4 } 5 6 [Export(typeof(IMessageSender))] 7 public class TCPSender : IMessageSender { 8 ... 9 }

I fail to understand why the Export attribute is necessary in this standard (and according to the MEF wiki, recommended) usage pattern.  I see many issues here…

The ExportAttribute simply duplicates information that is already available via the .NET typing system.  By definition, any public type is exported by the assembly that contains it.  If you argue that you may have public types that you don’t want to export for consumption, I would want to know why they’re public instead of internal types.  So why would I want yet another vector on my type that just duplicates information I already have?

Someone pointed out the ExportMetadataAttribute as a counter to this argument.  I responded by asking why the export metadata shouldn’t be expressed as properties on the object being exported.

It’s not that I mind contract-based object graph wireups like this, but I don’t like that the contract is part of the type definition.  I want to allow the object that is consuming this dependency to choose how to consume it, not the other way around.  In fact, cramming the wireup contract into the type definition is a cost, not a benefit – if you need to modify the wireup, you need to recomplie both the consuming and dependency objects.  Why would I want that when I can specify the wireup elsewhere, like an IoC assembly or XML file?

In short, setting up these exports seems like duplicate work with no payoff.

Things I Do Like (and Some I Would Change)

I like the assembly catalog concept in MEF – of building a list of assemblies the consumer deems appropriate for importing its dependencies.  I like that because enables the application behavior to be dynamic, which is really the point of frameworks like these isn’t it? 

I also like the idea of decorating a consuming object with attributes to describe what they want or need.  Well, sort of.  At first the ImportAttribute and her sisters looked a lot like the ExportAttribute, and I had the same qualms about them:

1 public class Notifier { 2 [ImportMany] 3 public IEnumerable<IMessageSender> Senders {get; set;} 4 public void Notify(string message) { 5 foreach(IMessageSender sender in Senders) 6 sender.Send(message); 7 } 8 }

If the ImportManyAttribute were missing, would anyone looking at the object not know that it needs a collection of IMessageSenders?  Of course not.  So at first I thought it was another redundant framework construct.  Then I realized that it wasn’t expressing a dependency of the object, it was marking a object dependency as a point for extensibility.

In other words, the ImportAttribute says “you can use whatever type you find in the dynamic catalog here.”  That’s beyond expressing a dependency – it’s defining how that dependency should be resolved.  I don’t mind that as much, and I’m even okay with having that expressed in the type definition.  I think I would still prefer expressing the dependency resolution outside of the consuming type, but I’m warming up to the idea.

However, if MEF wants me to work this way I see some significant improvements that can be made.  First and foremost, support for standard object patterns.  E.g., I would prefer to implement the Notifier example this way:

1 public class Notifier { 2 [ImportAsComposite] 3 public IMessageSender Sender {get; set;} 4 public void Notify(string message) { 5 Sender.Send(message); 6 } 7 }

It’s less code, and my intent is just as clear.  Moreover, I would want to be able to apply decorators:

1 public class Notifier { 2 [Import] 3 [DecorateWith(InstrumentingMessageSender)] 4 public IMessageSender Sender {get; set;} 5 public void Notify(string message) { 6 Sender.Send(message); 7 } 8 }

and chains of responsibility:

1 public class Notifier { 2 [ImportAsChain] 3 public IMessageSender Sender {get; set;} 4 public void Notify(string message) { 5 Sender.Send(message); 6 } 7 }

oh and of course default behaviors for when there is no import or chain or whatever:

1 public class Notifier { 2 [ImportAsChain] 3 [Default(NullMessageSender)] 4 public IMessageSender Sender {get; set;} 5 public void Notify(string message) { 6 Sender.Send(message); 7 } 8 }

.. etc, etc, etc.  These are just ideas though, I have no code to back them up, but I certainly want it.  I like these because they help the consuming object express what it wants and how it will behave, and they realistically require nothing unique on the dependency objects.

You smell that too?  It’s another project brewing…

kick it on DotNetKicks.com
E-mail • Permalink • Comments (0)

What is the Metric for Obtuse LINQ?

The more I use LINQ the more I like it.  However I’m starting to wonder when it’s too much.

Check out this code below.  My concern is that the other guy on the project won’t have a clue what it’s doing.  See if you can tell what it’s doing before reading the explanation that follows.  I’m no LINQ expert, so any advice on making this simpler and easier to comprehend is appreciated…

   1: public IEnumerabe<DocumentField> Adapt(IEnumerable<ScanMark> marks )
   2: {
   3:     var fieldGroupMarkValues = (from mark in marks
   4:                                 let monikerParts = mark.Name.Split(new char[] {':'}, 2)
   5:                                 let groupName = monikerParts[0]
   6:                                 let markValue = mark.IsMarked ? monikerParts[1] : null
   7:                                 select new
   8:                                            {
   9:                                                FieldGroup = groupName,
  10:                                                Value = markValue
  11:                                            });
  12:     var fieldGroupValues = from markGroup in fieldGroupMarkValues
  13:                      group markGroup by markGroup.FieldGroup
  14:                      into fields
  15:                      let monikerParts = fields.Key.Split(new char[] {'!'}, 2)
  16:                      let fieldName = monikerParts[0]
  17:                      let groupName = monikerParts[1]
  18:                      let rawValue = ( from f in fields where null != f.Value select f.Value ).ToArray()
  19:                      let zeroValue = ( ! rawValue.Any() ? " " : null )
  20:                      let strayValue = 1 < rawValue.Count() ? "*" : null
  21:                      let fieldValue = rawValue.FirstOrDefault()
  22:                          select new
  23:                                     {
  24:                                         FieldName = fieldName,
  25:                                         GroupName = groupName,
  26:                                         Value = zeroValue ?? strayValue ?? fieldValue
  27:                                     };
  28:  
  29:     return from fieldGroup in fieldGroupValues
  30:                       group fieldGroup by fieldGroup.FieldName into fields
  31:                       let fieldName = fields.Key
  32:                       let value = from g in fields select g.Value
  33:                       select new DocumentField()
  34:                                      {
  35:                                          Name = fieldName,
  36:                                          Value = String.Join( "", value.ToArray() )
  37:                                      };
  38:     
  39:  
  40: }        

In a nutshell, the Adapt method transforms a set of ScanMark objects into a set of DocumentField objects.  The tricky part is that there is a hierarchy encoded into the ScanMark.Name property values, which appear similar to this:

FIELDNAME!GROUPNAME:MARKNAME

Each ScanMark belongs to a named group of marks – GROUPNAME in the example above.  The collection of marks within a group determine a single string value for the group.  There can be three valid states:

  1. The group contains no marks (every ScanMark.IsMarked value is false ).  In this case, the group value is a space (“ ”).
  2. The group contains one mark (only one ScanMark.IsMarked value is true ).  In this case, the group value is the name of the selected ScanMark.
  3. The group contains more than one mark (two or more ScanMark.IsMarked values are true).  In this case, the group value is an asterisk (“*”).

Moreover, the value across a set of groups is concatenated into a single named field value (the name being FIELDNAME from the ScanMark.Name property example).

So my thought process is set-based:

  1. Collate marks across all field groups;
  2. Transform field group mark values into a single field group value;
  3. Collate field group values into a single value for the field.

I’m just not convinced the code says this clearly.  Thoughts?

kick it on DotNetKicks.com
E-mail • Permalink • Comments (4)

Open Sourcing of the ASP.NET Membership PowerShell Provider

Over the past year I've blogged and presented about the benefits of targeting PowerShell as a framework for supporting the applications you develop.  I firmly believe PowerShell is the most appropriate choice of platforms for creating interactive and flexible toolsets.  Today I'm proud to announce that one of the original projects that led to this belief - the ASP.NET Membership PowerShell Provider - is being released by Code Owls LLC as open source.

You can find the project hosted on CodePlex here.

My reasons for this don't really center around wanting to share the code.  That is, I've already written detailed blogs about creating this particular provider, and plan to round those out with a few more posts:

So in my mind, the code is already public.  The primary reason I wanted to get this project public and posted was to get people using it and contributing to the project.  At the moment, the glaring omission is Active Directory support, and this is where I need the most help since I don’t have ready access to an Active Directory environment.  If you’re interested in helping out, by all means contact me through this blog or through the CodePlex project page.

I realize this project may be a bit niche, but its a niche is begging to be filled.  The existing Membership toolset is atrocious, and the applicable PowerShell offering is robust, interactive, and full of chewy goodness.

Enjoy!

kick it on DotNetKicks.com
E-mail • Permalink • Comments (2)

CodeStock 2010: PowerShell as a Tools Platform

At long last, I'm home from my working vacation and have a chance to do some CodeStock postprocessing.  Several people have asked me for the resources from my PowerShell presentation.  You'll find downloadable RARs of the powerpoint and code below.  I've also placed the deck on SlideShare for convenience:

The bulk of the code is described in the following posts:

I will be adding a few posts soon to round out the code coverage.  Feel free to drop me any questions or concerns you have.  I'd love the chance to give this talk to any .NET user groups in the area!

ASPNETMembership.rar (284.35 kb)

PowerShell as a Tools Platform.rar (1.17 mb)

kick it on DotNetKicks.com
E-mail • Permalink • Comments (0)