Wie kann ich Ninject oder DI auf asp.net Web Forms implementieren?

83

Es gibt viele Beispiele dafür, wie es in einer MVC-Anwendung funktioniert. Wie wird es in Web Forms gemacht?

Nellbryant
quelle

Antworten:

79

Hier sind die Schritte zur Verwendung von Ninject mit WebForms.

Schritt 1 - Downloads

Es sind zwei Downloads erforderlich - Ninject-2.0.0.0-release-net-3.5 und die WebForm-Erweiterungen Ninject.Web_1.0.0.0_With.log4net (es gibt eine NLog-Alternative).

Die folgenden Dateien müssen in der Webanwendung referenziert werden: Ninject.dll, Ninject.Web.dll, Ninject.Extensions.Logging.dll und Ninject.Extensions.Logging.Log4net.dll.

Schritt 2 - Global.asax

Die Global-Klasse muss von ableiten Ninject.Web.NinjectHttpApplicationund implementieren CreateKernel(), wodurch der Container erstellt wird:

using Ninject; using Ninject.Web;

namespace Company.Web {
    public class Global : NinjectHttpApplication


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

Der StandardKernelKonstruktor nimmt a Module.

Schritt 3 - Modul

In diesem Fall YourWebModuledefiniert das Modul alle Bindungen, die die Webanwendung benötigt:

using Ninject;
using Ninject.Web;

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

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

In diesem Beispiel wird überall dort, wo auf die ICustomerRepositorySchnittstelle verwiesen wird, der Beton CustomerRepositoryverwendet.

Schritt 4 - Seiten

Sobald dies erledigt ist, muss jede Seite erben von 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);
            }

Das InjectAttribute -[Inject]- weist Ninject an, ICustomerRepositoryin die CustomerRepo-Eigenschaft zu injizieren .

Wenn Sie bereits eine Basisseite haben, müssen Sie nur Ihre Basisseite von der Ninject.Web.PageBase ableiten lassen.

Schritt 5 - Masterseiten

Zwangsläufig verfügen Sie über Masterseiten. Damit eine Masterseite auf injizierte Objekte zugreifen kann, müssen Sie Ihre Masterseite ableiten von Ninject.Web.MasterPageBase:

using Ninject;
using Ninject.Web;

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

        #region Properties

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

Schritt 6 - Statische Webdienstmethoden

Das nächste Problem bestand darin, nicht in statische Methoden injizieren zu können. Wir hatten einige Ajax PageMethods, die offensichtlich statisch sind, daher musste ich die Methoden in einen Standard-Webdienst verschieben. Auch hier muss der Webdienst von einer Ninject-Klasse abgeleitet sein - 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 Ihrem JavaScript müssen Sie auf den Standarddienst verweisen - Company.Web.Services.YourWebService.GetProductivity(user, onSuccess)und nicht PageMethods.GetProductivity(user, onSuccess).

Das einzige andere Problem, das ich fand, war das Einfügen von Objekten in Benutzersteuerelemente. Es ist zwar möglich, ein eigenes Basis-UserControl mit Ninject-Funktionen zu erstellen, ich fand es jedoch schneller, dem Benutzersteuerelement eine Eigenschaft für das erforderliche Objekt hinzuzufügen und die Eigenschaft auf der Containerseite festzulegen. Ich denke, die sofortige Unterstützung von UserControls steht auf der "To-Do" -Liste von Ninject.

Das Hinzufügen von Ninject ist recht einfach und eine beredte IoC-Lösung. Viele Leute mögen es, weil es keine XML-Konfiguration gibt. Es hat andere nützliche "Tricks" wie das Verwandeln von Objekten in Singletons mit nur der Ninject-Syntax - Bind<ILogger>().To<WebLogger>().InSingletonScope(). Es ist nicht erforderlich, WebLogger in eine tatsächliche Singleton-Implementierung zu ändern. Das gefällt mir.

Joe Ratzer
quelle
1
Dies ist ein Vergnügen, aber ich habe dies bei einer neuen WebForms-App (aus den Vorlagen) versucht und festgestellt, dass die App einen kryptischen Fehler zurückgegeben hat, als die Datei Global.asax.cs die Standardsitzungsmethoden in (z. B. Application_Start) hatte. Es mag offensichtlich sein, dass Sie diese löschen müssen (wie sie in NinjectHttpApplication implementiert sind), aber es hat mich überrascht.
Matt
Ich bin froh, dass es hilfreich war. Der Fehler klingt seltsam, was war die Ausnahme? Ich habe Code im Application_Start und es funktioniert gut.
Joe Ratzer
3
Ich habe nur vergessen, die Methode NinjectHttpApplication.Application_Start des Supertyps aus der Application_Start-Methode der Datei Global.asax.cs aufzurufen. Funktioniert wenn ich das mache, sonst ist es kaputt gegangen.
Matt
1
Absolut, die Anwendung, an der ich arbeite, hat jetzt eine Basisseite (immer eine gute Idee). Holen Sie sich einfach die Basisseite, um PageBase zu implementieren.
Joe Ratzer
2
Dies scheint jetzt veraltet zu sein. Das NuGet-Paket ninject.web enthält Code zum Booten der App, sodass die Notwendigkeit, direkt von NinjectHttpApplication zu erben, überflüssig ist.
RyanW
68

Mit der Veröffentlichung von Ninject v3.0 (Stand: 12.04.2012) ist es einfacher geworden. Die Injektion wird über HttpModule implementiert, sodass Ihre Seiten nicht von einer benutzerdefinierten Seite / MasterPage geerbt werden müssen. Hier sind die Schritte (und der Code) für eine schnelle Spitze.

  1. Erstellen Sie ein neues ASP.NET WebForms-Projekt
  2. Verwenden Sie NuGet, um die Ninject.Web-Bibliothek hinzuzufügen (wodurch auch die Ninject.Web.Common- und Ninject-Bibliotheken heruntergefahren werden).
  3. Registrieren Sie Ihre benutzerdefinierten Bindungen in der App_Start / NinjectWebCommon.cs / RegisterServices-Methode
  4. Verwenden Sie die Attributinjektion auf Ihren Seiten

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>();
    } 

Standard

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());
    }
}

Modelle

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";
    }
}

Ergebnisse aus dem Ausgabefenster

I am a model 1
From master: I am a model 1
Jason
quelle
Hallo Jason ... Ich versuche das zu implementieren ... Habe die Ninject.Web-Bibliothek von NuGet in Ordnung ... Es wurde ein Bootstrapper auf App_Start erstellt ... Ich habe versucht, einen Haltepunkt auf RegisterServices zum Debuggen hinzuzufügen, aber er kommt nie dort an ... Soll ich noch etwas hinzufügen?
Paul
1
@Paul Sie können Debugger.Break () in die RegisterServices-Methode einfügen, um das Debuggen zu starten.
Boggin
5
Hallo Jason. Ich habe Probleme, dies zum Laufen zu bringen, also habe ich mich gefragt, ob dies der vollständige Überblick ist. Ich kann sehen, dass das NinjectHttpModule zur Laufzeit geladen wird, aber keine Injektion stattfindet. Können Sie sich einen Grund vorstellen, warum dies der Fall sein sollte?
Rusmus
3
Tolles Beispiel Danke Es funktioniert auf der Seite / Masterseite, aber nicht auf Asmx. Wie kann ich es auf Asmx zum Laufen bringen? Vielen Dank.
Lawphotog
1
@mreyeros Stellen Sie sicher, dass Sie eine NinjectWeb.csin haben App_Start. Ihr Code zum Initialisieren von Ninject muss in dieser Datei enthalten sein. Wenn es sich in einer separaten Datei befindet (z. B. NinjectWebCommon.csfunktioniert es nicht). Dies kann passieren, wenn Sie Ninject.Web später als die anderen Ninject-Pakete mit NuGet installieren.
Nebffa
12

Die Antwort hier funktioniert derzeit aufgrund eines offenen Fehlers nicht . Hier ist eine modifizierte Version von @ Jasons Schritten, bei denen ein Kunden-http-Modul zum Einfügen in Seiten und Steuerelemente verwendet wird, ohne von ninject-Klassen erben zu müssen.

  1. Erstellen Sie ein neues ASP.NET WebForms-Projekt
  2. Verwenden Sie NuGet, um die Ninject.Web-Bibliothek hinzuzufügen
  3. Registrieren Sie Ihre benutzerdefinierten Bindungen in der App_Start / NinjectWebCommon.cs / RegisterServices-Methode
  4. Fügen Sie InjectPageModule hinzu und registrieren Sie sich in NinjectWebCommon
  5. Verwenden Sie die Attributinjektion auf Ihren Seiten

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>();

    } 

Standard

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());
    }
}

Modelle

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";
    }
}

Ergebnisse aus dem Ausgabefenster

I am a model 1
From master: I am a model 1
Adam Bell
quelle
4
Bitte beachten Sie, dass dies für Seiten fehlschlägt, die keine Masterseite haben. Ich habe NinjectWebCommon folgendermaßen geändert: if (currentPage.Master!=null) { this.lazyKernel().Inject(currentPage.Master); }
H. Wolper
1
Vielen Dank, dass Sie sich die Zeit genommen haben, dies zu dokumentieren. Ich hatte das gleiche Problem bei der Arbeit mit einem älteren Webforms-Projekt. Dank dir hast du diese sortiert - angepasst, um auch vor Page_Init zu arbeiten, da es zu spät aufgerufen wurde, aber ansonsten perfekt. Danke @Adam!
PoorbandTony
Diese Lösung verursachte einige wirklich zufällige Probleme für mich. Beispielsweise wurden OnCommand-Ereignisse auf LinkButtons in Repeatern nicht mehr ausgeführt. Es wurden jedoch nicht alle Code-Behind-Ereignisse unterbrochen.
Mike Cole
8

Ich denke, hier sind die Schritte zum Implementieren von Ninject.Web auf ASP.NET Web Forms.

  1. Implementieren Sie NinjectHttpApplication unter Global.asax. Übergeben Sie den Kernel, indem Sie NinjectModule implementieren.
  2. Implementieren Sie Ninject.Web.PageBase auf jedem Ladeereignis einer Webformularseite im Code dahinter. Fügen Sie eine Instanzklasse mit dem Filter [Inject] hinzu.

Im Folgenden finden Sie einige nützliche Links, die ich gefunden habe:

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

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

Nellbryant
quelle
1
Dies setzt voraus, dass Sie die Ninject.Web-Erweiterung hinzugefügt haben. Genau das müssen Sie tun.
Dave Thieben
7
@nellbryant Beide Links sind jetzt tot.
Boggin
Der Link joeandcode.net ist jetzt tot. Die archivierte Version ist hier: web.archive.org/web/20160324142135/http://joeandcode.net/post/…
Chris Walsh
4

Schauen Sie sich die Ninject.Web-Erweiterung an. Es bietet die Basisinfrastruktur https://github.com/ninject/ninject.web

Remo Gloor
quelle
5
Ich weiß, ich sollte mit gutem Beispiel vorangehen, aber die README sollte> 0 Bytes sein!
Ruben Bartelink
@ Ruben: Ich bin damit einverstanden, dass das Aktualisieren des Doku eine der Hauptaufgaben auf der Liste atm ist.
Remo Gloor
2
Es geht um Priorität und darum, wie viel Hilfe von der Community kommt. Im vergangenen Jahr wurde viel Dokumentation hinzugefügt. Nicht für Ninject.Web, da ich es für weniger wichtig halte als andere Funktionen, weil: a) WebForms veraltet ist - verwenden Sie stattdessen MVC b) Ich habe diese Erweiterung nicht geschrieben. Wie alle anderen muss ich das Wissen über diese Erweiterung aus dem Code entnehmen. c) Ich benutze es nicht selbst - es gibt andere, die das Dokument viel besser schreiben können als ich. Aber es scheint für keinen von ihnen wichtig zu sein. Meine Freizeit ist wie deine begrenzt und deshalb kann ich nicht alles machen.
Remo Gloor
6
@RyanW Downvoting nur wegen fehlender Dokumentation wird meine Motivation nicht wirklich erhöhen oder die Priorität erhöhen, dieses Dokument für Sie zu schreiben!
Remo Gloor
1
@ RyanW Woher bekommst du deine Ideen? Ich fühle mich schlecht wegen meines snarky Kommentars von vor einem Jahr, aber a) es wurde von einer Gegenstimme begleitet b) es war sarkastisch, aber nicht weinerlich. Bitte widerlegen Sie die völlig legitimen Punkte, die Remo dem Wegwerfen von Spielzeug vorzieht. Übrigens, ich bin mir sicher, dass Unity eine großartige Dokumentation hat und vielleicht mehr für Sie ist. Schauen Sie sich die Antwort von nellbryant oder Jor R an und versuchen Sie, entweder daran etwas auszusetzen oder selbst eine Readme-Datei oder ein Beispiel zu schreiben.
Ruben Bartelink
0

Lesen Sie das Buch "Pro ASP.NET MVC 2 Framework, 2. Ausgabe" von Steve Sanderson (Apress). Der Autor verwendet Ninject, um eine Verbindung mit einer Datenbank herzustellen. Ich denke, Sie können die Beispiele verwenden und sie an Ihre Bedürfnisse anpassen.

jmpcm
quelle
Ja, ich habe das, was mich dazu veranlasst hat, diese Frage zu stellen. Wie wird es gemacht, wenn es sich bei der Webanwendung um Webformulare handelt?
Nellbryant
So gehe ich normalerweise auch in WebForms vor. Der Trick ist, dass Sie die Get-Methode des Kernels verwenden müssen.
Didaxis
0
public IGoalsService_CRUD _context { get; set; }

Das _context-Objekt wird irgendwie auf null gesetzt. Im Folgenden sind die restlichen Einstellungen aufgeführt

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

Für globale Datei

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>();
    }
}
bshah
quelle