Как я могу реализовать Ninject или DI в веб-формах asp.net?

83

Есть много примеров того, как это работало в приложении MVC. Как это делается в веб-формах?

Nellbryant
источник

Ответы:

79

Вот шаги по использованию Ninject с WebForms.

Шаг 1 - Загрузки

Требуются две загрузки - Ninject-2.0.0.0-release-net-3.5 и расширения WebForm Ninject.Web_1.0.0.0_With.log4net (есть альтернатива NLog).

В веб-приложении необходимо указать следующие файлы: Ninject.dll, Ninject.Web.dll, Ninject.Extensions.Logging.dll и Ninject.Extensions.Logging.Log4net.dll.

Шаг 2 - Global.asax

Класс Global должен быть унаследован Ninject.Web.NinjectHttpApplicationи реализован CreateKernel(), что создает контейнер:

using Ninject; using Ninject.Web;

namespace Company.Web {
    public class Global : NinjectHttpApplication


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

StandardKernelКонструктор принимает Module.

Шаг 3 - Модуль

Модуль в этом случае YourWebModuleопределяет все привязки, которые потребуются веб-приложению:

using Ninject;
using Ninject.Web;

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

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

В этом примере, где бы ни ICustomerRepositoryупоминался интерфейс, CustomerRepositoryбудет использоваться бетон .

Шаг 4 - Страницы

Как только это будет сделано, каждая страница должна унаследовать от 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]- говорит Ninject впрыснуть ICustomerRepositoryв CustomerRepo собственности.

Если у вас уже есть базовая страница, вам просто нужно получить базовую страницу, производную от Ninject.Web.PageBase.

Шаг 5 - мастер-страницы

Неизбежно у вас будут главные страницы, и чтобы позволить MasterPage получить доступ к внедренным объектам, вам нужно будет получить главную страницу из Ninject.Web.MasterPageBase:

using Ninject;
using Ninject.Web;

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

        #region Properties

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

Шаг 6 - Статические методы веб-службы

Следующая проблема заключалась в невозможности внедрить в статические методы. У нас было несколько методов Ajax PageMethods, которые явно статичны, поэтому мне пришлось перенести методы в стандартный веб-сервис. Опять же, веб-сервис должен быть производным от класса 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);

В вашем JavaScript вам нужно будет ссылаться на стандартный сервис - Company.Web.Services.YourWebService.GetProductivity(user, onSuccess), а не наPageMethods.GetProductivity(user, onSuccess) .

Единственная другая проблема, которую я обнаружил, - это внедрение объектов в пользовательские элементы управления. Хотя можно создать свой собственный базовый UserControl с возможностями Ninject, я обнаружил, что быстрее добавить свойство в пользовательский элемент управления для требуемого объекта и установить свойство на странице контейнера. Я думаю, что поддержка UserControls из коробки находится в списке дел Ninject.

Добавить Ninject довольно просто, и это красноречивое решение IoC. Многим это нравится, потому что здесь нет конфигурации Xml. В нем есть и другие полезные "приемы", такие как превращение объектов в синглтоны с помощью синтаксиса Ninject - Bind<ILogger>().To<WebLogger>().InSingletonScope(). Нет необходимости превращать WebLogger в реальную имплементацию Singleton, мне это нравится.

Джо Ратцер
источник
1
Это работает, но я попробовал это в новом приложении WebForms (из шаблонов) и обнаружил, что когда в файле Global.asax.cs были методы сеанса по умолчанию в (например, Application_Start), приложение возвращало загадочную ошибку. Может быть очевидно, что вам нужно удалить их (поскольку они реализованы в NinjectHttpApplication), но меня это поймало.
Мэтт
Рад, что это было полезно. Ошибка звучит странно, в чем было исключение? У меня есть код в Application_Start, и он отлично работает.
Joe Ratzer
3
Я просто забыл вызвать метод супертипа NinjectHttpApplication.Application_Start из метода Application_Start файла Global.asax.cs. Работает, когда я так делаю, иначе сломалось.
Мэтт
1
Безусловно, приложение, над которым я работаю, теперь имеет базовую страницу (всегда хорошая идея), просто получите базовую страницу для реализации PageBase.
Джо Ратцер
2
Сейчас это кажется устаревшим. Пакет NuGet, ninject.web, содержит код для начальной загрузки приложения, поэтому необходимость наследования напрямую от NinjectHttpApplication устарела?
RyanW
68

Это стало проще с выпуском Ninject v3.0 (от 12.04.2012). Внедрение осуществляется через HttpModule, поэтому нет необходимости наследовать ваши страницы от настраиваемой Page / MasterPage. Вот шаги (и код) для быстрого всплеска.

  1. Создайте новый проект ASP.NET WebForms
  2. Используйте NuGet, чтобы добавить библиотеку Ninject.Web (которая также отключит библиотеки Ninject.Web.Common и Ninject)
  3. Зарегистрируйте свои пользовательские привязки в методе App_Start / NinjectWebCommon.cs / RegisterServices
  4. Используйте внедрение атрибутов на своих страницах

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

По умолчанию

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

Модели

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

Результаты из окна вывода

I am a model 1
From master: I am a model 1
Джейсон
источник
Привет, Джейсон ... Я пытаюсь реализовать это ... Получил Ninject.Web lib из NuGet в порядке ... Он создал Bootstrapper на App_Start ... Я попытался добавить точку останова на RegisterServices для отладки, но он так и не дошел ... Могу ли я добавить что-нибудь еще?
Пол
1
@Paul Вы можете поместить Debugger.Break () в метод RegisterServices, чтобы начать отладку.
Боггин,
5
Привет, Джейсон. У меня возникают проблемы с тем, чтобы это работало, поэтому мне было интересно, не является ли это полным изложением? Я вижу, что NinjectHttpModule загружается во время выполнения, но инъекции не происходит. Можете ли вы придумать причину, по которой это должно быть так?
Rusmus
3
Отличный пример. Спасибо. Он работает на странице / главной странице, но не на Asmx. Как я могу заставить его работать на Asmx? Благодарю.
lawphotog
1
@mreyeros Убедитесь , что у вас есть NinjectWeb.csв App_Start. Ваш код для инициализации Ninject должен находиться в этом файле. Если он в отдельном файле (например NinjectWebCommon.cs, не сработает). Это может произойти, если вы установите Ninject.Web позже, чем другие пакеты Ninject, использующие NuGet.
nebffa
12

Ответ здесь в настоящее время не работает из-за открытой ошибки . Вот модифицированная версия шагов @Jason с использованием клиентского httpmodule для внедрения в страницы и элементы управления без необходимости наследования от классов ninject.

  1. Создайте новый проект ASP.NET WebForms
  2. Используйте NuGet, чтобы добавить библиотеку Ninject.Web
  3. Зарегистрируйте свои пользовательские привязки в методе App_Start / NinjectWebCommon.cs / RegisterServices
  4. Добавьте InjectPageModule и зарегистрируйтесь в NinjectWebCommon
  5. Используйте внедрение атрибутов на своих страницах

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

    } 

По умолчанию

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

Модели

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

Результаты из окна вывода

I am a model 1
From master: I am a model 1
Адам Белл
источник
4
Обратите внимание, что это не работает для страниц, не имеющих главной страницы. Я модифицировал NinjectWebCommon следующим образом: if (currentPage.Master!=null) { this.lazyKernel().Inject(currentPage.Master); }
H.Wolper 05
1
Спасибо, что нашли время задокументировать это. У меня была такая же проблема при работе с устаревшим проектом веб-форм. Благодаря вам этот отсортированный - скорректированный мой также работал до Page_Init, так как он вызывался слишком поздно, но в остальном идеально. Спасибо @Adam!
PoorbandTony
Это решение вызвало у меня действительно случайные проблемы. Например, события OnCommand перестали выполняться на LinkButton в Repeaters. Однако это не нарушило всех событий кода программной части.
Майк Коул
8

Я думаю, вот шаги по реализации Ninject.Web в веб-формах ASP.NET.

  1. Внедрите NinjectHttpApplication в Global.asax. Для ядра передайте его, реализовав NinjectModule.
  2. В каждом событии загрузки страницы веб-форм в коде позади реализуйте Ninject.Web.PageBase. Добавьте класс экземпляра с фильтром [Inject] поверх него.

Для более подробного примера ниже приведены некоторые полезные ссылки, которые я нашел:

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

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

Nellbryant
источник
1
это предполагает, что вы добавили расширение Ninject.Web, а это именно то, что вам нужно.
Дэйв Тибен
7
@nellbryant Обе эти ссылки теперь мертвы.
Боггин,
Ссылка joeandcode.net теперь мертва. Архивная версия находится здесь: web.archive.org/web/20160324142135/http://joeandcode.net/post/…
Крис Уолш
4

Взгляните на расширение Ninject.Web. Он предоставляет базовую инфраструктуру https://github.com/ninject/ninject.web

Ремо Глор
источник
5
Я знаю, что должен подавать пример, но README должен быть> 0 байт!
Рубен Бартелинк
@Ruben: Я согласен, что обновление документа - одна из первоочередных задач в списке банкоматов.
Ремо Глор
2
Все дело в приоритете и в том, сколько помощи приходит от сообщества. За последний год было добавлено много документации. Не для Ninject.Web, потому что я считаю его менее важным, чем другие функции, потому что: а) WebForms устарел - используйте вместо него MVC б) Я не писал это расширение. Как и все, я должен получить информацию об этом расширении из кода. в) Я сам им не пользуюсь - есть другие, которые могут написать документ намного лучше меня. Но, кажется, никому из них это не важно. Мое свободное время ограничено, как и ваше, и поэтому я не могу делать все.
Remo Gloor 02
6
@RyanW Голосование против только из-за отсутствия документации на самом деле не увеличит мою мотивацию и не повысит приоритет написания этого документа для вас!
Remo Gloor 02
1
@RyanW Откуда у тебя идеи? Мне не нравится мой язвительный комментарий, сделанный год назад, но а) он сопровождался положительным голосом, б) он был саркастичным, но не плаксивым. Пожалуйста, опровергните полностью законные утверждения, которые Ремо предпочитает выбрасывать игрушки. Кстати, я уверен, что у Unity отличная документация, и, возможно, она вам подойдет. И посмотрите на ответ nellbryant или Jor R и попробуйте либо придраться к нему, либо использовать его для написания ридми или образца самостоятельно.
Рубен Бартелинк,
0

Посмотрите книгу Стива Сандерсона (Apress) "Pro ASP.NET MVC 2 Framework, 2nd Edition". Автор использует Ninject для подключения к базе данных. Я думаю, вы можете использовать примеры и адаптировать их к своим потребностям.

jmpcm
источник
Да, у меня есть то, что побудило меня задать этот вопрос. Как это сделать, если веб-приложение - это веб-формы?
nellbryant
То же самое я обычно делаю в WebForms. Хитрость в том, что вам нужно использовать метод Get ядра.
Didaxis
0
public IGoalsService_CRUD _context { get; set; }

Для объекта _context каким-то образом устанавливается значение null. Ниже приведены остальные настройки.

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

Для глобального файла

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>();
    }
}
бшах
источник