Query methods

Jan 15, 2009 at 3:33 PM
Hi,

While I am sure it is not a universally available feature, it seems that ISL is lacking boolean tests of the form:

bool IsServiceAvailable(Type serviceType)

etc.

Is there a way to achieve this that I'm missing?

(Using the GetAllInstances() method and then checking for a non-empty set will not work, since this will activate all of those instances - I may only be interested in the default instance, or wish to defer activation.)

Interested to know if there has been any discussion about this feature.

Cheers,

Nick
Jan 23, 2010 at 9:40 PM
Edited Jan 24, 2010 at 10:12 AM

Hi Nick,

I understand that checking whether a type / service is registered or not is useful in certain scenario's, but I don't believe all IoC frameworks support this mechanism. The Common Service Locator is simply the lowest common denominator of all IoC implementations.

While this isn't supported, it's possible to build such a check yourself, for instance by catching the ActivationException that an implementation might throw. You could define an extension method (C# 3.0) on the IServiceLocator method, like this:

public static class ServiceLocatorExtensions
{
public static bool IsServiceAvailable<T>(
this IServiceLocator locator)
{
try
{
using (var instance = ServiceLocator.Current
.GetInstance<T>() as IDisposable)
{
}
return true;
}
catch (ActivationException)
{
return false;
}
}
}

Now your method can be used as follows:

bool weaponAvailable =
ServiceLocator.Current.IsServiceAvailable<IWeapon>();

Of course the performance of this thing isn't great, because of the thrown exception. When this is undesirable, you can also test for availability once and cache that result, as is done in the following example:

public static class ServiceLocatorExtensions
{
public static bool IsServiceAvailable<T>(
this IServiceLocator locator)
{
return ServiceChecker<T>.IsAvailable;
}

static class ServiceChecker<T>
{
private readonly static bool Available;

static ServiceChecker()
{
try
{
using (var instance = ServiceLocator.Current.
GetInstance<T>() as IDisposable)
{
}
Available = true;
}
catch (ActivationException) { }
}

public static bool IsAvailable { get { return Available; } }
}
}

I hope this helps.

 

 

Feb 13, 2010 at 10:18 AM

Nick, @dot_net_junkie is correct. We debated about this one back and forth. Some containers did not support checking existing whcih is why we left it out.

Glenn

Feb 10, 2011 at 12:58 PM

The interesting thing is that the GetService method of IServiceProvider should return null if the service is not available. This is part of the specification of the IServiceProvider interface, and therefore part of its contract. Its a simple matter to create your own implementation that implements the interface correctly.

Feb 10, 2011 at 6:20 PM
Edited Mar 23, 2012 at 8:29 AM

Suedama is correct. I happened to stumble upon this a few days back, while working on the Simple Injector. There is actually a flaw in the design of the ServiceLocatorImplBase class, that makes it very easy to create an incorrect implementation. The flaw is that the virtual GetService(Type) method of the ServiceLocatorImplBase calls into the abstract GetInstance(Type, string) method. This is wrong because -as suedama noted- the GetService method should return null when a registration for the given type is missing, while the GetInstance methods should throw an ActivationException when no registration is present (according to the API documentation).

As a matter of fact, the writers of the WindsorServiceLocator, SpringServiceLocatorAdapter, UnityServiceLocator, and StructureMapServiceLocator adapters actually tripped over this flaw, and because of this their implementations are -unfortunately- incorrect. The Spring adapter throws an exception instead of returning null from the GetService(Type) method. With the other three I'm not sure what their exact behavior is, but either they return null from GetInstance, or throw from GetService. I implemented the correct behavior for the SimpleInjectorAdapter, but unfortunately the this flaw makes it impossible to rely on the IServiceProvider interface when using the CSL.

When all implementations would follow the contract, creating an TryGetInstance method would be a one liner:

public partial static class ServiceLocatorExtensions
{
    public static bool TryGetService<T>(this IServiceProvider provider, out T instance)
    {
        return (instance = (T)provider.GetService(typeof(T))) != null;
    }
}

Okay, I admit that I cheated a bit to get it on one line :-). The IsServiceAvailable would also be a one liner:

public partial static class ServiceLocatorExtensions
{
public static bool IsServiceAvailable<T>(this IServiceProvider provider)
{
return provider.GetService(typeof(T)) != null;
}
}

Note however that I incorrectly wrapped the call to GetInstance in a using block (in my comment on the 23rd of January 2010). Disposing the returned object is incorrect, because it is impossible to tell what the lifetime of that instance is and what the consequences are of disposing that object. For that reason it is in fact impossible to create a correct IsServiceAvailable extension method. The only way to do this correctly is by having access to the underlying container.

The bigger issue of course is that IDisposable is a leaky abstraction and developers should IMO try to prevent returning disposable objects from a container. But that's another discussion.

Feb 10, 2011 at 6:46 PM

Sounds like we might have messed up here. The idea of throwing an exception was an explicit design goal that we all agreed on. Making it implement IServiceProvider was more of a convenience, but sounds like this was overlooked. I imagine 90% or more of the people who use CSL don't use it as an IServiceProvider, however this is a good catch.

 

Feb 6, 2014 at 7:43 AM
Today a new version of the CSL has been released. It's nice to see that old issues are being addressed

I'm curious if you have any intentions to address the issue I described above and was confirmed by @gblock?