nRoute and MEF

Jul 11, 2010 at 8:03 AM

I wanted to start this discussion to talk about best practises when using nRoute and MEF.  Currently im trying to use MEF and nRoute to build a new composable application and have some interesting thoughts and challenges.  Consider the following scenario - a main shell view contains a 'library' control that can host different views (user controls).  I use nRoute to build a suite of these library views by creating various UserControls each with a ViewModel (VM), then bind the 2 together using the [MapViewModel] attribute on the VM and the BridgeViewModelBehavior in the view.  The library itself is also a UserControl with a VM bridged in the same way.  The library view then contains a tab control with the ItemSource bound to an observable collection in the library VM that i am trying to build dynamically at runtime use MEF and the [ImportMany] attribute.

The problem i am facing is that when MEF goes through the object graph to compose the application and resolve [Import] definitions, it does not do so for the VMs.  The reason for this is that by using nRoute attributes to bind the two together, the VM is already nicely decoupled from the view and hence MEF does not know to go and resolve any [Import] definitions in the VMs.  I am looking for a nice clean way to solve this problem so that MEF will work, but i can still use the nice attribute driven way to decouple the view/VM with nRoute.  Have you encountered this situation at all or can think of a way to work around it?

Aso the MEF framework is very well designed and im finding it extremely useful - do you have any plans to integrate some of your composition stuff more with MEF in the future?

Coordinator
Jul 11, 2010 at 10:38 AM

Hi Xcalibur.. Now, sometime last year I did evaluate using MEF in nRoute, I even wrote a bit about it here http://www.orktane.com/Blog/post/2010/01/04/nRoute-Resource-Locator-Framework-(Part-I).aspx. However since then I've held back on using MEF, because RLF (Resource Locator Framework) in nRoute has evolved into something that can stand to MEF feature-to-feature. Now, no doubt MEF is great and really well developed, but RLF can cover at least 90% of the things that MEF can do and then some more - like in your scenario, where I understand you want recomposition support, you can do it using RLF by resolving for either of the three types (where T is your resource type):

  • ReadOnlyObservableCollection<T>
  • ReadOnlyObservableCollection<Lazy<T>>
  • ReadOnlyObservableCollection<Lazy<T, Metadata>>

The use of these three types has been specifically designed to be ensure you don't inevitably leak memory; recomposition is tricky business and I have taken special care to ensure you are both abstracted and weakly-connected to underlying framework. Read more about it here http://www.orktane.com/Blog/post/2010/04/13/nRoute-Now-More-Wholesome.aspx

Let me also point out couple of things about RFL and MEF:

- As you know there are two parts to IoC/DI - one exposing your resources (ie. Export) and the second, resolving your exposed resources (ie. Import), now RLF supports customizing and hooking into both ends of the pipeline. For example, you can expose your "search provides" with a custom [MapSearchProvider] or [ExportSearchProvider] attribute, and both MEF and RLF allow custom exports/mapping. However, with RLF you can also create a custom [ResolveSearchProvider] attribute and inject your custom logic in the resolving of the resource - so for example, if the customer's preference is for Bing then you could inject a Bing search provider, whereas for everyone else you could resolve a Google provider.

[MapViewModel(typeof(CustomerView))]
public class CustomerViewModel : ViewModelBase
{
	[ResolveConstructor]
	public CustomerViewModel([ResolveSearchProvideer]ISearchProvider searchProvider)
	{
		// ...
	}
}

This is a very powerful tool that can let you "inject" your domain specific logic into your application without creating overt dependencies. Have a look at [ResolveViewModel] attribute in nRoute to see how it can be done. 

- Another related thing is so-called "higher order dependencies" (see http://nblumhardt.com/2010/01/the-relationship-zoo/), this is also something that RLF supports by allowing adapters that hook into the resolving end of the IoC/DI chain. Now, MEF has limited support by the way of couple of predefined types like IEnumerable<T>, Lazy<T> and Lazy<T,Metadata> - in nRoute you not just limited to the built in types, you are free to add your own. In addition to the recomposition types, the following types are supported out of the box:

  • T[]
  • Func<T>
  • Lazy<T>
  • Lazy<T, Metadata>
  • Future<T>
  • List<T>
  • ICollection<T>
  • IQueryable<T>
  • IEnumerable<T>
  • IEnumerable<Func<T>>
  • IEnumerable<Lazy<T>>
  • IEnumerable<Lazy<T, TMetadata>>
  • IChannel<T>

Also see the Common Context Adapters project http://cca.codeplex.com/ to see how this allows you keep out the IoC/DI business out of your domain

- Another thing, if you read MEF's documentation it says it doesn't support multi-threaded use, well RLF does support multi-threaded use - and this was a big deal for not wanting to use MEF

- One of the big difference between MEF and RLF is MEF's use of catalogs, this is something that RLF doesn't support - as in you can't package some set of resources/dependencies as catalogs and then add or remove them at runtime. This maybe be my shortsightedness as I din't see the need to packaging things as catalogs and even to remove them - but understand I designed this thing with Silverlight in mind, which doesn't allow you to unload types once loaded. My design goal was to make it super fast and with as little contention as possible. Therefore in RLF, internally we open a catalog for every type of resource exposed - so for example ISearchProvider type resources will have its own catalog, and ICustomerService will have its own. This way they can be resolved in parallel without the expensive look-up and locking costs associated with having all resources in one catalog - this also goes towards easing possible thread-switching costs inherent in multi-threaded use. Another benefit of "automatic catalogs", as it were, is that there is no need to do any kind of setup of catalogs etc.

- Also I don't discuss this much, but like in MEF, with RLF you expose any resource as an instance of itself or a particular type. For example if you put [Export] attribute on MyCustomView user-control then you are exposing/exporting MyCustomView type, but you could also expose it as a user control type by specifying the type [Export(typeof(UserControl))] - now the equivalent of this with RLF is [MapResource] as an instance of itself or [MapResource(typeof(UserControl))] as a UserControl type. And the equivalent of [Import] in RLF is [ResolveResource], they both also allow you to resolve resources by names and/or specific types

- RLF also doesn't support exporting methods or properties, because I'm not sure how useful it is and secondly it can cause memory leaks like nothing else can!

- One piece in RLF that gives you enormous flexibility is the use of IResourceLocator, it essentially outsources the locating of resources to you and on a per type basis. Which means you can resolve two instances of say IDialogService totally differently to each other, one can say do type probing whereas the other can say forward the call to the NavigationService. It is a bit crude, but having the power/access under the hood is a valuable thing when you need it

- Also, this is not that big of a thing but it is/was a consideration, MEF's dlls file size is bigger than the whole of nRoute and every feature in it including RLF - and so if I use MEF instead of RLF I will probably be doubling the size of nRoute 

Having said all of above, there are a number of ways I want to improve RLF, for one I want to provide procedural access to registering resources, two simplify the combination of IResourceLocator, Metadata, and Custom Attribute required to create custom exports, and lastly provide inherited mapping capability. Nonetheless, as I promised someone else, I'm going to have a look at MEF again and see if I build the same set of features atop MEF.

Lastly, about the resolving nRoute based dependencies whilst using [Import] in MEF - I'm not sure how you can fit in the pipeline but you can programatically resolve a type of T by using nRoute.Components.Composition.TypeBuilder.BuildType<T>(). Nonetheless, try using the ReadOnlyObservableCollection to see if it works for you.

Cheers,
Rishi 

 

Jul 13, 2010 at 4:10 PM
Edited Jul 14, 2010 at 8:27 AM

Hi Rishi - the RLF is indeed very powerful - when looking at it before there was one part i couldnt see demonstrated in your RLF sample app that MEF provides. This is resolving a collection of resources wihc i need in the scenario i described above to dynamically resolve a list of views i can inject into the tab control via a collection in the VM. Taking this step by step, using RLF i am exposing my views by putting a [MapResource(typeof(UserControl), "LibrarySection")] attribute on the view codebehind (no metadata used just yet but this should be a simple extension of the example). Then in the library view model i should be able to resolve a list of these views in the constructor like so:

[ResolveConstructor]
public LibraryViewModel([ResolveResource("LibrarySection")] ReadOnlyObservableCollection<UserControl> views)
{
}

This kind of works - in that only one of the views gets resolved (i have 2 defined currently). I know that both are discoverable as removing one of them resolves the other one and vice versa. So my first question is what is causing this. Now ideally i dont want to resolve the list of views via the constructor - rather i want to do it directly into a public property that the tab control can bind to. In MEF i can do this:

[ImportMany("LibrarySection")]
public ReadOnlyObservableCollection<UserControl> Views
{
    get;
    private set;
}

Im not sure how to achieve this using RLF. I have tried the following but it does not resolve anything (as in, it seems that RLF is not going through the entire VM when created and fulfiling all [ResolveXXX] attributes):

[ResolveResource("LibrarySection")]
public ReadOnlyObservableCollection<Lazy<UserControl>> Views
{
    get;
    set;
}

Can you please give me an example of how to achieve this please - note i am using strings to differentiate the library sections at the moment as i will have many other types of view in the application that will be loaded into other sections.

cheers

Coordinator
Jul 14, 2010 at 1:08 PM
Edited Jul 14, 2010 at 1:53 PM

The reason it doesn't work as you expect is because in RLF a resource name is supposed to represent only a single resource and not a grouping or "contract name" as in MEF. And I did it so as to enable particular scenarios where you had to work directly against a single registered resource, such as with Modules or ViewServices. Now it might be possible to extend RLF to target "grouped" resources capturing a group-name and filtering the resources. So, below I've quickly whipped up a meta-data class that holds the "group name" along with an attribute that captures it. Both these classes just extend the [MapResource] related metadata and attribute classes (note there is no DefineResource attribute):

public class GroupedResourceMeta
    : ResourceMeta
{
    private readonly string _groupName;

    public GroupedResourceMeta(ResourceMeta meta, string groupName) 
        : this(meta.ResourceType, meta.ImplementationType, groupName, meta.Lifetime) { }

    public GroupedResourceMeta(Type resourceType, Type implementationType, string groupName, InstanceLifetime lifetime)
        : base(resourceType, implementationType, lifetime)
    {
        if (string.IsNullOrEmpty(groupName)) throw new ArgumentNullException("groupName");
        _groupName = groupName;
    }

    public string GroupName
    {
        get { return _groupName; }
    }
}

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true, Inherited = false)]
public class MapGroupedResourceAttribute
    : MapResourceAttribute
{
    private readonly string _groupName;

    public MapGroupedResourceAttribute(string groupName)
		: this(null, null, groupName, InstanceLifetime.PerInstance) { }

    public MapGroupedResourceAttribute(string name, string groupName)
		: this(null, name, groupName, InstanceLifetime.PerInstance) { }

    public MapGroupedResourceAttribute(string groupName, InstanceLifetime lifetime)
		: this(null, null, groupName, lifetime) { }

    public MapGroupedResourceAttribute(string name, string groupName, InstanceLifetime lifetime)
		: this(null, name, groupName, lifetime) { }

    public MapGroupedResourceAttribute(Type resourceType, string groupName)
		: this(resourceType, null, groupName, InstanceLifetime.PerInstance) { }

    public MapGroupedResourceAttribute(Type resourceType, string name, string groupName)
		: this(resourceType, name, groupName, InstanceLifetime.PerInstance) { }

    public MapGroupedResourceAttribute(Type resourceType, string groupName, InstanceLifetime lifetime)
		: this(resourceType, null, groupName, lifetime) { }

    public MapGroupedResourceAttribute(Type resourceType, string name, string groupName, InstanceLifetime lifetime)
        : base(resourceType, name, lifetime)
    {
        if (string.IsNullOrEmpty(groupName)) throw new ArgumentNullException("groupName"); 
        _groupName = groupName;
    }

#region Override

    protected override IResourceLocator GetResourceLocator(Type targetType)
    {
        var _locator = (DefaultResourceLocator)base.GetResourceLocator(targetType);
        return new DefaultResourceLocator(_locator.ResourceName, new GroupedResourceMeta(_locator.ResourceMeta, _groupName));
    }

#endregion

}

 

Now its use is quite simple, rather than [MapResource] your would use like [MapGroupedResource("LibrarySection")]. Well, this should take care of the export/exposing part, on the importing/resolving part you will need to filter things a bit by using the metadata:

public class Consumer
{
    [ResolveConstructor]
    public Consumer(IEnumerable<Lazy<UserControl, GroupedResourceMeta>> groupedResources)
    {
        var _filteredResources = groupedResources.Where((l) => l.Metadata.GroupName == "LibrarySection")
            .Select((l) => l.Value);
        // use it..
    }
}


Above by virtue of specifying Lazy<T, GroupedResourceMeta> nRoute will only return resources with grouped metadata, however still we might want to just get a specific group by name so we use the where and select clauses. The above is workable but by using another feature in RLF we can maybe make it a bit better - like I mentioned earlier, in nRoute you can inject yourself in the importing/resolving pipeline, and so we can write an attribute like:

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
public class ResolveGroupedResourcesAttribute
    : ResolveResourceBaseAttribute
{
    private readonly static Type ENUM_GENERIC_TYPE = typeof(IEnumerable<>);

    private readonly string _groupName;

    public ResolveGroupedResourcesAttribute(string groupName)
    {
        if (string.IsNullOrEmpty(groupName)) throw new ArgumentNullException("groupName");
        _groupName = groupName;            
    }

    protected override Object ResolveResource(Type targetType)
    {
        if (!targetType.IsAssignableFrom(ENUM_GENERIC_TYPE)) throw new InvalidOperationException();

        var _genericArgumentType = targetType.GetGenericTypeDefinition();
        if (ENUM_GENERIC_TYPE != _genericArgumentType) throw new NotSupportedException();

        var _genericParameterType = targetType.GetGenericArguments()[0];
        var _filter = (GroupFilter)TypeActivator.CreateInstance(typeof(GroupFilter<>).MakeGenericType(_genericParameterType));
        return _filter.Filter(_groupName);            
    }

    public abstract class GroupFilter
    {
        public abstract IEnumerable Filter(string groupName);
    }

    public class GroupFilter<T> : GroupFilter
    {
        public override IEnumerable Filter(string groupName)
        {
            var _list = ResourceLocator.GetResource<IEnumerable<Lazy<T, GroupedResourceMeta>>>();
            return _list.Where((l) => string.Equals(l.Metadata.GroupName, groupName, StringComparison.OrdinalIgnoreCase))
                .Select((l) => l.Value);
        }
    }
}

 

Well the above attribute just does the same thing as before, which is it filters the listing based on the group name (except it has to do it in a non-generic world), but once written it can be used everywhere like:

public class Consumer2
{
    [ResolveConstructor]
    public Consumer2([ResolveGroupedResources("LibrarySection")]IEnumerable<UserControl> groupedResources)
    {
        // use it..
    }
}


Having the [ResolveGroupResources] attribute basically intercedes the resolving process and takes care of the resolving - this is a very powerful feature as using custom attributes you could resolve bring in MEF to resolve dependencies (as you asked). Hope this helps, sorry I'm bit in a hurry for now so I've not tested the code but does compile :)
Rishi

Jul 14, 2010 at 2:08 PM
Edited Jul 14, 2010 at 2:09 PM

Hi Rishi - as always thanks for the thorough reply, im going to try it out right now.  A few questions after quickly scanning through the answer (Sorry if they are already answered):

1) Given that RLF already supports resolving ReadOnlyObservableCollections (and hence i assume groups of objects), is the above really how it needs to be done or is there another way you intended it to work that supports resolving groups of items right now out of the box?

2) I cant see how this will solve the problem i am having of resolving the collection property directly, without having to use the constructor

Coordinator
Jul 14, 2010 at 2:49 PM
Edited Jul 14, 2010 at 3:59 PM

Look as of now there is no out-of-the-box way to get a sub-set of resources based on "group name" or "contract name" as we don't even capture any group-name, you can get all the resources along with the metadata then filter them yourself in code. Names as they are now are supposed to be unique identifiers for every resource type, so for example you should not be able to have two "UserControl" type resources registered with the same name. You can have UserControlA and UserControlB types stored with the same name, but not at the abstraction level of UserControl. Anyway, this is something I can change - in fact RLF is something I want to optimize the most, since I feel it has too many moving parts and I can look into switching from a name based to a contract name based registering approach if that is indeed the more predominant type of scenario.

Secondly, you can put the [ResolveGroupResources] or a [ResolveResource] attribute onto a property, it should work (note it has to be public type for SL). Below is another version of the attribute, just this time it is designed to work with anything that asks for a derivate of IEnumerable<T> including ReadOnlyCollection<T>. I'm not sure if it will work 100%, have a go and let me know:

 

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
public class ResolveGroupedResourcesAttribute
    : ResolveResourceBaseAttribute
{        
    private readonly string _groupName;

    public ResolveGroupedResourcesAttribute(string groupName)
    {
        if (string.IsNullOrEmpty(groupName)) throw new ArgumentNullException("groupName");
        _groupName = groupName;
    }

    protected override Object ResolveResource(Type targetType)
    {   
        // making assumption that the asked for type is supported
        var _genericArgumentType = targetType.GetGenericTypeDefinition();            
        var _genericParameterType = targetType.GetGenericArguments()[0];
            
        var _lazyMetaType = typeof(Lazy<,>).MakeGenericType(_genericParameterType, typeof(GroupedResourceMeta));
        var _lazyMetaOfGenericType = _genericArgumentType.MakeGenericType(_lazyMetaType);

        var _filterType = typeof(GroupFilter<,>).MakeGenericType(_genericParameterType, _lazyMetaOfGenericType);
        var _filter = (GroupFilter)TypeActivator.CreateInstance(_filterType);
        return _filter.Filter(_groupName);
    }

    public abstract class GroupFilter
    {
        public abstract IEnumerable Filter(string groupName);
    }

    public class GroupFilter<T, C> : GroupFilter
        where 
        C : IEnumerable<Lazy<T, GroupedResourceMeta>>
    {
        public override IEnumerable Filter(string groupName)
        {
            var _list = (C)ResourceLocator.GetResource(typeof(C));
            return _list.Where((l) => string.Equals(l.Metadata.GroupName, groupName, StringComparison.OrdinalIgnoreCase))
                .Select((l) => l.Value);
} } }

Cheers,
Rishi 

Jul 14, 2010 at 3:35 PM

Appreciate your help on this.  Threw the following error when trying to run the BridgeViewModel behavior:

{"Unable to cast object of type 'WhereSelectEnumerableIterator`2[nRoute.Components.Lazy`2[System.Windows.Controls.UserControl,Worship.Contracts.GroupedResourceMeta],System.Windows.Controls.UserControl]' to type 'System.Collections.ObjectModel.ReadOnlyObservableCollection`1[System.Windows.Controls.UserControl]'."}

Works fine if the property is IEnumerable<UserControl> though.

I assume that when i do want to attach metadata, and hence retrieve Lazy<T, Metadata> objects, i will still be able to do this by creating derived classes/attributes from the GroupedResourceMeta class and associated custom attribute?  For example i want to provide a 'Title' string metadata that can then be used to bind the tab header with MetaData.Title.

Coordinator
Jul 14, 2010 at 4:50 PM

Opps, my bad. The Select statement transforms the C<Lazy<T, GroupedResourceMeta>> into an IEnumerable<T> which is why it works with IE<T>. I wanted to actually transform a C<Lazy<T, GroupedResourceMeta>> to a C<T> where C<T> is what you requested (in your case you are asking for ReadOnlyObservableCollection<UserControl>) but I just realized there is no generic/universal way to do a C<Lazy<T, GroupedResourceMeta>> to a C<T>. Well, another problem with putting a Linq based where clause to a ObservableCollection is that you'll not another Observable, hence one will not get any updates - here Rx can help as it can filter and pass on, but that's not what we want..

So I suppose the best way to go about it would be do a manual filter in your code, and ask for ReadOnlyObservableCollection<Lazy<UserControl, GroupedResourceMeta>> - this way everytime the resource collection changes you will get notified, but before you use it you can check if the GroupName is what you want to process via the metadata. Sorry it is ugly, or we can exclusively create a [ResolveObservableGroupedResource] attribute to process only ReadOnlyObservableCollections which will then intermediate and filter results as per group name..?

Rishi 

 

Jul 19, 2010 at 2:12 PM

Sorry for the late response - work took over for the last few days.  Ultimately what i want to see is a similar setup to MEF, that is a new attribute, lets call it [ResolveAllResources] that seperates the issue of cardinality from the data itself.  The problem i have with the above solution is that it forces me to resolve my dependencies in the constructor and adds composition logic to the consuming class.  None of these are showstoppers, but less than desirable.  Due to nRoutes extensible nature i believe my goal can be reached quite easily - you mentioned that RLF is an area you would like to focus on, i think this would be a fantastic area to try and improve on.  Im a little surprised that i seem to be the first one to really require such functionality :)  I have little time at the moment due to work, but if you can give me an outline of how to extend RLF to support this scenario more 'out of the box' i would be happy to try and implement something when i get a chance.

Coordinator
Jul 19, 2010 at 4:11 PM
Hi, I just want to be clear you want to implement something like MEF's inherited exports or you want something like a conventions based auto-mapping capability?

Rishi
>
Jul 20, 2010 at 6:03 AM
InheritedExport is a nice to have, but really just saves a little bit of code.
I would like to see support for "conventions based auto-mapping capability" as you put it for a cardinality of 0-many. Now that would be awesome
Coordinator
Jul 20, 2010 at 8:18 PM
Edited Jul 20, 2010 at 8:29 PM

Let me give you an overview of how RLF works, I hope you are sitting.. ;) just kidding..

First off, as of now, to implement something like conventions based mapping you'll need it do from the inside of nRoute as I have purposefully not exposed the necessary API. I wanted to open it, but was not sure about the implications and then did not have enough time to study it. Secondly, the most important classes with regards what you want to are:

1. Resource abstract class, 
2. Resource<T> class, 
3. AssemblyMapper class, and 
4. TypeBuilder<T>

Let me just take you step-by-step what happens and how nRotue maps its resources and I believe this is what you intent to change.

- It all starts with the nRoute's bootstrapper (nRouteApplicationService class) which makes a hard/internal call to Resource abstract class' initialize method.

- The initialize method registers with Channel<MappedAssemblyInfo> to listen into info about every assembly mapped by the AssemblyMapper. Next it tells the AssemblyMapper go map all the Application's assemblies. Note the use of the word "map" is a little disingenuous as the AssemblyMapper really doesn't do the mapping it just locates the assemblies and publishes info about it. However it does keep a registry of all assemblies it has "mapped" to enable components loading in late in the lifecycle to catch up as it were.

- The AssemblyMapper then goes and looks into all the assemblies listed in the application's manifest (or loaded in the app's domain in WPF) and studiously publishes info about every assembly it located through the aforementioned channel

- Since the Resource abstract class is listening for any assemblies found, when it gets news of a new assembly it does two things in order.
1. It use a static helper method in AssemblyMapper called GetAttributesInMappedAssembly to get all "MapAsKnownResource" derived attributes - it does this before anything else because we can use this attribute to register something as a future to be resource and hence not causing "not found" type of exceptions
2. It then use the same static GetAttributesInMappedAssembly helper method to get all the assemblies that derive from "MapResourceBase" attribute. This attribute provides us four bits of information:

a. What type of resource is it? This determines the catalog it will be registered in, remember I said each resource type get its own catalog 
b. Can we initialize the resource mapping immediately, if not now it gets put in a pending queue, and the pending mappings get re-evaluated whenever a new assembly is loaded
c. Is it the default resource of it's type
d. Give us the IResourceLocator for it

- The IResourceLocator is the critical piece that actually does the realizing of a resource ie. instantiating (mostly though everyone delegates to TypeBuilder<T>). An IResourceLocator implementation provides for three things:
a. Resource Name (this one needs to be unique, at least for now and cannot be null) 
b. Resource Metadata Class (this can actually be whatever, as in two resources of the same type can have different base classes and there is no base class that defines a metadata object)
c. and it provides a GetResourceInstance method that actually gives a resource instance as the name suggests

- Now, as I mentioned each resource has it's own catalog - well the catalog is of type Resource<T>. And the Resource<T> maintains a dictionary of IResourceLocator. Oh and BTW, the Resource<T> inherits from Resource abstract class - we do this to provide non-generic access to each catalog. 

- Now, to register anything you use the Resource class' RegisterResourceLocator method and it takes in IResouceLocator and a bool value as to if it is the default resource. This class internally looks up for the right Resource<T> catalog and asks it to register the locator. For now, this is the only way to get into the catalogs and this should also be your entry point. Further, the "is default resource" might not be important in a lot of scenarios but it is very important in others, and one can access a default resource of a type by using Resource<T>.Catalog.Default else you need to specify Resource<T>.Catalog["nameOfResource"] to get hold of a particular resource

- I'll also point out that ResourceLocator static class is just a helper/wrapper around the functionality found in Resource and Resource<T> classes. Also because the IResourceLocator catalogs are stored in the generic versions i.e. the Resource<T> it is always better/preferable to use the generic version. However, the Resource class maintains a master-catalog of all catalogs, so it has to do an extra look-up to find a catalog of a resource type. But as you will find out that in DI/IoC world you have do most of your work in the non-generic world :) 

So the most important take aways should be:

1. Use the Channel<MappedAssemblyInfo> to listen into new assemblies that are being mapped. Right now, there is no way to unload or unregister an assembly. 
2. You need to generalize or "function-alize" your idea of convections, and specifically match them with they type of resource they will register - for example a View-ViewModel pairing in nRoute is registered with the Resource<IViewModelProvider> catalog, so if you find conventions then realize them as IViewModelProvider. 
3. Probably you also need to create some sort of enumerator like GetAttributesInMappedAssembly that will help you extract your known conventions
4. And register them by creating the convention's appropriate IResourceLocator, you'll find every type of resource there will already be something like "Default_____Locator"

Essentially you have to replace the functionality provided in the MapResourceBase attribure - simple as that.

Hope all this is clear/understood, and let's start with the "exporting/mapping" part then we can explore the "importing/resolving" to go even further.

Cheers,
Rishi

Jul 22, 2010 at 7:04 AM
Edited Jul 22, 2010 at 7:10 AM
Cool im glad to hear that because my solution in the interim has been to do just that. I wanted a solution where i could use MEF but keep using the attribute/behavior driven VM binding. What i have done is to subclass the BridgeViewModelBehavior like so:
public class BridgeAndComposeViewModelBehavior : BridgeViewModelBehavior
{
    protected override void OnAttached()
    {
        base.OnAttached();

        // At this point the ViewModel has been resolved by nRoute so we can compose it using MEF
        var vm = base.AssociatedObject.DataContext;
        CompositionInitializer.SatisfyImports(vm);
    }
}

This means i can simply replace the behavior in the view and have it automatically bind the ViewModel, then at the same time resolve any dependencies using MEF. I should mention that the above code uses CompositionInitializer to perform the resolution without having to manually bootstrap MEF. Now this is provided out of the box in Silverlight 4.0, but in WPF they didnt get it into .NET 4 for some reason (!?!?). Fortunately one of the team members has made a desktop version which you can download here to use in conjunction with the above behavior.

Thanks for all the info - once my current work commitment has cleared ill be looking closely at what i can do with this.

Coordinator
Jul 22, 2010 at 9:30 AM
Edited Jul 22, 2010 at 8:11 PM

Seems cool, but I'm just wondering to what benefit? As in what are you doing post-initialization because above nRoute would have resolved your dependencies? Is there some xaml based composition or? Also, the reason they didn't get the CompositionInitializer in was because for the .NET framework releases they have to check in code way-way early, and thereafter they can only debug it but not add features. So Silverlight 4's MEF feature-set is ahead of .NET 4.

Rishi

Jul 22, 2010 at 2:33 PM

The benefit of using MEF, at least as a short-term solution, is that right now, out of the box it supports resolving collections of objects using ImportMany, wheras RLF does not do this as you have explained without some work.  I fully intend to use what you have explained to improve this situation in the long term, this is just the most elegant way i could see of getting the results in the short term so i can complete this work

Coordinator
Jul 22, 2010 at 8:30 PM

Got it.. and I was just thinking I'd like to ensure that in the next release RLF's extensibility points are both well-defined and brightly-identified, so if somebody wants to do xml based configuration they would be welcome to do so :)

Rishi

Jul 23, 2010 at 3:46 AM
I'd be keen to explore adding in a Resolver that uses Ninject to resolve dependencies that the RLF doesn't have defined.
Coordinator
Jul 23, 2010 at 5:00 PM

@dbokely cool, though I'd love to know the reason? Like what kind of dependencies would be external to RLF, and if so for what technical/design reasons? Secondly, would you want to do this with the current version or the next release which should have a better extensibility story with RLF?

Cheers,
Rishi

Jul 24, 2010 at 1:00 PM
I'm looking for convention-based auto-wiring, for the general cases, rather than attribute-based mappings. Unless the RLF already has that and I can't see it?
Coordinator
Jul 24, 2010 at 1:38 PM
Edited Jul 25, 2010 at 3:21 PM

@dbokely, no nRoute as of now doesn't have convention based auto-wiring enabled, which is why we were discussing how we can put in extensibility points to enable such scenarios. On a related point, with conventions based auto-wiring one of the issues is how do we capture metdata as we do with attributes, for example, each navigable Views needs to have a Url associated with it. So how do we capture that? Equally how do we auto-associate meta-data with a convention? Yet you still need to consider more advance cases such as when using url-tokens.

Anyway, for now I'm look for requirements and use-cases of how they are applicable. Cheers,
Rishi 

Jul 25, 2010 at 1:07 AM
I think the attribute based ones are ideal for mapping views and view models, controllers, etc. and adding URLs and other metadata. My use case for convention based it more just for application services. Things that are pure code, and not UI elements, and independent of nRoute
Coordinator
Jul 25, 2010 at 3:16 PM
Edited Jul 25, 2010 at 3:23 PM

@dbokely Good point. Sorry to belabor this, but I was thinking one should register custom conventions using a fluent kinda of API. Something akin to:

 

ResourceLocator.Conventions.CreateNew("RepositoryConvention").TypeNameEndsWith("Repository").MapResourceAs<T,IRepository<T>>((t) => new RepositoryProvider<T>());

 

Looks a bit wordy, but what do you think?

Rishi