Jak zaimplementować Ninject lub DI w formularzach sieci Web asp.net?

83

Istnieje wiele przykładów na to, aby działał on w aplikacji MVC. Jak to się robi w formularzach sieci Web?

nellbryant
źródło

Odpowiedzi:

79

Oto kroki, aby używać Ninject z WebForms.

Krok 1 - Pobieranie

Wymagane są dwa pliki do pobrania - Ninject-2.0.0.0-release-net-3.5 i rozszerzenia WebForm Ninject.Web_1.0.0.0_With.log4net (istnieje alternatywa NLog ).

W aplikacji sieciowej muszą znajdować się odniesienia do następujących plików: Ninject.dll, Ninject.Web.dll, Ninject.Extensions.Logging.dll i Ninject.Extensions.Logging.Log4net.dll.

Krok 2 - Global.asax

Klasa Global musi pochodzić z Ninject.Web.NinjectHttpApplicationi zaimplementować CreateKernel(), co tworzy kontener:

using Ninject; using Ninject.Web;

namespace Company.Web {
    public class Global : NinjectHttpApplication


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

StandardKernelKonstruktor trwa Module.

Krok 3 - Moduł

Moduł w tym przypadku YourWebModuledefiniuje wszystkie powiązania, których będzie potrzebować aplikacja internetowa:

using Ninject;
using Ninject.Web;

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

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

W tym przykładzie wszędzie tam, gdzie ICustomerRepositoryjest odniesienie do interfejsu, CustomerRepositoryzostanie użyty beton .

Krok 4 - Strony

Gdy to zrobisz, każda strona musi dziedziczyć z 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);
            }

InjectAttribute -[Inject]- mówi Ninject wstrzyknąć ICustomerRepositorydo CustomerRepo Property.

Jeśli masz już stronę podstawową, wystarczy, aby Twoja strona podstawowa pochodziła z Ninject.Web.PageBase.

Krok 5 - Strony wzorcowe

Nieuchronnie będziesz mieć strony wzorcowe i aby umożliwić MasterPage dostęp do wstrzykniętych obiektów, z których będziesz musiał wyprowadzić swoją stronę wzorcową Ninject.Web.MasterPageBase:

using Ninject;
using Ninject.Web;

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

        #region Properties

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

Krok 6 - statyczne metody usług internetowych

Następnym problemem była niemożność wstrzyknięcia do metod statycznych. Mieliśmy kilka Ajax PageMethods, które są oczywiście statyczne, więc musiałem przenieść te metody do standardowej usługi internetowej. Ponownie, usługa internetowa musi pochodzić z klasy Ninject - 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);

W swoim JavaScript musisz odwołać się do standardowej usługi - Company.Web.Services.YourWebService.GetProductivity(user, onSuccess)zamiast PageMethods.GetProductivity(user, onSuccess).

Jedynym innym problemem, jaki znalazłem, było wstrzykiwanie obiektów do kontroli użytkownika. Chociaż możliwe jest utworzenie własnej podstawowej UserControl z funkcjami Ninject, szybciej okazało się, że dodawanie właściwości do kontrolki użytkownika dla wymaganego obiektu i ustawienie właściwości na stronie kontenera jest możliwe. Myślę, że obsługa UserControls po wyjęciu z pudełka znajduje się na liście „rzeczy do zrobienia” Ninject.

Dodanie Ninject jest dość proste i jest to wymowne rozwiązanie IoC. Wiele osób to lubi, ponieważ nie ma konfiguracji Xml. Ma inne przydatne „sztuczki”, takie jak przekształcanie obiektów w Singletony za pomocą tylko składni Ninject - Bind<ILogger>().To<WebLogger>().InSingletonScope(). Nie ma potrzeby zmieniania WebLoggera na rzeczywistą implementację Singletona, podoba mi się to.

Joe Ratzer
źródło
1
Działa to świetnie, ale wypróbowałem to w nowej aplikacji WebForms (z szablonów) i stwierdziłem, że gdy plik Global.asax.cs miał domyślne metody sesji w (np. Application_Start), aplikacja zwróciła tajemniczy błąd. Może być oczywiste, że musisz je usunąć (ponieważ są one zaimplementowane w NinjectHttpApplication), ale mnie to zaskoczyło.
Matt,
Cieszę się, że to było pomocne. Błąd brzmi dziwnie, jaki był wyjątek? Mam kod w Application_Start i działa dobrze.
Joe Ratzer
3
Właśnie zapomniałem wywołać metodę supertype NinjectHttpApplication.Application_Start z metody Application_Start pliku Global.asax.cs. Działa, kiedy to robię, inaczej się zepsuł.
Matt
1
Oczywiście aplikacja, nad którą pracuję, ma teraz stronę bazową (zawsze dobry pomysł), po prostu pobierz stronę podstawową, aby zaimplementować PageBase.
Joe Ratzer
2
To wydaje się teraz przestarzałe. Pakiet NuGet, ninject.web, zawiera kod do ładowania początkowego aplikacji, przez co konieczność dziedziczenia bezpośrednio z NinjectHttpApplication jest przestarzała?
RyanW
68

Stało się to łatwiejsze dzięki wydaniu Ninject v3.0 (stan na 12.04.2012). Wstrzykiwanie jest realizowane za pośrednictwem HttpModule, więc nie ma potrzeby dziedziczenia stron po niestandardowej stronie / MasterPage. Oto kroki (i kod) dla szybkiego wzrostu.

  1. Utwórz nowy projekt ASP.NET WebForms
  2. Użyj NuGet, aby dodać bibliotekę Ninject.Web (która spowoduje również obniżenie bibliotek Ninject.Web.Common i Ninject)
  3. Zarejestruj swoje niestandardowe powiązania w metodzie App_Start / NinjectWebCommon.cs / RegisterServices
  4. Użyj wstrzykiwania atrybutów na swoich stronach

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

Domyślna

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

Modele

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

Wyniki z okna wyników

I am a model 1
From master: I am a model 1
Jason
źródło
Cześć Jason ... Próbuję to zaimplementować ... Mam bibliotekę Ninject.Web z NuGet w porządku ... Utworzono program Bootstrapper na App_Start ... Próbowałem dodać punkt przerwania w RegisterServices do debugowania, ale nigdy się tam nie dostał ... Czy mam dodać coś jeszcze?
Paul
1
@Paul Możesz umieścić Debugger.Break () w metodzie RegisterServices, aby rozpocząć debugowanie.
Boggin,
5
Cześć Jason. Mam problem z uruchomieniem tego, więc zastanawiałem się, czy to jest kompletny przegląd? Widzę, że moduł NinjectHttpModule jest ładowany w czasie wykonywania, ale nie następuje iniekcja. Czy możesz wymyślić jakiś powód, dla którego powinno to mieć miejsce?
rusmus
3
Świetny przykład Dzięki Działa na stronie / stronie wzorcowej, ale nie na Asmx. Jak mogę sprawić, by działało na Asmx? Dzięki.
lawphotog
1
@mreyeros Upewnij się, że masz plik NinjectWeb.csin App_Start. Twój kod inicjujący Ninject musi znajdować się w tym pliku. Jeśli jest w osobnym pliku (np NinjectWebCommon.cs. Nie zadziała). Może się tak zdarzyć, jeśli zainstalujesz Ninject.Web później niż inne pakiety Ninject przy użyciu NuGet.
nebffa
12

Odpowiedź tutaj obecnie nie działa z powodu otwartego błędu . Oto zmodyfikowana wersja kroków @ Jasona wykorzystujących httpmodule klienta do wprowadzania do stron i kontrolek bez konieczności dziedziczenia z klas ninject.

  1. Utwórz nowy projekt ASP.NET WebForms
  2. Użyj NuGet, aby dodać bibliotekę Ninject.Web
  3. Zarejestruj swoje niestandardowe powiązania w metodzie App_Start / NinjectWebCommon.cs / RegisterServices
  4. Dodaj InjectPageModule i zarejestruj się w NinjectWebCommon
  5. Użyj wstrzykiwania atrybutów na swoich stronach

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

    } 

Domyślna

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

Modele

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

Wyniki z okna wyników

I am a model 1
From master: I am a model 1
Adam Bell
źródło
4
Należy pamiętać, że to się nie powiedzie w przypadku stron, które nie mają strony wzorcowej. Zmodyfikowałem NinjectWebCommon w ten sposób: if (currentPage.Master!=null) { this.lazyKernel().Inject(currentPage.Master); }
H.Wolper
1
Dziękuję za poświęcenie czasu na udokumentowanie tego. Miałem ten sam problem podczas pracy ze starszym projektem formularzy internetowych. Dzięki tobie udało się to załatwić - dostosowałem również moje do pracy przed Page_Init, ponieważ było wywoływane za późno, ale poza tym idealnie. Dzięki @Adam!
PoorbandTony
To rozwiązanie spowodowało dla mnie naprawdę przypadkowe problemy. Na przykład zdarzenia OnCommand przestały działać na LinkButtons w Repeaters. Nie złamał jednak wszystkich zdarzeń związanych z kodem.
Mike Cole
8

Myślę, że oto kroki, aby zaimplementować Ninject.Web w ASP.NET Web Forms.

  1. Zaimplementuj NinjectHttpApplication w Global.asax. W przypadku jądra przekaż go, implementując NinjectModule.
  2. Zaimplementuj Ninject.Web.PageBase na każdym zdarzeniu ładowania strony formularzy internetowych w kodzie za nim. Dodaj klasę instancji z filtrem [Inject] na górze.

Aby uzyskać bardziej szczegółowy przykład, poniżej znajduje się kilka przydatnych linków, które znalazłem:

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

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

nellbryant
źródło
1
zakłada się, że dodałeś rozszerzenie Ninject.Web, co jest dokładnie tym, co musisz zrobić.
Dave Thieben
7
@nellbryant Oba te linki są teraz martwe.
Boggin
Link joeandcode.net jest teraz martwy. Wersja archiwalna jest tutaj: web.archive.org/web/20160324142135/http://joeandcode.net/post/…
Chris Walsh
4

Spójrz na rozszerzenie Ninject.Web. Zapewnia podstawową infrastrukturę https://github.com/ninject/ninject.web

Remo Gloor
źródło
5
Wiem, że powinienem dawać przykład, ale README powinien wynosić> 0 bajtów!
Ruben Bartelink,
@Ruben: Zgadzam się, że aktualizacja dokumentu jest jednym z najważniejszych zadań na liście atm.
Remo Gloor
2
Chodzi o priorytet i to, ile pomocy pochodzi od społeczności. W ubiegłym roku dodano wiele dokumentacji. Nie dla Ninject.Web, ponieważ uważam to za mniej ważne niż inne funkcje, ponieważ: a) WebForms jest przestarzały - użyj zamiast tego MVC b) Nie napisałem tego rozszerzenia. Podobnie jak wszyscy inni, muszę uzyskać wiedzę na temat tego rozszerzenia z kodu. c) Sam go nie używam - są inni, którzy potrafią napisać dokument znacznie lepiej niż ja. Ale wydaje się, że dla żadnego z nich nie jest to ważne. Mój wolny czas jest tak ograniczony jak Twój i dlatego nie mogę zrobić wszystkiego.
Remo Gloor
6
@RyanW Głosowanie w dół tylko z powodu braku dokumentacji nie zwiększy mojej motywacji ani nie zwiększy priorytetu pisania tego dokumentu dla Ciebie!
Remo Gloor
1
@RyanW Skąd bierzesz swoje pomysły? Czuję się źle z powodu mojego złośliwego komentarza sprzed roku, ale a) towarzyszył mu głos pozytywny b) był sarkastyczny, ale nie jęczący. Proszę odrzucić całkowicie uzasadnione uwagi, które Remo woli wyrzucać zabawki. BTW, jestem pewien, że Unity ma niesamowitą dokumentację i może być bardziej w twoim zaułku. I spójrz na odpowiedź nellbryant lub Jor R i spróbuj znaleźć w niej błąd lub użyj jej do samodzielnego napisania pliku readme lub próbki.
Ruben Bartelink
0

Zapoznaj się z książką „Pro ASP.NET MVC 2 Framework, 2nd Edition” autorstwa Steve Sanderson (Apress). Autor używa Ninject do łączenia się z bazą danych. Myślę, że możesz wykorzystać przykłady i dostosować je do swoich potrzeb.

jmpcm
źródło
Tak, mam to, co skłoniło mnie do zadania tego pytania. Jak to się robi, jeśli aplikacja internetowa to formularze internetowe?
nellbryant
Tak też zwykle postępuję w WebForms. Sztuczka polega na tym, że musisz użyć metody Get jądra.
Didaxis
0
public IGoalsService_CRUD _context { get; set; }

Obiekt _context jest w jakiś sposób ustawiany na null. Poniżej przedstawiono pozostałe ustawienia

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

Dla pliku globalnego

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
źródło