Најбоље праксе, савети и трикови за убризгавање језгре зависности АСП.НЕТ-а

У овом чланку ћу поделити своја искуства и предлоге о коришћењу Депенденци Ињецтион у АСП.НЕТ Цоре апликацијама. Мотивација ових принципа је;

  • Ефикасно дизајнирање услуга и њихових зависности.
  • Спречавање проблема са више навоја.
  • Спречавање цурења меморије.
  • Спречавање потенцијалних грешака.

Овај чланак претпоставља да сте већ упознати са Депенденци Ињецтион и АСП.НЕТ Цоре на основном нивоу. Ако не, прво прочитајте документацију за убризгавање језгре АСП.НЕТ.

Основе

Убризгавање конструктора

Убризгавање конструктора користи се за проглашавање и добијање зависности услуге о сервисној конструкцији. Пример:

ПродуцтСервице јавне класе
{
    приватно само за читање ИПродуцтРепоситори _продуцтРепоситори;
    јавна ПродуцтСервице (ИПродуцтРепоситори продуцтРепоситори)
    {
        _продуцтРепоситори = продуцтРепоситори;
    }
    јавна воид Избриши (инт ид)
    {
        _продуцтРепоситори.Делете (ид);
    }
}

ПродуцтСервице убризгава ИПродуцтРепоситори као зависност у свој конструктор, а затим га користи унутар Делете методе.

Добре праксе:

  • Дефинирајте потребне зависности изричито у сервисном конструктору. Стога се услуга не може конструирати без њених зависности.
  • Додијелите ињектирану зависност пољу / својству само за читање (како бисте спријечили да случајно додијелите другу вриједност њему унутар методе).

Ињецтион Проперти

Стандардни спремник убризгавања зависности АСП.НЕТ Цоре не подржава убризгавање својстава. Али можете користити други спремник који подржава убризгавање имовине. Пример:

коришћењем Мицрософт.Ектенсионс.Логгинг;
коришћењем Мицрософт.Ектенсионс.Логгинг.Абстрацтионс;
простор за имена МиАпп
{
    ПродуцтСервице јавне класе
    {
        јавни ИЛоггер <ПродуцтСервице> Логгер {гет; комплет; }
        приватно само за читање ИПродуцтРепоситори _продуцтРепоситори;
        јавна ПродуцтСервице (ИПродуцтРепоситори продуцтРепоситори)
        {
            _продуцтРепоситори = продуцтРепоситори;
            Логгер = НуллЛоггер <ПродуцтСервице> .Инстанце;
        }
        јавна воид Избриши (инт ид)
        {
            _продуцтРепоситори.Делете (ид);
            Логгер.ЛогИнформатион (
                $ "Избрисао је производ са ид = {ид}");
        }
    }
}

ПродуцтСервице изјављује својство Логгера јавним сеттером. Контејнер за убризгавање зависности може подесити Логгер ако је доступан (регистрован у ДИ спремник претходно).

Добре праксе:

  • Користите убризгавање својстава само за необавезне зависности. То значи да ваша услуга може правилно да функционише без давања ових зависности.
  • Користите Нулл Објецт Паттерн (као што је случај у овом примеру) ако је могуће. У супротном, увек користите проверу за нулу док користите зависност.

Лоцатор сервиса

Образац локатора услуга је још један начин добивања зависности. Пример:

ПродуцтСервице јавне класе
{
    приватно само за читање ИПродуцтРепоситори _продуцтРепоситори;
    приватно само за читање ИЛоггер <ПродуцтСервице> _логгер;
    јавна услуга (услуга ИСервицеПровидерПровидер)
    {
        _продуцтРепоситори = сервицеПровидер
          .ГетРекуиредСервице <ИПродуцтРепоситори> ();
        _логгер = услугаПровидер
          .ГетСервице <ИЛоггер <ПродуцтСервице>> () ??
            НуллЛоггер <ПродуцтСервице> .Инстанце;
    }
    јавна воид Избриши (инт ид)
    {
        _продуцтРепоситори.Делете (ид);
        _логгер.ЛогИнформатион ($ "Избрисан је производ са ид = {ид}");
    }
}

ПродуцтСервице убризгава ИСервицеПровидер и решава зависности користећи га. ГетРекуиредСервице изузима изузетак ако тражена зависност није раније регистрована. С друге стране, ГетСервице у том случају само враћа нулу.

Када решите услуге унутар конструктора, они се пуштају када се услуга изда. Дакле, не занима вас пуштање / одлагање услуга решених унутар конструктора (баш као и убризгавање конструктора и својстава).

Добре праксе:

  • Немојте користити образац локатора услуге кад год је то могуће (ако је врста услуге позната у време развоја). Зато што зависности подразумева. То значи да није могуће лако видети зависности током прављења инстанције услуге. Ово је посебно важно за јединице тестова где бисте желели да се ругате неким зависностима сервиса.
  • Решите зависности у сервисном конструктору ако је могуће. Решавање у сервисном методу чини вашу апликацију сложенијом и склони грешкама. У наредним одељцима покрићу проблеме и решења.

Сервице Лифе Тимес

Постоје три животна века у АСП.НЕТ увођењу језгре зависности:

  1. Привремене услуге се креирају сваки пут када се убризгавају или затраже.
  2. Опсег услуга креира се по обиму. У веб апликацији, сваки веб захтев ствара нови одвојени опсег услуга. То значи да се опсег услуга углавном креира по захтеву веба.
  3. Синглетон услуге се креирају по ДИ контејнеру. То генерално значи да се креирају само један пут по апликацији, а затим се користе током целог животног века апликације.

ДИ контејнер прати све решене услуге. Услуге се ослобађају и одлажу након истека њиховог века трајања:

  • Ако сервис има зависности, оне се такође аутоматски пуштају и одлажу.
  • Ако сервис имплементира интерфејс који може да се користи, метод пуштања аутоматски се позива на издање услуге.

Добре праксе:

  • Региструјте своје услуге као пролазне кад год је то могуће. Јер је једноставно дизајнирати привремене услуге. Углавном вас не занимају вишеструке навоје и цурење меморије и знате да сервис има кратак век.
  • Пажљиво користите опсег сервисног века услуга, јер може бити тешко ако направите опсег услуга за децу или користите ове услуге из не-веб апликације.
  • Пажљиво користите синглетон век трајања, од тада требате да се бавите проблемима с вишеструким навојем и потенцијалним пропуштањем меморије.
  • Не зависите од пролазне или опсежне услуге од синглетон услуге. Будући да прелазна услуга постаје синглетон инстанција када је синглетон услуга убризгава и то може створити проблеме ако прелазна услуга није дизајнирана тако да подржава такав сценарио. Подразумевани ДИ контејнер АСП.НЕТ Цоре већ баца изузетака у таквим случајевима.

Решавање услуга у методом тела

У неким случајевима ће вам можда требати да разрешите другу услугу на неки начин своје услуге. У таквим случајевима осигурајте да услугу пустите након употребе. Најбољи начин да се то осигура је креирање опсега услуге. Пример:

ПрицеЦалцулатор јавне класе
{
    приватно само за читање ИСервицеПровидер _сервицеПровидер;
    јавни ПрицеЦалцулатор (услуга ИСервицеПровидерПровидер)
    {
        _сервицеПровидер = услугаПровидер;
    }
    јавни пловак Израчунајте (производ производа, укупан број,
      Унесите порезСтратегиСервицеТипе)
    {
        помоћу (вар досег = _сервицеПровидер.ЦреатеСцопе ())
        {
            вар такСтратеги = (ИТакСтратеги) обим.СервицеПровидер
              .ГетРекуиредСервице (такСтратегиСервицеТипе);
            вар цена = производ.Цена * број;
            повратна цена + порезСтратеги.ЦалцулатеТак (цена);
        }
    }
}

ПрицеЦалцулатор убризгава ИСервицеПровидер у свој конструктор и додељује га пољу. ПрицеЦалцулатор затим га користи унутар методе израчуна да би створио опсег дечије услуге. Користи ранге.СервицеПровидер за решавање услуга, уместо убризгане инстанце _сервицеПровидер. Стога се све услуге које се рјешавају из опсега аутоматски пуштају / одлажу на крају употребне изјаве.

Добре праксе:

  • Ако решавате услугу у тијелу метода, увек креирајте домет дечије услуге како бисте осигурали да се решене услуге правилно пуштају.
  • Ако метода добије ИСервицеПровидер као аргумент, можете директно решити услуге из ње без бриге о пуштању / одлагању. Стварање / управљање опсегом услуге одговорно је за код који позива вашу методу. Слиједећи овај принцип ваш код је чистији.
  • Не држите референцу на решену услугу! У супротном може проузроковати пропуштање меморије и приступићете распоређеној услузи када касније користите референцу објекта (осим ако је решена услуга једнократна).

Синглетон Сервицес

Синглетон услуге су углавном дизајниране тако да одржавају стање апликације. Предмеморија је добар пример стања апликација. Пример:

ФилеСервице јавне класе
{
    приватно само за читање ЦонцуррентДицтионари <стринг, бите []> _цацхе;
    јавна ФилеСервице ()
    {
        _цацхе = нови ЦонцуррентДицтионари <стринг, бите []> ();
    }
    јавни бајт [] ГетФилеЦонтент (стринг филеПатх)
    {
        ретурн _цацхе.ГетОрАдд (филеПатх, _ =>
        {
            врати Филе.РеадАллБитес (филеПатх);
        });
    }
}

ФилеСервице једноставно кешира садржај датотеке да би смањио читање диска. Ова услуга треба да буде регистрована као синглетон. У супротном, кеширање неће радити како се очекује.

Добре праксе:

  • Ако сервис има државу, требао би му приступити на сигуран начин. Зато што сви захтеви истовремено користе исту инстанцу услуге. За сигурност навоја користио сам ЦонцуррентДицтионари уместо Дицтионари.
  • Не користите опсежне или привремене услуге од синглетон услуга. Јер, пролазне услуге можда нису дизајниране тако да буду сигурне за нит. Ако их морате користити, водите рачуна о вишеструком навоју док користите ове услуге (на пример, користите закључавање).
  • Пропуштање меморије углавном узрокују синглетон услуге. Они се не пуштају / уклањају до краја пријаве. Дакле, ако инстанцирају класе (или их убацују), али их не ослободе / одложе, они ће такође остати у меморији до краја апликације. Обавезно их пустите / одложите у право време. Погледајте Ресолвинг Сервицес у одељку Боди Метход (Поглавље 2).
  • Ако кеширате податке (садржај датотеке у овом примеру), требали бисте креирати механизам за ажурирање / поништавање кешираних података када се промијени изворни извор података (када се кеширана датотека промијени на диску за овај примјер).

Опсег услуга

Прво се чини добар кандидат за складиштење података на веб захтевима. Зато што АСП.НЕТ Цоре креира опсег услуга по веб захтеву. Дакле, ако региструјете услугу по опсегу, она се може делити током веб захтева. Пример:

јавна класа РекуестИтемсСервице
{
    приватни речник за читање само <стринг, објецт> _итемс;
    јавна РекуестИтемсСервице ()
    {
        _итемс = нови речник <стринг, објецт> ();
    }
    јавна воид сет (назив низа, вредност објекта)
    {
        _итемс [име] = вредност;
    }
    јавни објект Гет (назив низа)
    {
        ретурн _итемс [име];
    }
}

Ако региструјете РекуестИтемсСервице као опсег и убризгавате га у две различите услуге, тада можете добити ставку која је додата из друге услуге, јер ће делити исту инстанцу РекуестИтемсСервице. То очекујемо од опсежних услуга.

Али .. чињеница можда није увек таква. Ако креирате опсег дечије услуге и решите РекуестИтемсСервице из дечијег обима, добићете нову инстанцу РекуестИтемсСервице и она неће радити онако како очекујете. Дакле, опсег услуга не значи увек инстанцу по захтеву веба.

Можда ћете помислити да не направите тако очигледну грешку (решавање опсега унутар дечијег досега). Али, ово није грешка (врло редовна употреба) и случај можда није тако једноставан. Ако постоји велики графикон зависности између ваших услуга, не можете знати да ли је неко створио дечији домет и разрешио услугу која убризгава другу услугу ... која коначно убризгава опсег услуге.

Добра пракса:

  • Опсег услуга може се сматрати оптимизацијом у коју га убацује превише услуга у веб захтеву. Дакле, све ове услуге користиће једну инстанцу услуге током истог веб захтева.
  • Услуге опсега не морају бити дизајниране као сигурне за нити. Јер, требало би их нормално користити једним веб захтевом / нити. Али ... у том случају, не би требало да делите опсег услуга између различитих нити!
  • Будите пажљиви ако дизајнирате опсег услуге за размјену података између осталих сервиса у веб захтјеву (објашњено горе). Подаци по веб захтевима можете да похраните у ХттпЦонтект (убаците ИХттпЦонтектАццессор да бисте му приступили) што је најсигурнији начин да се то уради. Животни век ХттпЦонтект није обухваћен. Заправо, он уопште није регистрован за ДИ (зато га не убризгавате, већ уместо њега убризгавате ИХттпЦонтектАццессор). Имплементација ХттпЦонтектАццессор користи АсинцЛоцал за дељење истог ХттпЦонтект током веб захтева.

Закључак

Убризгавање зависности у почетку се чини једноставним, али постоје потенцијални проблеми са више навоја и пропуштањем меморије ако не следите неке строге принципе. Поделио сам неке добре принципе засноване на сопственим искуствима током развоја АСП.НЕТ оквира бојлера.