NHibernate: как внедрить зависимость от сущности - PullRequest
9 голосов
/ 06 ноября 2011

NHibernate 3.2 / Fluent NHibernate 1.3 / StructureMap 2.6.3 -

Пытаясь следовать DDD как архитектурной стратегии, я обычно не зависим от доменных сущностей. Однако сейчас я экспериментирую с добавлением большего поведения в мои доменные сущности, чтобы они не были настолько анемичными. Все шло хорошо, пока я не подключил NHibernate. У меня есть две проблемы:

  1. NH требует конструктора без параметров, и я бы предпочел не иметь ctor, который не должен использоваться.
  2. Когда NH пытается создать экземпляр моей сущности, он должен разрешить мою зависимости, но я не дал NH ничего, с чем он может сделать что.

Я читал в Интернете, но большинство (если не все) примеров, которые я нашел, устарели (или просто старые). Хотя лагерь NH, вероятно, не одобряет то, что я делаю, я ищу способ сделать это.

Ответы [ 3 ]

4 голосов
/ 15 ноября 2011

Решение закончилось реализацией IInterceptor NHibernate. На самом деле это очень простая реализация, когда вы наследуете от EmptyInterceptor и переопределяете JUST методы Instantiate () и SetSession (). Вот мой перехватчик, использующий StructureMap:

public class DependencyInjectionEntityInterceptor : EmptyInterceptor
{
    IContainer _container;
    ISession _session;

    public DependencyInjectionEntityInterceptor(IContainer container)
    {
        _container = container;            
    }

    public override void SetSession(ISession session)
    {
       _session = session;            
    }

    public override object Instantiate(string clazz, EntityMode entityMode, object id)
    {
        if (entityMode == EntityMode.Poco)
        {
            var type = Assembly.GetAssembly(typeof (SomeClass)).GetTypes().FirstOrDefault(x => x.FullName == clazz);
            var hasParameters = type.GetConstructors().Any(x => x.GetParameters().Any());
            if (type != null && hasParameters)
            {
                var instance = _container.GetInstance(type);

                var md = _session.SessionFactory.GetClassMetadata(clazz);
                md.SetIdentifier(instance, id, entityMode);
                return instance;
            }
        }
        return base.Instantiate(clazz, entityMode, id);
    }
}

Затем все, что вам нужно сделать, это указать NHibernate использовать ваш перехватчик:

public FluentConfiguration GetFluentConfiguration(IContainer container)
{
    return Fluently.Configure()
        .Database(MsSqlConfiguration.MsSql2008
                  .ConnectionString(c => c.FromConnectionStringWithKey("Database"))
                      .ShowSql())
        .Mappings(m => 
            m.AutoMappings.Add(AutoMap.AssemblyOf<SomeClass>()))
        .ExposeConfiguration(x => 
            x.SetInterceptor(new DependencyInjectionEntityInterceptor(container)));                
}

Когда я исследовал это, некоторые предлагали передать SessionFactory в ctor класса перехватчика. Честно говоря, с точки зрения управления сессиями, такой подход был бы лучше.

1 голос
/ 06 ноября 2011

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

Теперь вы спросите себя, как вы получаете зависимость.Для этого вы можете использовать CommandHandlers и Commands.Обработчик команды принимает зависимость в своем конструкторе и вызывает метод объекта.В пользовательском интерфейсе вы создаете командное сообщение и отправляете его командному процессору, который отвечает за вызов правильного обработчика команд.

Я надеюсь, что мое объяснение для вас приемлемо.

Домен:

public class Employee
{
    public int Id { get; set; }
    public string Name { get; set; }

    public void SendNotification(string message, INotifier notifier)
    {
        notifier.SendMessage(string.Format("Message for customer '{0}' ({1}): {2}", Name, Id, message));
    }
}

Компонент инфраструктуры INotifier передается через метод, а не через конструктор!

Инфраструктура:

public interface INotifier
{
    void SendMessage(string message);
}

class EmailNotifier : INotifier
{
    public void SendMessage(string message)
    {
        // SmtpClient...
    }
}

class SMSNotifier : INotifier
{
    public void SendMessage(string message)
    {
        // SMS ...
    }
}

Command и CommandHandler:

public class NotificationCommandHandler : ICommandHandler<NotificationCommand>
{
    private readonly INotifier _notifier;

    public NotificationCommandHandler(INotifier notifier)
    {
        _notifier = notifier;
    }

    public void Execute(NotificationCommand commandMessage)
    {
        commandMessage.Employee.SendNotification(commandMessage.Message, _notifier);
    }
}

public class NotificationCommand
{
    public string Message { get; set; }
    public Employee Employee { get; set; }
}

CommandHandler получает INotifier посредством внедрения конструктора.Таким образом, вам не нужно использовать ваш IoC-контейнер как ServiceLocator.

Использование, т. Е. В пользовательском интерфейсе контроллера:

public class Controller
{
    private readonly IMessageProcessor _messageProcessor;

    public Controller(IMessageProcessor messageProcessor)
    {
        _messageProcessor = messageProcessor;
    }

    public void SendNotification (Employee employee, string message)
    {
        var sendMailCommand = new NotificationCommand
        {
            Employee = employee,
            Message = message
        };

        _messageProcessor.Process(sendMailCommand);
    }
}

Если у вас есть вопросы о командном процессоре, посмотрите проект mvccontrib или задайте отдельный вопрос..

0 голосов
/ 06 ноября 2011

Извините, мой предыдущий ответ не касался конкретного вопроса. Я провел еще какое-то исследование, и, похоже, мне еще многое предстоит узнать о том, когда и когда не следует использовать модель анемичной области. Что касается вашего вопроса, я обнаружил, эта статья очень по теме. Это на Java, а не на C #, но принципы те же. Надеюсь, это поможет.

...