nRoute/Agatha IoC

Sep 15, 2010 at 9:16 AM

Hi Orktane,

I am planning on using the Agatha WCF framework in an application built on the nRoute Framework. Agatha requires an IoC container to be present and they have provided an interface, IContainer, to allow integration with other IoC containers.

How would I go about creating a custom IoC container for Agatha using the nRoute RLF.

Thanks,

The agatha IContainer interface looks like this:

    public class AgathaIocBridge : IContainer
    {
        public void Register<TComponent, TImplementation>(Lifestyle lifestyle) where TImplementation : TComponent
        {
            throw new NotImplementedException();
        }

        public void Register(Type componentType, Type implementationType, Lifestyle lifeStyle)
        {
            throw new NotImplementedException();
        }

        public void RegisterInstance<TComponent>(TComponent instance)
        {
            throw new NotImplementedException();
        }

        public void RegisterInstance(Type componentType, object instance)
        {
            throw new NotImplementedException();
        }

        public void Release(object component)
        {
            throw new NotImplementedException();
        }

        public object Resolve(Type componentType)
        {
            throw new NotImplementedException();
        }

        public TComponent Resolve<TComponent>()
        {
            throw new NotImplementedException();
        }
    }

Coordinator
Sep 16, 2010 at 9:18 AM

Hi Ross, implementing this interface is not a problem per se except in nRoute, for various reasons, I have internalized registration and de-registration APIs for RLF. Now, you have two options, one compile you own version of nRoute by making two calls public or I can suggest a workaround that will work with the released version. To open the API, you'll need to edit the nRoute.Components.Composition.Resource class and make public RegisterResourceLocator, UnregisterResourceLocator and per you need any other registration related methods. Alternatively, the workaround involves using attributes wrappers or creating adapters, but it's not cleanest of approaches. Choose you approach, then I can advise you accordingly but basically in RLF to register any component we create an IResourceLocator implementation that manages the lifetime and initialization of the resource. 

Also this might not be related, but below is IServiceLocator implementation for RLF:

public class ResourceServiceLocator
	: ServiceLocatorImplBase 
{
	private static readonly IEnumerable<object> EMPTY_CATALOG = Enumerable.Empty<Object>();

#region Overrides

	protected override IEnumerable<object> DoGetAllInstances(Type serviceType)
	{
		var _resourceLocators = Resource.GetResourceCatalog(serviceType);
		if (_resourceLocators != null)
		{
			return _resourceLocators.Select((l) => l.GetResourceInstance());
		}
		else
		{
			return EMPTY_CATALOG;
		}
	}

	protected override object DoGetInstance(Type serviceType, string key)
	{
		return string.IsNullOrEmpty(key) ? 
			Resource.GetResource(serviceType) : Resource.GetResource(serviceType, key);
	}

#endregion

}

Cheers
Rishi 

Sep 16, 2010 at 10:49 AM

Thanks, I think I will go with opening up the API approach.

Does this mean that I must create a resource locator for each type that need to be registered in the IoC container?

If this is the case then when the Register method is called on IContainer, I will need to locate the correct IResourceLocator implementation and register that resource locator?

 

Coordinator
Sep 16, 2010 at 11:57 AM
Edited Sep 16, 2010 at 11:58 AM

Yes, you create a resource locator instance for each registered resource. So something, like:

public void Register(Type componentType, Type implementationType, Lifestyle lifeStyle)
{
	var _metadata = new ResourceMeta(componentType, implementationType,  
		lifetime == Lifestyle.Singleton ?  InstanceLifetime.Singleton : InstanceLifetime.PerInstance);
	var _locator = new DefaultResourceLocator(Guid.NewGuid().ToString(), _metadata);
	Resource.RegisterResourceLocator(componentType, _locator, true);
}

public void RegisterInstance<TComponent>(TComponent instance)
{
	var _metadata = new ResourceMeta(typeof(TComponent), instance.GetType(), InstanceLifetime.Singleton );
	var _locator = new InstanceResourceLocator(_metadata, instance);
	Resource.RegisterResourceLocator(typeof(TComponent), _locator, true);
}

 

Assuming you have opened up the internal methods in Resource class, all you need to is register a ResourceLocator for each resource. The idea of ResourceLocator is that it knows how to "locate" an instance of the resource, and for maximum flexibility/extensibility you can register a different/customized resource locator for each instance of a resource. Now, above for the fist method we are just using the built-in resource locator and metadata class, which works nicely with the givens. However, for the second case we don't have a instance registration resource locator (actually we have but it's specific for ViewServices), but not to worry you can create your own as it's quite simple. So a simple implementation would be like:

public class InstanceResourceLocator : IResourceLocator
{
	private readonly ResourceMeta _metadata;
	private readonly string _resourceName;
	private Object _instance;

	public InstanceResourceLocator(ResourceMeta metadata, Object instance)
	{
		if (metadata == null) throw new ArgumentNullException("metadata");
		if (instance == null) throw new ArgumentNullException("instance");

		_metadata = metadata;
		_instance = instance;
		_resourceName = instance.GetHashCode();
	}

	public string ResourceName
	{
		get { return _resourceName; }
	}

	public object ResourceMeta
	{
		get { return _metadata; }
	}

	public object GetResourceInstance()
	{
		return _instance;
	}

	public void Dispose()
	{
		_instance = null;
	}
} 

 

One important thing to note is for any given resource type each registered resource must have a unique resource name (hence you see me using the hash-code) as you can multiple registration of the same type and this allows you to retrieve any individual resource. Also, the metadata class isn't really used by nRoute, it's there for the application to use it - so you can omit it, but I tend to store resource related information as you can see above. Lastly, once you have registered, you can resolve resources using the ResourceLocator static class. 

Hope it helps,
Rishi

 

Sep 16, 2010 at 2:50 PM

Thanks Rishi,

Nearly there. The last issue now is that some of the components within Agatha have parameters being passed in via the constructor. So when the instance is created it complains that there is no constructor marked with [ResolveConsturctor], actual error below:

Cannot resolve a default constructor to use for Agatha.Common.AsyncRequestDispatcher, please specify using ResolveConstructor Attribute

Is there any way around this?

Thanks

Coordinator
Sep 17, 2010 at 12:08 PM

Hi Ross, well it's all fixable - but you'll need to do some work for it. What happens is that the to resolve a type we deffer to a TypeBuilder, which has been designed to seek [ResolveConstructor] and/or [ResolveResource] attributes (just like MEF) to know where/what to resolve. Now, it seems you need to forgo the use of the special attributes, if so then you'll need to change the behavior/working of the TypeBuilder. One idea would be to provide hints that reside outside of the type, like have [DefineResolvableResource] attribute and [DefineResolvableConstructor] attributes and hook up the TypeBuilder to seek them, or you can provide your own custom TypeBuilder. 

It's not so simple, but not too difficult too.

Hope this helps.
Rishi