Anyone who has ttried to add a service implemented through Remoting to the WorkItem's services collection was sadly surprised that it can't be done.
But why?
When we use the following code:
WorkItem.Services.Add<IServiceContract>(serviceInstance);
It comes to something like this:
if (!typeof(IServiceContract).IsAssignableFrom(serviceInstance.GetType())
throw new ArgumentException();
And how do we solve this problem?
easy: generics.
All we need to do is make some changes to the ServiceCollection class.
We change the Add<TService>(TService serviceInstance) method to look like this:
public void Add<TService>(TService serviceInstance)
{
Guard.ArgumentNotNull(serviceInstance, "serviceInstance");
Build<TService>(serviceInstance);
}
At this point we need to refactor the Build(Type typeToBuild, Type typeToRegisterAs, object serviceInstance) method to have two entry points to the common its logic:
private object Build(Type typeToBuild, Type typeToRegisterAs, object serviceInstance)
{
Guard.TypeIsAssignableFromType(typeToBuild, typeToRegisterAs, "typeToBuild");
return BuildImplementation(typeToBuild, typeToRegisterAs, serviceInstance);
}
private TService Build<TService>(TService serviceInstance)
{
return (TService)BuildImplementation(serviceInstance.GetType(), typeof(TService), serviceInstance);
}
private object BuildImplementation(Type typeToBuild, Type typeToRegisterAs, object serviceInstance)
{
if (locator.Contains(new DependencyResolutionLocatorKey(typeToRegisterAs, null), SearchMode.Local))
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
Properties.Resources.DuplicateService, typeToRegisterAs.FullName));
if (serviceInstance == null)
serviceInstance = BuildFirstTimeItem(typeToBuild, typeToRegisterAs, null);
else if (!container.Contains(serviceInstance))
serviceInstance = BuildFirstTimeItem(typeToBuild, typeToRegisterAs, serviceInstance);
else
BuildRepeatedItem(typeToRegisterAs, serviceInstance);
return serviceInstance;
}
Easy, isn't it? And I suspect that that's not the only improvement like this that we can get into CAB.
Oh! And why would we wanto to use Remoting? To use NMock2 in the unit tests, of course!
Updated: November 14th, 2006
Changed:
private TService Build<TService>(object serviceInstance)
{
Guard.TypeIsCompatibleType<TService>(serviceInstance, "typeToBuild");
return (TService)BuildImplementation(serviceInstance.GetType(), typeof(TService), serviceInstance);
}
Removed:
And add the TypeIsCompatibleType<T>(object providedInstance, string argumentName) method to the Guard class:
public static void TypeIsCompatibleType<T>(object providedInstance, string argumentName)
{
if (!(providedInstance is T))
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
Properties.Resources.TypeNotCompatible, typeof(T), providedInstance.GetType()),
argumentName);
}