MEF and XAML integration – Self composition

As I am hoping you heard from Nick, we just shipped MEF Preview 6 which includes a version of MEF for Silverlight 3! At the same time that we’ve been wrapping up on our .NET 4.0 release, we are busy working on a proper release for Silverlight vNext. The things you see in MEF Preview 6 for SL3 are just the beginning.

For SL, we are working on among other things, a set of MEF/XAML integration features. One of the problems we want to address is have parts within XAML which are (optimally automatically) composed. That is elements which normally would have to be manually added to the container in the code-behind in order for their imports to be satisfied. For example you drop a control anywhere in XAML that has imports, and it will get composed, regardless of where it sits within the XAML hierarchy.

In order to do this, we’re exploring a pattern we’re calling “Self composition”, that is an unreachable part (one that the container has no access to) can register itself to get composed. The way this works is the part (UIElement) calls a RegisterForComposition method on it’s load event. Calling the method, forces it to get composed by a host-specific strategy, one which in the case of SL might use an app-wide container, or one that is scoped. The important thing is the grid itself is not coupled to how it is going to get composed. For example, see the sample control below.

image

CustomGrid is a UserControl and it imports a set of IGridBehavior contracts. It’s using MEF for extensibility, thus the application author can deploy behaviors in the container’s catalog, which the grid will use.

So why does the grid need to register itself? Can’t it somehow be created from the container, which will then satisfy it’s imports? Short answer, no. First notice it has no Export, so it will not get picked up by a catalog. But second, the grid itself is declared within XAML, much like any other control, thus the XAML parser is the one who creates it. This would be the case whether it was manually defined within the .XAML file or if the control had been dragged from the toolbox. Thus our only option at that point is to grab the existing instance, and see that it’s imports get satisfied.

But, can’t we somehow take over the creation of the element from XAML, can we have the parser pass of creation to MEF? Actually the XAML 2.0 parser does provide hooks for factories for doing this, however after investigating, there’s a bunch of “devil is in the details” type issues around us doing this, which prevent us from taking this approach, including significant performance hurdles.

One option is to consider using markup extensions, for example an {ImportMany} ME that is a service locator that can be used within XAML. This would allow setting a Behaviors Dependency Property to all imports of IGridBehavior. This could work, however it means that every user of the grid, must explicitly specify one or more markup  extensions each time they use the grid, something that is leaky, and very poor from a usability perspective. As a side note, we are actually looking to add Markup Extensions for explicitly specifying imports on Dependency Properties, but that is geared more towards the user of the control vs the author.

Another option is to consider using attached properties, for example a “Compose” attached property would allow specifying Compose=True on our Grid.

image

Although this is less painful then having to specify all the gory details, it still means that the user of the control must explicitly set this wherever the use it. It means the control author cannot transparently decide to start using MEF without impacting every single XAML file that has ever sited that control.

Now all that being said, in general we’re of mixed feelings on the team on the self-registration approach:

On the down-side: 

  • Having parts have to compose themselves is a violation of SOC, in an ideal world, a part should never be responsible for telling itself to get compose. It’s generally a bad idea.
  • It’s a DRY Violation, my part has imports, yet I still have to “Tell” it to compose itself.
  • It poses several challenges relating to lifetime management.

On the up-side:

  • It allows for components which are instantiated by the runtime (in this case FrameworkElements in XAML), and normally unreachable by the container to be able to leverage MEF. This means frameworks (including our own) and applications can gradually migrate to leverage MEF rather than having to be completely re-written / designed in order to leverage composition.  It also means that you can use MEF where you see fit, regardless of whether the framework infrastructure supports it.
  • The pattern can even be extended outside of XAML, for example the same approach could work in Winforms, or ASP.NET Web Forms, or even Mobile. This is possible, because the design relies on a host-specific strategy, thus ASP.NET could provide a per-request container, etc.
  • Self-registered parts are not coupled to a specific container instance. It’s up to the host strategy to decide the container.

What do you think? Do the pros outweigh the cons? Or should we hold off on introducing such functionality because it will cause more harm than good?

Your candid feedback is appreciated.

This entry was posted in MEF, silverlight, XAML. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://codebetter.com/members/gblock/default.aspx Glenn Block

    @Mike problem with the attached behavior, is it feels unnatural to set that in code (which one would be required to do if deriving from an existing control). Also it ONLY supports dependency objects. This approach would allow non-DOs to get composed inline.

  • http://codebetter.com/members/gblock/default.aspx Glenn Block

    BTW Ward, you are saying static composition. What about this is static? Yes, you make a static call, but that call only expresses “I need composition” in the same way as raising an event. We’re not allowing you to depend on a static container instance or something (Container.Current)

    Glenn

  • http://codebetter.com/members/gblock/default.aspx Glenn Block

    @Ward, the cost is singifcant. I can’t go into all the details here, but you will just have to trust me. Suffice is to say, there is the cost of doing the work itself, and there is a high perf cost we would need to optimize for even if we could pull it off.

    As far as resources for VM, I would not be recommending using RegisterForComposition on the VM. If at all you can have the container create things you should. So in the case of a VM, I would say import the VM on the View which you will be able to do in two ways, one is have in import on the view, say SalesOrderViewModel, or two use the new Import Markup Extension we are planning in the XAML.

    We are only introducing this pattern out there to address places where instantiation is controlled by the framework itself, and where we have no other option.

    As a side note, we are looking into an alternative approach that would allow adding hooks higher-level in the UI stack (not the parser) which we might be able to use to automatically determine whether something needs to be composed, and if so do it. The RegisterForComposition approach would be a fallback if we cannot get this work through.

  • Ward Bell

    Thanks, Glen. This is extremely helpful communication.

    You know I feel uneasy about this … no more uneasy than you, or Kent, or anyone here. For one, I’m not loving the use of the static Composition.

    I really want to tell the XAML how to parse … I want to put something in the parse pipeline. Of course I don’t want to give up “too much” perf to get it. I’m not feeling like I have a handle on the costs so I have to take your word for it that it is “too expensive.” These words are in quotes because they are indicative without having specific meaning. I’m with Kent on trying to put some pressure on your conclusion that it has to be “too expensive”.

    Another symptom. I want DI just as much for my resources as I do for my controls. You may have noticed interest in having the DataContext be initialized in XAML with a ViewModel (VM). All of my interesting VM’s lean on DI (or would lean on MEF). I suppose you’re saying that I should call Compose on my VM in the Loaded method too … if, as with the control, I wish to delay composition beyond instantiation to the moment of “load”. My code-behind is loading up.

    Just thinking out loud. I don’t have a better idea. Thanks for putting this out there so we can mull it over and, best of all, see how you are evaluating the options.

  • http://azurecoding.net Mike Brown

    Glenn,
    Glad to see that this finally made it into a release. I’ll kick around the tires and see how it feels. It looks like a lot of thought has gone into this system.

    You already know how I feel re using an attached behavior vs. code-behind, but as I mentioned before, I think your syntax makes creating an attached behavior for it trivial. Great going!

  • http://codebetter.com/members/gblock/default.aspx Glenn Block

    Kent, that would work fine for a user control, but what if I am deriving from a sealed control like Button, where there is no xaml?

    Then I would have to set the attached behavior in code, in which case it feels more natural to just call a method. Furthermore it introduces an inconsistency by having two different models.

    Now one other possibility is exploring control templates / styles and set it there, though that feels a bit weird.

  • http://kentb.blogspot.com Kent Boogaart

    To me, the pros win, but it would be ideal if the performance issues with the XAML hooks could be rectified. Even if you still support manual composition for other platforms, it would be great if it was unneccessary for SL.

    Also, I don’t understand this point re attached behavior:

    > Although this is less painful then having to specify all the gory details, it still means that the user of the control must explicitly set this wherever the use it

    Why can’t the author of the control’s XAML just stick the attached property on the root element?

    c:Composition.Compose="True">

  • http://codebetter.com/members/gblock/default.aspx Glenn Block

    Joe

    Ultimately we could support scoping through the Visual Tree / Dependency property system. At a minimum we would need an app-level container at the highest level, with potentially scoped containers as children. The verdict is still out whether we would support scoping in the first version.

    As far as the generic container interface, you mean IContainerFacade / Common Service Locator? Where do you see that coming to play in the self-composition scenario?

    A

  • joe

    Wouldn’t this work by simply locating the container by going up the visual tree. The container would be responsible for resolving imports. I don’t see any SOC problems there… unless I’m missing something. This could be a generic container interface, PRISM style.

  • http://codebetter.com/members/gblock/default.aspx Glenn Block

    @Gunther what I meant by not coupled, is the part is not coupled to a specific host setup. It might be composed by a gobal container, or a scoped container.

    As far as having deeper XAML integration, we definitely want that, and are going to explore this much more deeply post .NET 4.0.

    However, the benefit of this approach (I thnk) as I mentioned is it works beyond XAML, for example ASP.NET, Mobile, etc. For those environments XAML won’t help you.

  • alwink

    Would it be possible to tell the container to look at all the controls in the XAML hierarchy, checks the type of those controls, looks for imports on that type. Then the container hooks to the Loaded event of the controls that have imports and fulfills the dependencies. That way you only have to compose the top-level ‘form’ (I don’t know how thats called in XAML) and all children will be composed automatically.
    - No need for every control to compose itself.
    - No DRY violation on every control, [Import] is enough.
    - Maybe lifetime management can be done using the lifetime of the top-level ‘form’?

    No idea if this works, because I’ve never used MEF or XAML at all :)

  • GuntherM1466

    I must admit that I’m not thrilled by this approach. I think you said it correctly above “something that is leaky, and very poor from a usability perspective”.

    I would expect that the composition.compose property would be applied very inconsistently. Assuming this is the case, what are the side effects?

    Finally, can you clarify why “Self-registered parts are not coupled to a specific container instance” is in the upside section? I would think this could be achieved as well if MEF is integrated into the parser, as long as the integration is well designed and configurable. What am I missing?

    Glad to see you’re working on this now :-)

    Gunther

  • http://codebetter.com/members/gblock/default.aspx Glenn Block

    Thanks Rick, my personal lean is the same, even though I really don’t love it.

    We actually have some ideas around lifetime. For example, we could have the mechanism such that we subscribe to the control closing event, so that when it is closed, all transient (non-shared) resources that were created within that user control are released / disposed.

  • http://blogs.compdj.com/ Rick Ratayczak

    I think it would be good. Even though there are some drawbacks, especially the lifetime mgmt. The benefits outweigh the drawbacks.