Внедрение зависимостей в классы доменной модели с помощью Nhibernate (ASP.NET MVC + IOC) - PullRequest
7 голосов
/ 13 января 2010

Я создаю приложение ASP.NET MVC, которое использует подход DDD (Domain Driven Design) с доступом к базе данных, обрабатываемым NHibernate. У меня есть класс модели домена (администратор), в который я хочу внедрить зависимость через контейнер IOC, такой как Castle Windsor, что-то вроде этого:

public class Administrator
{
    public virtual int Id { get; set; }

    //.. snip ..//

    public virtual string HashedPassword { get; protected set; }

    public void SetPassword(string plainTextPassword)
    {
        IHashingService hasher = IocContainer.Resolve<IHashingService>();

        this.HashedPassword = hasher.Hash(plainTextPassword);
    }
}

В основном я хочу внедрить IHashingService для метода SetPassword без непосредственного вызова контейнера IOC (поскольку предполагается, что это анти-шаблон IOC). Но я не уверен, как это сделать. Мой объект Administrator либо создается с помощью new Administrator();, либо загружается с помощью NHibernate, так как бы я внедрил IHashingService в класс Administrator?

Если подумать, правильно ли я поступлю? Я надеялся избежать засорения моей кодовой базы ...

currentAdmin.Password = HashUtils.Hash(password, Algorithm.Sha512);

... и вместо этого заставьте саму модель домена заботиться о хешировании и аккуратно ее инкапсулировать. Я могу предположить, что другой разработчик случайно выберет неправильный алгоритм и будет иметь несколько паролей, например, Sha512, некоторые - как MD5, некоторые с одной солью, некоторые с другой солью и т.д.

currentAdmin.SetPassword(password);

... тогда это скрыло бы эти детали и решило бы проблемы, перечисленные выше, не так ли?

Ответы [ 4 ]

5 голосов
/ 13 января 2010

На SO уже есть похожие вопросы:

Внедрение зависимостей с объектами NHibernate

DI / IoC, NHibernate и помощь в их совместной работе

Вам нужно будет использовать перехватчики. Посмотрите на пост Фабио Мауло для реализации:

http://nhforge.org/blogs/nhibernate/archive/2008/12/12/entities-behavior-injection.aspx

2 голосов
/ 13 января 2010

Вам нужно запомнить , как было хешировано. Это сделано для того, чтобы в будущем вы могли хэшировать строку, чтобы проверить и проверить, является ли это их паролем, сравнив его со значением хеширования. Это означает, что вам нужно сохранить enum или другое поле в вашем объекте, которое указывает механизм хеширования, который использовался в вашей базе данных.

В противном случае, если вы измените реализацию хеширования по умолчанию, все ваши старые хешированные пароли больше не будут работать, и ваши пользователи останутся ломать голову над тем, почему их пароли больше не работают - и вы получите IHashingService интерфейс, который не обеспечивает гибкости (поскольку реализация хеширования не может быть изменена без добавления странных правил, таких как «используйте этот хеш для администраторов, созданных до 2010-01-12»), существующих без реальной причины.

С этой целью я бы добавил соответствующее поле (перечисление, строку, возвращаемую интерфейсом IHashingService, что-то) и либо NHibernate создал для меня службу хеширования через реализацию IUserType, либо используйте шаблон фабрики, где конкретные экземпляры были предоставлены фабрике контейнером IoC. Это будет сочетать инъекцию уровня метода Джарретта с решением, которое позволяет повторно гидратированным объектам находить свои реализации хеширования, не завися от контейнера IoC.

Удачи!

1 голос
/ 13 января 2010

Либо хэшируйте пароль на фасаде приложения (если вы его используете), либо предоставьте реализацию IHashingService при каждом вызове Administrator.SetPassword(..). Я думаю, что это называется двойная отправка ?!

Если вы настаиваете на решении DI-in-entity , я сделал что-то подобное с PostSharp AOP и PostSharp4Spring, объявив атрибут [Configurable] на объекте, но решение для Spring.Net. Вы можете посмотреть здесь для получения дополнительной информации. Кроме того, если вы настраиваете NHibernate из контейнера DI, вы можете попасть в рекурсию, пытаясь DI-сущность до того, как контейнер завершит настройку. Вам нужен простой статический класс с методом для подавления DI при построении сущности во время инициализации контейнера. Не могу привести пример, хотя: (

1 голос
/ 13 января 2010

Есть ли причина, по которой вы не можете передать IHashingService в конструкторе для класса Administrator? Вот так я бы разрешил зависимость.

public class Administrator
{
    private readonly IHashingService _hashingService;

    public Administrator(IHashingService hashingService)
    {
        _hashingService = hashingService;
    }

    // <snip>

    public void SetPassword(string plainTextPassword)
    {
        this.HashedPassword = _hashingService.Hash(plainTextPassword);
    }
}

Редактировать # 1

При извлечении из модели попробуйте использовать инъекцию на уровне метода.

public void SetPassword(string plainText, IHashingService hasher)
{
    if (hasher == null) throw new ArgumentNullException("hasher");
    this.HashedPassword = hasher.Hash(plainText);
}

Редактировать # 2

Кроме того, почему бы не упростить себя и просто сделать расширение для строки?

public static class ExtensionsOfString
{
    public static string Hash(this string s)
    {
        // hash with SHA256
        return hashedString;
    }
}

Несмотря на то, что я понимаю, что в использовании внедрения зависимостей есть «заменимый» аспект кода, для этого примера это не так уж важно. Вам действительно не нужен IPasswordEncryptionService так же, как вам нужен, скажем, ICreditCardAuthorizationService. Если когда-нибудь вы измените свой алгоритм хэширования с SHA256 на SHA512, вы теперь аннулируете все пароли в своей базе данных.

...