2
votes

I've successfully implemented Ninject in an MVC3 application, but am running into some trouble doing the same thing with ASP.NET Web Forms. I'm getting null references every time I try to access an injected property in my business layer. After setting breakpoints within the CreateKernel method, as well as several places within the ServiceLocator class, it looks like none of them are ever getting hit, so it's not even loading.

I'm sure I'm just approaching this wrong, but there is very little documentation or info out there for wiring up Ninject in a Web Forms application.

Basically here's what I have so far:

code behind

public class ReviewManager
    {
        [Inject] private IReviewRepository _reviewRepository { get; set; }

        public ReviewManager() { }

        public ReviewManager(IReviewRepository reviewRepository)
        {
            _reviewRepository = reviewRepository;
        }

        public Review GetById(int id)
        {
            if (id <= 0) throw new ArgumentException("ID must be greater than zero");

            **I get a null reference exception on the next line. _reviewRepository is null**
            return _reviewRepository.GetById(id);
        }
}

global.asax.cs

public class Global : NinjectHttpApplication
{
    protected override IKernel CreateKernel()
    {
        return ServiceLocator.Kernel;
    }

    // deleted for brevity
}

ServiceLocator.cs (edited for brevity, the relevant parts are here)

public static class ServiceLocator
    {
        public static IKernel Kernel { get; set; }

        public static ILogger Logger { get; set; }

        static ServiceLocator()
        {
            Kernel = new StandardKernel(new INinjectModule[] {
                new LoggerBindings(),
                new DataBindings()
            });

            if (Logger == null)
                Logger = Kernel.Get<ILogger>();
        }
}
public class LoggerBindings : NinjectModule
    {
        public override void Load()
        {
            Bind<ILogger>().To<NLogLogger>();
        }
    }

    public class DataBindings : NinjectModule
    {
        public override void Load()
        {
            Bind<IReviewRepository>().To<ReviewRepository>();
        } 
    }
4
The key question here is: who's instantiating ReviewManager??Jeff
It's instantiated from any of the ASP.NET code-behinds.Scott
Using your ServiceLocator or by cvalling new ReviewManager?Jeff
and that would be why it's not getting injected. If you just do new ReviewManager(), there's nothing to inject its dependencies. You need to either use the ServiceLocator to create ReviewManager...or if you want it to happen automatically, create a custom handler factory as shown here stackoverflow.com/questions/589374/…Jeff
Then what's the purpose of the [Inject] attribute on this property: [Inject] private IReviewRepository _reviewRepository { get; set; }? Seems to me that should indicate to Ninject that I want it to resolve the concrete implementation...Scott

4 Answers

2
votes

ASP.Net via WebForms does not allow you to manage the lifecycle of all object instances (like MVC does). For example, the framework instantiates page objects. This means you probably can't implement DI in quite the same way as you would in MVC/WPF/Silverlight (the same problem is present in WinForms IIRC). You will likely have to initiate the dependency graph directly in each of your code behinds.

Translation: you will want to call ServiceLocator.Kernel.Get<IReviewRepository> when your page loads (or as lazy-init on the property).

2
votes

The cool thing about MVC ist that it can run side a side of ASP.NET WebForm pages in the same application. In my opinion the best way to extend ASP.NET WebForms websites is to create new pages using MVC3 and to refactor every page that needs major changes to MVC3.

If this is no option go and use the Ninject.Web extension. It contains a IHttpModule that property injects all web pages and controlls after they are initialized. That way you can property inject the services als have them created by Ninject.

0
votes

A potential workaround, by changing your DataBindings class as follows:

 public class DataBindings : NinjectModule 
    { 
        public override void Load() 
        { 
            Bind<IReviewRepository>().To<ReviewRepository>(); 
            Bind<ReviewManager>().ToSelf();
        }  
    } 

And within your caller, instead of

var rm = new ReviewManager();

Try using

var rm = ServiceLocator.Kernel.Get<ReviewManager>();

I havent tested this code, but i think it'll solve your null reference problem.

0
votes

I use property injection for pages, masterpages and usercontrols. All my pages, for example, inherit from a base class that overrides RequestActivation method with the following code:

    ''' <summary>
    ''' Asks the kernel to inject this instance.
    ''' </summary>
    Protected Overridable Sub RequestActivation()
        ServiceLocator.Kernel.Inject(Me)
    End Sub

And in each page I declare injectable properties:

    <Inject()>
    Property repo As IMyRepository