How can I implement Ninject or DI on asp.net Web Forms?
Asked Answered
S

6

84

There are plenty of examples for having it worked on an MVC application. How is it done on Web Forms?

Seashore answered 8/2, 2011 at 13:36 Comment(0)
R
78

Here are the steps to use Ninject with WebForms.

Step1 - Downloads

There are two downloads required - Ninject-2.0.0.0-release-net-3.5 and the WebForm extensions Ninject.Web_1.0.0.0_With.log4net (there is an NLog alternative).

The following files need to be referenced in the web application: Ninject.dll, Ninject.Web.dll, Ninject.Extensions.Logging.dll and Ninject.Extensions.Logging.Log4net.dll.

Step 2 - Global.asax

The Global class needs to derive from Ninject.Web.NinjectHttpApplication and implement CreateKernel(), which creates the container:

using Ninject; using Ninject.Web;

namespace Company.Web {
    public class Global : NinjectHttpApplication


        protected override IKernel CreateKernel()
        {
            IKernel kernel = new StandardKernel(new YourWebModule());
            return kernel;
        }

The StandardKernel constructor takes a Module.

Step 3 - Module

The Module, in this case YourWebModule, defines all the bindings the web application will need:

using Ninject;
using Ninject.Web;

namespace Company.Web
{
    public class YourWebModule : Ninject.Modules.NinjectModule
    {

        public override void Load()
        {
            Bind<ICustomerRepository>().To<CustomerRepository>();
        }   

In this example, wherever the ICustomerRepository interface is referenced the concrete CustomerRepository will be used.

Step 4 - Pages

Once that's done each page needs to inherit from Ninject.Web.PageBase:

  using Ninject;
    using Ninject.Web;
    namespace Company.Web
    {
        public partial class Default : PageBase
        {
            [Inject]
            public ICustomerRepository CustomerRepo { get; set; }

            protected void Page_Load(object sender, EventArgs e)
            {
                Customer customer = CustomerRepo.GetCustomerFor(int customerID);
            }

The InjectAttribute -[Inject] - tells Ninject to inject ICustomerRepository into the CustomerRepo Property.

If you already have a base page you just need to get your base page to derive from the Ninject.Web.PageBase.

Step 5 - Master Pages

Inevitably, you'll have master pages, and to allow a MasterPage to access injected objects you'll need to derive your master page from Ninject.Web.MasterPageBase:

using Ninject;
using Ninject.Web;

namespace Company.Web
{
    public partial class Site : MasterPageBase
    {

        #region Properties

        [Inject]
        public IInventoryRepository InventoryRepo { get; set; }     

Step 6 - Static Web Service Methods

The next problem was not being able to inject into static methods. We had a few Ajax PageMethods, which are obviously static, so I had to move the methods into a standard web service. Again, the web service needs to derive from a Ninject class - Ninject.Web.WebServiceBase:

using Ninject;
using Ninject.Web;    
namespace Company.Web.Services
{

    [WebService(Namespace = "//tempuri.org/">http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]    
    [System.Web.Script.Services.ScriptService]
    public class YourWebService : WebServiceBase
    {

        #region Properties

        [Inject]
        public ICountbackRepository CountbackRepo { get; set; }

        #endregion

        [WebMethod]
        public Productivity GetProductivity(int userID)
        {
            CountbackService _countbackService =
                new CountbackService(CountbackRepo, ListRepo, LoggerRepo);

In your JavaScript you'll need to reference the standard service - Company.Web.Services.YourWebService.GetProductivity(user, onSuccess), rather than PageMethods.GetProductivity(user, onSuccess).

The only other problem I found was injecting objects into User Controls. While it's possible to create your own base UserControl with Ninject capabilities, I found it quicker to add a Property to the user control for the required object and setting the Property in the container page. I think supporting UserControls out of the box is on the Ninject "to-do" list.

Adding Ninject is quite simple and it is an eloquent IoC solution. Many people like it because there is no Xml configuration. It has other useful "tricks" such as turning objects into Singletons with just the Ninject syntax - Bind<ILogger>().To<WebLogger>().InSingletonScope(). There is no need to change WebLogger into an actual Singleton implmentation, I like this.

Randolphrandom answered 25/5, 2011 at 7:55 Comment(9)
This works a treat, but I tried this on a new WebForms app (from the templates) and found that when the Global.asax.cs file had the default session methods in (e.g. Application_Start) the app returned a cryptic error. It may be obvious that you need to delete these (as they're implemented in NinjectHttpApplication) but it caught me out.Dulles
Glad it was helpful. The error sounds strange, what was the exception? I have code in the Application_Start and it works fine.Randolphrandom
I just forgot to call the supertype's method NinjectHttpApplication.Application_Start from the Global.asax.cs file's Application_Start method. Works when I do that, otherwise it broke.Dulles
This is a fantastic response! I have a question though ... I currently have a need to implement this in an application that already has its own, custom PageBase, and I'm not sure how to get both it and Ninject to play well together. Is this possible to do?Oh
Absolutely, the application I'm working on now has a base page (always a good idea), just get the base page to implement PageBase.Randolphrandom
This seems outdated now. The NuGet package, ninject.web, contains some code for bootstrapping the app, making the need to inherit directly from NinjectHttpApplication obsolete?Scoreboard
NuGet is definitely the way to go.Randolphrandom
Following this guide, I repeatedly get this error The static container already has a kernel associated with it. Does anyone know what might cause this error?Longship
@NielsBrinch You may get that if you've used the NuGet package which put code NinjectWebCommon into App_start and your Global.asax(.cs) inherits from NinjectHttpApplication. Remove NinjectWebCommon.cs and try again.Jun
F
70

It's gotten easier with the release of Ninject v3.0 (as of 4/12/2012). Injection is implemented via HttpModule so there is no need to have your pages inherit from a custom Page / MasterPage. Here are the steps (and code) for a quick spike.

  1. Create a new ASP.NET WebForms project
  2. Use NuGet to add the Ninject.Web lib (which will also bring down the Ninject.Web.Common and Ninject libs)
  3. Register your custom bindings in App_Start / NinjectWebCommon.cs / RegisterServices method
  4. Use attribute injection on your pages

NinjectWebCommon / RegisterServices

    /// <summary>
    /// Load your modules or register your services here!
    /// </summary>
    /// <param name="kernel">The kernel.</param>
    private static void RegisterServices(IKernel kernel)
    {
        kernel.Bind<IAmAModel>().To<Model1>();
    } 

Default

public partial class _Default : System.Web.UI.Page
{

    [Inject]
    public IAmAModel Model { get; set; }

    protected void Page_Load(object sender, EventArgs e)
    {
        System.Diagnostics.Trace.WriteLine(Model.ExecuteOperation());
    }
}

Site.Master

public partial class SiteMaster : System.Web.UI.MasterPage
{

    [Inject]
    public IAmAModel Model { get; set; }

    protected void Page_Load(object sender, EventArgs e)
    {
        System.Diagnostics.Trace.WriteLine("From master: " 
            + Model.ExecuteOperation());
    }
}

Models

public interface IAmAModel
{
    string ExecuteOperation();         
}

public class Model1 : IAmAModel
{
    public string ExecuteOperation()
    {
        return "I am a model 1";
    }
}

public class Model2 : IAmAModel
{
    public string ExecuteOperation()
    {
        return "I am a model 2";
    }
}

Results from output window

I am a model 1
From master: I am a model 1
Fissure answered 12/4, 2012 at 19:51 Comment(11)
Hi Jason... I am trying to implement that... Got the Ninject.Web lib from NuGet fine... It created Bootstrapper on App_Start ... I tried to add a breakpoint on RegisterServices to debug, but it never get there... Am I suppose to add something else?Fob
Hi Paul, depending on what host you're using (i.e., IIS, IIS Express, cassini), it may just be that code has already run by the time visual studio attaches to the process. There are ways to make that work (I was using in IIS and attaching to process), but before you go through the effort, I'd see if the spike above works (i.e., you see output). If it does, then you know that Ninject at least installed correctly and working.Fissure
@Fob You can put Debugger.Break() in the RegisterServices method to start the debugging.Jun
Hi Jason. I am having trouble getting this to work, so I was wondering if this is the complete rundown? I can see that the NinjectHttpModule is loaded at runtime, but no injection is happening. Can you think of any reason this should be the case?Gamesmanship
How can i do this in a vb.net project?Antagonize
I am assuming the structure is the same as it's basic .NET. VB is not my strong suit; does this help as far as syntax goes? converter.telerik.comFissure
Great example Thanks It works on the page/master page but not on Asmx. How can i make it work on Asmx? Thanks.Chud
I tried following these steps on an application that I am working on but I do not see the property injection taking place. I can set a breakpoint in my NinjectWebCommon's RegisterServices method and the breakpoint is hit but no inject takes place on the page. I tried doing this on a brand new webforms project and it works as expected. What about my existing project could cause the injection not to work?Pocketknife
@Pocketknife Make sure you have a NinjectWeb.cs in App_Start. Your code to initialise Ninject has to go in this file. If it's in a separate file (e.g. NinjectWebCommon.cs it won't work). This can happen if you install Ninject.Web later than the other Ninject packages using NuGet.Edgaredgard
@Edgaredgard I am experiencing the same issue as mreyeros. I have both NinjectWeb and NinjectWebCommon. Can you specify which code needs to be in NinjectWeb when you mean "the code to initialise Ninject"?Animadvert
@resmus I think you need to check that your interface is public and not private. Try that.Casias
S
12

The answer here currently does not work due to a open bug. Here is a modified version of @Jason's steps using a customer httpmodule to inject into pages and controls without needing to inherit from ninject classes.

  1. Create a new ASP.NET WebForms project
  2. Use NuGet to add the Ninject.Web lib
  3. Register your custom bindings in App_Start / NinjectWebCommon.cs / RegisterServices method
  4. Add InjectPageModule and register in NinjectWebCommon
  5. Use attribute injection on your pages

InjectPageModule.cs

 public class InjectPageModule : DisposableObject, IHttpModule
{
    public InjectPageModule(Func<IKernel> lazyKernel)
    {
        this.lazyKernel = lazyKernel;
    }

    public void Init(HttpApplication context)
    {
        this.lazyKernel().Inject(context);
        context.PreRequestHandlerExecute += OnPreRequestHandlerExecute;
    }

    private void OnPreRequestHandlerExecute(object sender, EventArgs e)
    {
        var currentPage = HttpContext.Current.Handler as Page;
        if (currentPage != null)
        {
            currentPage.InitComplete += OnPageInitComplete;
        }
    }

    private void OnPageInitComplete(object sender, EventArgs e)
    {
        var currentPage = (Page)sender;
        this.lazyKernel().Inject(currentPage);
        this.lazyKernel().Inject(currentPage.Master);
        foreach (Control c in GetControlTree(currentPage))
        {
            this.lazyKernel().Inject(c);
        }

    }

    private IEnumerable<Control> GetControlTree(Control root)
    {
        foreach (Control child in root.Controls)
        {
            yield return child;
            foreach (Control c in GetControlTree(child))
            {
                yield return c;
            }
        }
    }

    private readonly Func<IKernel> lazyKernel;
}

NinjectWebCommon / RegisterServices

    private static void RegisterServices(IKernel kernel)
    {
        kernel.Bind<IHttpModule>().To<InjectPageModule>();
        kernel.Bind<IAmAModel>().To<Model1>();

    } 

Default

public partial class _Default : System.Web.UI.Page
{

    [Inject]
    public IAmAModel Model { get; set; }

    protected void Page_Load(object sender, EventArgs e)
    {
        System.Diagnostics.Trace.WriteLine(Model.ExecuteOperation());
    }
}

Site.Master

public partial class SiteMaster : System.Web.UI.MasterPage
{

    [Inject]
    public IAmAModel Model { get; set; }

    protected void Page_Load(object sender, EventArgs e)
    {
        System.Diagnostics.Trace.WriteLine("From master: " 
            + Model.ExecuteOperation());
    }
}

Models

public interface IAmAModel
{
    string ExecuteOperation();         
}

public class Model1 : IAmAModel
{
    public string ExecuteOperation()
    {
        return "I am a model 1";
    }
}

public class Model2 : IAmAModel
{
    public string ExecuteOperation()
    {
        return "I am a model 2";
    }
}

Results from output window

I am a model 1
From master: I am a model 1
Speedwell answered 1/4, 2014 at 19:50 Comment(3)
Please note that this fails for pages which do not have a Master page. I modified NinjectWebCommon with this: if (currentPage.Master!=null) { this.lazyKernel().Inject(currentPage.Master); }Caliban
Thanks for taking the time to document this. I had the same problem working with a legacy webforms project. Thanks to you got this sorted - adjusted mine as well to work prior to Page_Init as it was getting called too late, but otherwise perfect. Thanks @Adam!Prostitute
This solution caused some really random issues for me. For example, OnCommand events stopped running on LinkButtons within Repeaters. It didn't break all code-behind events though.Bechler
S
8

I think here are the steps to implement Ninject.Web on ASP.NET Web Forms.

  1. Implement NinjectHttpApplication at Global.asax. For the Kernel, pass it in by implementing NinjectModule.
  2. On each web forms page load event at code behind, implement Ninject.Web.PageBase. Add instance class with [Inject] filter on top of it.

For more detailed example, below are some useful links I found:

1.http://joeandcode.net/post/Ninject-2-with-WebForms-35

2.http://davidhayden.com/blog/dave/archive/2008/06/20/NinjectDependencyInjectionASPNETWebPagesSample.aspx

Seashore answered 8/2, 2011 at 15:22 Comment(3)
this assumes you have added the Ninject.Web extension, which is exactly what you need to do.Inadmissible
@Seashore Both of those links are now dead.Jun
joeandcode.net link is now dead. Archived version is here: web.archive.org/web/20160324142135/http://joeandcode.net/post/…Casias
T
0

Check the book "Pro ASP.NET MVC 2 Framework, 2nd Edition" by Steve Sanderson (Apress). The author uses Ninject to connect with a database. I think you can use the examples and adapt them to your needs.

Tetrastichous answered 8/2, 2011 at 13:46 Comment(2)
Yes, I have that which triggered me to ask that question. How is it done if web application is web forms?Seashore
This is also how I typically go about it in WebForms. The trick is you need to use the Get method of the Kernel.Catch
M
0
public IGoalsService_CRUD _context { get; set; }

The _context object is being set to null somehow. Following are the rest of the settings

public partial class CreateGoal : Page
{
    [Inject]
    public IGoalsService_CRUD _context { get; set; }
}

For Global File

protected override IKernel CreateKernel()
{
    IKernel kernel = new StandardKernel(new Bindings());
    return kernel;
}

public class Bindings : NinjectModule
{
    public override void Load()
    {
        Bind<goalsetterEntities>().To<goalsetterEntities>();
        Bind<IGoalsService_CRUD>().To<GoalsService_CRUD>();
    }
}
Mariettemarigold answered 27/3, 2018 at 1:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.