views:

749

answers:

2

I have a class that has several dependencies:

public class ThirdPartyDataSearchCoordinator<TItem, TSearchCriteria, TResult> : IThirdPartyDataSearchCoordinator where TItem : class, IThirdPartyItem
    {
        public ThirdPartyDataSearchCoordinator(IDataMiner<TSearchCriteria, TResult>[] miners, IThirdPartyItemRepository<TItem> repository, IMappingEngine mapper, IUpdater<TItem, TResult> updater)
        {
            this.miners = miners;
            this.repository = repository;
            this.mapper = mapper;
            this.updater = updater;
        }
}

And the relevant Castle registrations look like this:

container.Kernel.Resolver.AddSubResolver(new ArrayResolver(container.Kernel));
container.Register(Component.For<IMappingEngine>().Instance(Mapper.Engine));
container.AddComponent<IThirdPartyItemRepository<ThirdPartyPackage>, ThirdPartyPackageRepository>(typeof(IThirdPartyItemRepository<ThirdPartyPackage>).FullName);
container.Register(AllTypes.Pick()
                    .FromAssemblyNamed(Assembly.GetAssembly(typeof(TestDataMiner)).GetName().Name)
                    .WithService.FirstInterface());
container.AddComponent<IThirdPartyDataSearchCoordinator, ThirdPartyDataSearchCoordinator<ThirdPartyPackage,ThirdPartyPackageSearchCriteriaDto,ThirdPartyFlightSearchResultDto>>();

However, I'm having real trouble resolving an instance of IThirdPartyDataSearchCoordinator.

All these are successful:

ServiceLocator.Current.GetInstance<IThirdPartyItemRepository<ThirdPartyPackage>>()
ServiceLocator.Current.GetInstance<IUpdater<ThirdPartyPackage, ThirdPartyPackageSearchResultDto>>()
ServiceLocator.Current.GetInstance<IMappingEngine>()
ServiceLocator.Current.GetInstance<IDataMiner<ThirdPartyPackageSearchCriteriaDto, ThirdPartyPackageSearchResultDto>>()

However when I try:

ServiceLocator.Current.GetInstance<IThirdPartyDataSearchCoordinator>()

Castle complains:

Castle.MicroKernel.Handlers.HandlerException: Can't create component 'ApplicationServices.ThirdPartyData.ThirdPartyDataSearchCoordinator3' as it has dependencies to be satisfied. ApplicationServices.ThirdPartyData.ThirdPartyDataSearchCoordinator3 is waiting for the following dependencies:

Services: - IThirdPartyItemRepository1 which was not registered. - IUpdater2 which was not registered.

Keys (components with specific keys) - miners which was not registered.

Am I missing something - or am I expecting too much of Castle to resolve generic dependencies that way?

Edit What I would like to do is explicitly register each concrete ThirdPartyDataSearchCoordinator<TItem, TSearchCriteria, TResult> that I intend to use against the IThirdPartyDataSearchCoordinator interface in the application.

I don't want to make the interface generic, as then I wouldn't be able to hold a collection of different IThirdPartyDataSearchCoordinator.

How can I tell castle to explicitly initialise, for example, a ThirdPartyDataSearchCoordinator<ThirdPartyPackage, ThirdPartyPackageSearchCriteriaDto, ThirdPartyPackageSearchResultDto>, without having to feed in all the dependencies manually to the constructor?

+1  A: 

Not 100% sure, but I think you need to register the open generic interfaces first. Then you can override a specific implementation.

See http://stackoverflow.com/questions/872413/castle-windsor-resolving-and-generics

Mauricio Scheffer
Hi mausch. I knew you'd be around to answer this one! I don't want to make the interface generic, as then I wouldn't be able to hold a collection of different IThirdPartyDataSearchCoordinator. I've edited the question... any further ideas?
Hainesy
There are too many classes and interfaces here... Send me a stripped-down solution that reproduces this and I'll check it out.
Mauricio Scheffer
Okay, I'm not quite sure how to contact you
Hainesy
Just put every class and interface in a single file and drop it in http://pastebin.com/ or http://gist.github.com/ and send me the url
Mauricio Scheffer
Okay, I think I've narrowed the problem down to something very simple. I want to be able to register generic classes against a non-generic interface, see http://www.codepaste.net/oc1qqu
Hainesy
At first blush it looks like a bug in Windsor. I'll test it more thoroughly later.
Mauricio Scheffer
Registering a generic class with a non-generic interface works fine, all you need to do is container.AddComponent<ICoordinator, SimpleCoordinator<string>>(); I think the testcase wasn't enough to repro your original issue...
Mauricio Scheffer
Are you using a different version of Castle? In v1 it doesn't work.
Hainesy
I've just tried it with v2 and the test still fails. "System.ArgumentException: The type or method has 1 generic parameter(s), but 0 generic argument(s) were provided. A generic argument must be provided for each generic parameter."
Hainesy
Oh, I see, it doesn't like the Register All Types line. Hmm. I'll work on reproducing the problem with a bigger example then.
Hainesy
I'm also using v2. Send me a bigger example... RegisterAll was not really necessary in the example you sent me...
Mauricio Scheffer
Right, it's taken a while to replicate... I hope you're still following! :-) http://www.codepaste.net/pokd3s
Hainesy
"We're sorry, but an unhandled error occurred on the server" on codepaste.net :-(
Mauricio Scheffer
Weird! Try: http://pastebin.com/m33fa5655
Hainesy
This setup passes all tests: http://pastebin.com/mcd314b4It seems that the batch registration (AllTypes) was registering something it shouldn't have registered. When in doubt, use a LINQ expression instead of AllTypes et al, it gives you more control over the registrations (but it will also be more verbose).Also, use the castle users group (http://groups.google.com/group/castle-project-users) for future questions, there are more skilled people over there.
Mauricio Scheffer
Mausch - many thanks for your time on this! This creates a further problem in that I'm not sure I want to be registering every single class on its own explicit line. The real application is much bigger than the example and that will be a lot of registrations.
Hainesy
+1  A: 

Okay I played around for quite a bit and worked a solution. I changed my "AllTypes" registration to only register non-generic types.

container.Register(AllTypes.Pick()
.FromAssemblyNamed(Assembly.GetAssembly(typeof(IUpdater<,>)).GetName().Name)
.If(p => !p.ContainsGenericParameters)
.WithService.FirstInterface());

Now it all works nicely. I think there's some problem with AllTypes and generic class registrations.

Update Simply placing my generic class registrations before the AllTypes did the trick too.

Hainesy