Ninject Bootstrapper

Oct 14, 2010 at 7:09 AM
Edited Oct 14, 2010 at 8:58 AM

Hi,

I've beed experimenting with Ninject and Caliburn Micro. And came up with this implementation of the bootsrapper. Posting it here maybe someone else needs (suggestions and reviews are greatly appreciated also).

public class Bootstrapper : Bootstrapper<IShellViewModel>
    {
        private IKernel _kernel;

        protected override void Configure()
        {
            _kernel = new StandardKernel();

            _kernel.Bind<IWindowManager>().To<WindowManager>().InSingletonScope();
            _kernel.Bind<IEventAggregator>().To<EventAggregator>().InSingletonScope();

            _kernel.Bind<IShellViewModel>().To<ShellViewModel>().InSingletonScope();

            // scan services
            _kernel.Scan(x =>
            {
                x.FromAssemblyContaining<GuestModule>();
                x.FromAssemblyContaining<AdminModule>();

                x.Where(type => type.Name.EndsWith("Service"));

                x.BindWithDefaultConventions();

                x.InSingletonScope();
            });

            // scan the rest
            _kernel.Scan(x =>
            {
                x.FromAssemblyContaining<GuestModule>();
                x.FromAssemblyContaining<AdminModule>();

                x.Where(type => !type.Name.EndsWith("Service"));

                x.BindWithDefaultConventions();
            });

            GuestModule guestModule = new GuestModule(_kernel.Get<IShellViewModel>());
            guestModule.Load();
            AdminModule adminModule = new AdminModule(_kernel.Get<IShellViewModel>());
            adminModule.Load();
        }

        protected override object GetInstance(Type service, string key)
        {
            if (service != null)
            {
                return _kernel.Get(service);
            }

            throw new ArgumentNullException("service");
        }

        protected override System.Collections.Generic.IEnumerable<object> GetAllInstances(Type service)
        {
            return _kernel.GetAll(service);
        }

        protected override void BuildUp(object instance)
        {
            _kernel.Inject(instance);
        }

        protected override System.Collections.Generic.IEnumerable<Assembly> SelectAssemblies()
        {
            return new[] { Assembly.GetExecutingAssembly(), typeof(GuestModule).Assembly, typeof(AdminModule).Assembly };
        }
    }

 

Regards,

Oct 14, 2010 at 8:47 AM
Edited Oct 14, 2010 at 8:48 AM

I've done it like this

 

namespace phone.spike
{
    using System;
    using System.Collections.Generic;
    using System.Windows;
    using Caliburn.Micro;    
    using MessageHandler;
    using Ninject;
    using ViewModels;
    using Views;

    public class AppBootstrapper : PhoneBootstrapper
    {
        private IDictionary<string, Type> _viewModelDictionary;
        private IList<IMessageHandler> _messageHandler;
        private IKernel _kernel;
       
        private void RegisterViewModel<T>(string key)
        {
            if (_viewModelDictionary.ContainsKey(key))
                _viewModelDictionary[key] = typeof(T);
            else
            {
                _viewModelDictionary.Add(key, typeof(T));
            }
        }

        private void RegisterMessageHandler<T>() where T : IMessageHandler
        {
            if (_kernel == null) return;

            var messageHandler = _kernel.Get<T>();
            _messageHandler.Add(messageHandler);
        }

        protected override void Configure()
        {
            _viewModelDictionary = new Dictionary<string, Type>();
            _messageHandler = new List<IMessageHandler>();

            _kernel = new StandardKernel(
                new LoggingModule(),
                new ApplicationSettingsModule(),                
                new MessageHandlerModule()
                );
            
            RegisterViewModel<SomeViewModel>("SomeViewModel");                        

            RegisterMessageHandler<SomeAwesomeMessageHandler>();            
        }

        protected override object GetInstance(Type service, string key)
        {
            /* Ninject dont like resolving via key only.. am i right? */
            if (service != null)
                return _kernel.Get(service);

            if (_viewModelDictionary.ContainsKey(key) && _viewModelDictionary[key] != null)
            {
                return _kernel.Get(_viewModelDictionary[key]);
            }
            throw new ArgumentOutOfRangeException("service");
        }

        protected override IEnumerable<object> GetAllInstances(Type service)
        {
            return _kernel.GetAll(service);
        }

        protected override void BuildUp(object instance)
        {
            _kernel.Inject(instance);
        }        
    }
}

 

Oct 14, 2010 at 9:01 AM

Thanks kelkes,

Took the _kernel.Inject bit from you.

I prefer using ninject conventions helps cutting out a lot of code like RegisterViewModel.

Oct 14, 2010 at 10:20 AM

Dont you get a lot ArgumentNullException("service") from GetInstances. Afaik Caliburn.Micro uses the key and dont provide a Type to resolve.

Has this changed? This is the only reason why i've done things like RegisterViewModel :)

Oct 14, 2010 at 11:42 AM

Not at all, all the models get resolved by their interface.

Oct 14, 2010 at 12:06 PM

Ok so i missed a change in Caliburn.Micro :) Thxs.

Oct 15, 2010 at 2:03 AM

Up till now I've been doing convention bindings in Ninject with a custom kernel.

 

public class ConventionKernel : StandardKernel
{
    public ConventionKernel(params INinjectModule[] modules)
        : base(modules)
    {
    }
    public ConventionKernel(INinjectSettings settings, params INinjectModule[] modules)
        : base(settings, modules)
    {
    }

    protected override bool HandleMissingBinding(Type service)
    {
        if (!base.HandleMissingBinding(service))
        {
            if (service.IsInterface && service.Name.StartsWith("I"))
            {
                var implementation = service.Module.GetType(String.Format("{0}.{1}", service.Namespace, service.Name.Substring(1)));
                if (implementation == null || !service.IsAssignableFrom(implementation))
                    return false;

                var binding = new Binding(service)
                {
                    ProviderCallback = StandardProvider.GetCreationCallback(implementation),
                    Target = BindingTarget.Type
                };

                AddBinding(binding);

                return true;
            }

            return false;
        }

        return true;
    }
}

Now I'm gonna try out that conventions extension.

 

Oct 15, 2010 at 6:11 AM

Hi Cameron,

Glad you've find some use to this.

Regards,

Apr 2, 2011 at 2:45 PM
Edited Apr 2, 2011 at 2:45 PM

Calin,

Which version of Ninject are you using?

IKernel in Ninject 2.0 doesn't come with a Scan method. How would you go about this using Ninject 2.0?

Apr 2, 2011 at 6:37 PM

Hi Norgie,

 

 From version ninject 2.0 the team made the wise decision to pull out extra features into extensions, so what are you looking for is the convention extension https://github.com/ninject/ninject.extensions.contextpreservation. For a full list of extensions take a look at http://ninject.org/extensions.

 

Regards,

Apr 2, 2011 at 7:18 PM

Hi Calin,

Thanks for your reply. I'll look into it.

--norgie