ASP.NET MVC и IoC - внедрение цепочки - PullRequest
1 голос
/ 09 марта 2011

Пожалуйста, будьте осторожны, я новичок в этом IoC / MVC, но я пытаюсь. Я понимаю значение DI для целей тестирования и то, как IoC разрешает зависимости во время выполнения, и я рассмотрел несколько примеров, которые имеют смысл для ваших стандартных операций CRUD ...

Я начинаю новый проект и не могу придумать, как получить права пользователя. Мой сайт в основном защищен любыми страницами с функциями (кроме регистрации, часто задаваемых вопросов, о нас и т. Д.) За входом в систему. У меня есть пользовательское удостоверение, которое имеет несколько дополнительных свойств, которые контролируют доступ к данным ... Итак ....

Используя Ninject, я привязал конкретный тип * к методу (Bind<MyIdentity>().ToMethod(c => MyIdentity.GetIdentity());, поэтому, когда я добавляю MyIdentity в конструктор, он внедряется на основе результатов вызова метода.

Это все работает хорошо. Уместно ли (из метода GetIdentity()) напрямую запрашивать объект куки-файлов запроса (через FormsAuthentication)? При тестировании контроллеров я могу передать идентификацию, но метод GetIdentity() будет по существу непроверенным ...

Кроме того, в методе GetIdentity () я буду запрашивать базу данных. Должен ли я вручную создать конкретный экземпляр хранилища?

Или все вместе лучше?

Ответы [ 2 ]

4 голосов
/ 09 марта 2011

Я думаю, что вы на правильном пути, поскольку вы абстрагировали связь с базой данных и зависимости ASP.NET от своих модульных тестов. Не волнуйтесь, что вы не можете проверить все в своих тестах. В вашем приложении всегда будут строки кода, которые невозможно проверить. GetIdentity является хорошим примером. Где-то в вашем приложении вам нужно взаимодействовать с API, специфичным для фреймворка, и этот код не может быть охвачен вашими юнит-тестами.

Хотя, возможно, еще есть возможности для улучшения. Хотя непроверенный GetIdentity не является проблемой, тот факт, что он на самом деле вызывается приложением. Он просто висит там, ожидая, что кто-то случайно позвонит. Так почему бы не абстрагироваться от создания идентичностей. Например, создайте абстрактную фабрику, которая знает, как получить правильную идентичность для текущего контекста. Вы можете внедрить эту фабрику, вместо того, чтобы вводить саму личность. Это позволяет вам иметь реализацию, определенную около корня композиции приложения и вне зоны досягаемости остальной части приложения. Кроме того, код более четко передает происходящее. Никто не должен спрашивать «какую личность я на самом деле получаю?», Потому что это будет понятно по методу фабрики, которую они вызывают.

Вот пример:

public interface IIdentityProvider
{
    // Bit verbose, but veeeery clear,
    // but pick another name if you like,
    MyIdentity GetIdentityForCurrentUser();
}

В вашем корне композиции вы можете реализовать это:

private sealed class AspNetIdentityProvider : IIdentityProvider
{
    public MyIdentity GetIdentityForCurrentUser()
    {
       // here the code of the MyIdentity.GetIdentity() method.
    }
}

В качестве хитрости я иногда заставляю свои тестовые объекты реализовывать как фабрику, так и продукт, просто для удобства во время модульного тестирования. Например:

private sealed class FakeMyIdentity
    : FakeMyIdentity, IIdentityProvider
{
    public MyIdentity GetIdentityForCurrentUser()
    {
       // just returning itself.
       return this;
    }
}

Таким образом, вы можете просто добавить FakeMyIdentity в конструктор, который ожидает IIdentityProvider. Я обнаружил, что это не жертвует удобочитаемостью тестов (что важно). Конечно, вы хотите иметь как можно меньше кода в AspNetIdentityProvider, потому что вы не можете его протестировать (автоматически). Также убедитесь, что ваш класс MyIdentity не имеет никакой зависимости от каких-либо специфичных для фреймворка частей. Если это так, то вам также необходимо это абстрагировать.

Надеюсь, это имеет смысл.

1 голос
/ 10 марта 2011

Есть две вещи, которые я бы сделал здесь по-другому ...

  1. Я бы использовал пользовательский объект IPrincipal со всеми свойствами, необходимыми для ваших нужд аутентификации. Затем я бы использовал это в сочетании с созданием пользовательских файлов cookie и событием AuthenticateRequest, чтобы избежать обращений к базе данных при каждом запросе.

  2. Если бы мой IPrincipal / Identity требовался внутри другого класса, я бы передал его как параметр метода, а не как зависимость от класса, в котором он находится сам.
    Когда я иду по этому маршруту, я использую пользовательские привязки моделей, чтобы они были параметрами моих действий, а не волшебным образом появлялись в моих методах действий.
    ПРИМЕЧАНИЕ: я так поступаю, так что возьмите с собой кусочек соли.

Извините, это, вероятно, вызывает больше вопросов, чем ответов. Не стесняйтесь задавать больше вопросов о моем подходе.

...