Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • jake williamson 207 posts 873 karma points
    Apr 18, 2018 @ 11:00
    jake williamson
    0

    ioc/dependency injection issue when umbraco starts up

    ok, i've been banging my head against the wall for far too long on this one and i just can't see how to make this work...

    i'm using ninject for ioc and it's working using this code:

    public class UmbracoBooter : IApplicationEventHandler
    {
        public void OnApplicationInitialized(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
        {
        }
    
        public void OnApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
        {
        }
    
        public void OnApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
        {
            var kernel = new StandardKernel();
    
            kernel.Bind<IMyService>().To<MyService>();
    
            DependencyResolver.SetResolver(new NinjectDependencyResolver(kernel));
        }
    }
    

    i can inject 'IMyService' into a controller and it works e.g.

    private readonly IMyService _myService;
    
    public HomeController(IMyService myService)
    {
        _myService = myService;
    }
    
    public override ActionResult Index(RenderModel model)
    {
        _myService.DoSomething();
    
        return CurrentTemplate(model);
    }
    

    so far so good. however...

    i actually want to call my 'DoSomething()' method when umbraco starts up (in the real project it's setting up a bunch of examine index stuff).

    so the question is, how do i inject the 'IMyService' into my 'UmbracoBooter' class? if i try this:

    private readonly IMyService _myService;
    
    public Startup(IMyService myService)
    {
        _myService = myService;
    }
    
    public void OnApplicationInitialized(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
    {
    }
    
    public void OnApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
    {
    }
    
    public void OnApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
    {
        var kernel = new StandardKernel();
    
        kernel.Bind<IMyService>().To<MyService>();
    
        DependencyResolver.SetResolver(new NinjectDependencyResolver(kernel));
    
        _myService.DoSomething();
    }
    

    then '_myService' is always null...

    i've tried everything i can think off and just can't get this to work... it feels like i need to inject and call the method further down the pipe maybe?

    has anyone done this kinda thing before or am i attempting to do something that i shouldn't?!

    any feedback would be blardy amazing ;)

    cheers,

    jake

  • Euan Rae 105 posts 135 karma points
    Apr 18, 2018 @ 11:59
    Euan Rae
    1

    Hi Jake,

    You're correct; for this to work you need to create an instance of a class that takes an IMyService parameter in the constructor.

    Is the code in _myService.DoSomething(); only ever done at application startup? If that's the case, then I would move it into the Bootstrapper class.

    The way I handle startup code is to have a Bootstrapper class with a static function called Init, so the event handler looks something like

    public void OnApplicationStarted(...)
    {
        Bootstrapper.Init();
    }
    

    and Bootstrapper.Init() has all the code that's needed on application start; setting up IoC, creating custom database tables, setting up indexes, etc

    Hope that helps!

    Euan

  • jake williamson 207 posts 873 karma points
    Apr 18, 2018 @ 13:53
    jake williamson
    0

    hi euan,

    thank you so much for the reply, really appreciate it ;)

    i see where you're going, moving the code in '_myService.DoSomething();' into the startup class would be ideal however (and you knew there'd be a however...) the implementation of the 'IMyService' interface (yup, you guessed it!) calls a method in another interface:

    public class MyService : IMyService
    {
        private readonly ILog _log;
    
        public MyService(ILog log)
        {
            _log = log;
        }
    
        public void DoSomething()
        {
            _log.Info(DateTime.Now);
        }
    }
    

    the trick being if i move this code into the startup i have to inject 'ILog' and i'm back to it being null... damn.

    so i investigated the static class idea and i think i've got something working...

    my startup class now has this:

    public void OnApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
    {
        var kernel = new StandardKernel();
    
        kernel.Bind<ILog>().ToMethod(context => LogManager.GetLogger(context.Request.Target.Member.DeclaringType));
        kernel.Bind<IMyService>().To<MyService>();
    
        DependencyResolver.SetResolver(new NinjectDependencyResolver(kernel));
    
        Bootstrapper.Init();
    }
    

    the bootstrapper has this:

    public static class Bootstrapper
    {
        private static IMyService MyService => DependencyResolver.Current.GetService<IMyService>();
    
        public static void Init()
        {
            MyService.DoSomething();
        }
    }
    

    based on the testing i've done, this is now working! i can't test my startup or boostrapper classes but tbh i couldnt really anyway and realistically there's no logic to test anyway... and it's the code in the implementation of 'IMyService' that has the testable code.

    still got a bit of playing to do (like try this in a real project rather than the test project i'm using for this post) but does this sound about right?!

    cheers,

    jake

  • Euan Rae 105 posts 135 karma points
    Apr 19, 2018 @ 08:08
    Euan Rae
    1

    Hi Jake,

    I think I see what your'e doing there, and what you're doing makes sense.

    I would personally move the DI setup into another class / method, have another class that has an instance of IMyService in it and call them both from the OnApplication started event handler.

    public void OnApplicationStarted (..)
    {
        BootStrapper.Init(); // DI Setup Code goes in here
        StartupTasks.Run(); // (or whatever name suits based on what it's doing) instance of IMyService goes in here
    }
    

    This is just a personal preference in terms of separating out different pieces of functionality though.

    I hope you get it working nicely in a live project!

    Cheers, Euan

Please Sign in or register to post replies

Write your reply to:

Draft