Defining Resource Instances (eg - log4net ILog)

May 22, 2010 at 6:07 PM

Hi

In porting my app from Prism to nRoute i am unable to workout how to define an instance of a resource.  Specifically i want to tell nRoute how to create an ILog (log4net) instance that all services and viewmodels can then use via DI in the constructor.  So in Prism (ie using Unity) i can use the following to tell Unity how to create a specific instance of ILog:

this.Container.RegisterInstance<ILog>(LogManager.GetLogger(typeof(AppBootstrapper)));

This ensures the IOC container uses the defined instance, instead of trying to create a specific type when referenced.  In nRoute i can see that you can use an attribute to define a resource that will then be instantiated as required:

[MapService(typeof(ISalesService), InitializationMode=InitializationMode.OnDemand, Lifetime=InstanceLifetime.Singleton)]

However i am unsure how to replace the above code so that a specific instance is always used.  Can you inform me how to do this?

Coordinator
May 22, 2010 at 8:12 PM
Edited May 22, 2010 at 8:17 PM

Well, rather than the MapService the DefineService attribute. When you use MapService it means you are mapping the class onto which you've applied the attribute to be the service - all the MapXXXX attributes work like that, eg. when you say MapViewModel it means the class is the ViewModel, however when you say DefineViewModel then you are defining a ViewModel. Also, the DefineXXXX stuff can be use as an assembly-attribute eg.

[assembly: DefineService(typeof(ISalesService), typeof(SomeSaleService), InitializationMode=InitializationMode.OnDemand, Lifetime=InstanceLifetime.Singleton)]

above the SomeSaleService is your implementation type. Now, currently in nRoute I've not opened API to progamatically register a service against the ResourceLocator directly (although you can yourself create a proxy using a custom attribute and/or IResourceLocator). Anyway the easiest solution is for you register a factory for a resource by using the RegisterLocatorAdapter method on the Resource static class. There you can register an adapter to use pre-defined instances instead of creating a specific types.

Hope this helps.

Rishi

May 23, 2010 at 5:03 AM

I used MapService because, as you detailed, it maps to the type that the attribute was defined on.  This works great for my SalesService which is a type defined in my assembly.  However it has a dependency on log4net (public SalesService(ILog logger)).  I have no implementation of ILog defined - it comes from the static method of the LogManager class, hence my above question.  As you mentioned, i found the RegisterLocatorAdaptor method but havnt yet quite managed to get it to work.  Id really love to see this as an attribute i can define on the assembly, like:

[assembly: DefineServiceInstance(typeof(ISalesService), () => return LogManager.GetLogger("Main"), InitializationMode=InitializationMode.OnDemand, Lifetime=InstanceLifetime.Singleton)]

Thanks for the prompt reply - if you do get time to post an example of how to use the RegisterLocatorAdaptor method it would save me some time (especially if it was wrapped up in a nice, easy to reuse attribute ;oD)

cheers

BTW - the update samples are AWESOME and much appreciated, especially the transition behaviors which is something i had noted to investigate

Coordinator
May 23, 2010 at 9:30 AM
Edited May 23, 2010 at 9:46 AM

You can't use lambda statements in attributes, that's a limitation of .NET framework which only allows use of constants in attributes..

Now as for using adapters it's quite simple..

public class LogAdapter : ILocatorAdapter
{
    public object Resolve(string name)
    {
        return LogManager.GetLogger(name ?? "Main");
    }
}

And then you need to register it as such (note this should obviously be done before it is used):

Resource.RegisterLocatorAdapter((t) => t == typeof(ILog), (t) => new LogAdapter());

That's it you are done. And for greater flexibility see how I pass-through the resource name to match the logger name, which means you can ask for another logger like this:

public class MyViewModel 
{
    [ResolveConstructor]
    public MyViewModel([ResolveResource("Critical")] ILog logger)
    {
        // use your "Critical" logger
    }
}

So Above we're getting the "Critical" logger - if we remove the [ResolveResource] attribute then it will default to the "Main" logger. Also, FYI there are two other other ways of doing the same thing.. one using custom attributes based on ResolveResourceBaseAttribute and other using a custom IResourceLocator..

Hope this helps..
Rishi

May 23, 2010 at 5:19 PM

Brilliant, works perfectly!

Really nice work on this framework Rishi ...