Обработка события домена в asp.net контроллер mvc

#asp.net-mvc #domain-driven-design #domain-events

#asp.net-mvc #дизайн, управляемый доменом #домен-события

Вопрос:

Я изучаю возможность использования событий домена для извлечения информации из операций, происходящих глубоко внутри моей модели домена, чтобы сигнализировать об определенных событиях на уровне контроллера. К сожалению, я не смог найти пример того, как правильно подключить это к asp.net контроллер mvc.

Короче говоря, это то, что я ищу внутри моего действия:

 service.doSomethingComplicated();
var model = new ViewModel();
model.somethingComplicatedCausedSomethingElse = <true-if-my-domain-event-was-raised>;
return View(model);
  

Кто-нибудь может предложить мне какие-нибудь рекомендации?

Обновить

Просто для ясности, я понимаю, как я бы вызвал и обработал событие домена в контроллере; Я просто ищу реализацию регистрации для события, которая будет безопасна для использования в контексте, который я описал.

Ответ №1:

Основываясь на примере, на который вы ссылались, где автор делает это:

 Customer preferred = null;

DomainEvents.Register<CustomerBecamePreferred>(
    p => preferred = p.Customer
        );

c.DoSomething();
  

Вы должны быть в состоянии сделать это:

     var model = new ViewModel();

    // Register a handler that sets your bool to true if / when the event is raised
    DomainEvents.Register<YourDomainEvent>(e => model.somethingComplicatedCausedSomethingElse = true);
    // EDIT: If using the singleUseActions modification, pass the second parameter
    // DomainEvents.Register<YourDomainEvent>(e => model.somethingComplicatedCausedSomethingElse = true, true);

    // Call the service. If it raises the event, the handler you just registered will set your bool
    service.doSomethingComplicated();

    return View(model);
  

Редактировать (модификация DomainEvents)
Это непроверенное и записанное в поле редактирования StackOverflow, но именно с этого я бы начал. Я использую необязательный параметр, чтобы не изменять существующие вызовы, и отдельный список «singleUseActions», чтобы оставить существующие возможности как можно более нетронутыми. Надеюсь, это поможет.

 public static class DomainEvents
  { 
      [ThreadStatic] //so that each thread has its own callbacks
      private static List<Delegate> actions;

      [ThreadStatic] //so that each thread has its own callbacks
      private static List<Delegate> singleUseActions;

      public static IContainer Container { get; set; } //as before

      //Registers a callback for the given domain event
      public static void Register<T>(Action<T> callback, bool isSingleUse = false) where T : IDomainEvent
      {
         List<Delegate> targetList;
         if (isSingleUse)
         {
             if (singleUseActions == null) singleUseActions = new List<Delegate>();
             targetList = singleUseActions;
         }
         else
         {
             if (actions == null) actions = new List<Delegate>();
             targetList = actions;
         }

         targetList.Add(callback);
     }

     //Clears callbacks passed to Register on the current thread
     public static void ClearCallbacks ()
     {
         actions = null;
         singleUseActions = null;
     }

     //Raises the given domain event
     public static void Raise<T>(T args) where T : IDomainEvent
     {
        if (Container != null)
           foreach(var handler in Container.ResolveAll<Handles<T>>())
              handler.Handle(args);

        if (actions != null)
            foreach (var action in actions)
                if (action is Action<T>)
                    ((Action<T>)action)(args);

        if (singleUseActions != null)
            // Don't foreach because we are going to modify the collection
            for (int index = singleUseActions.Count - 1; index > -1; index--)
            {
                var singleUseAction = singleUseActions[index];
                if (singleUseAction is Action<T>)
                {
                    ((Action<T>)singleUseAction)(args);
                    singleUseActions.RemoveAt(index);
                }
            }


  }
} 
  

Комментарии:

1. Верно, но вы заметите [ThreadStatic] в списке зарегистрированных делегатов; не опасно ли это при использовании в контексте asp.net ? Я думаю, что это предназначалось для использования в модульных тестах и т. Д

2. All ThreadStatic позволяет ли это каждому потоку иметь свою собственную копию статической переменной вместо совместного использования одной и той же копии между потоками. Если doSomethingComplicated потенциально может вызвать событие из другого потока, то то, что я опубликовал, может не сработать (если вы не удалите ThreadStatic, но тогда вы можете открыть другую банку с червями). Еще одна вещь, которую вам, возможно, придется рассмотреть, — это удаление обратного вызова, как только вы закончите. «ClearCallbacks» может быть не совсем правильным, возможно, вам потребуется добавить метод Remove, чтобы быть более избирательным.

3. Если вы готовы представить улучшенную версию класса регистрации событий домена, я был бы рад пометить это как принятое.

4. Я опубликовал правку, но она сохраняет ThreadStatic и, следовательно, предполагает, что DomainEvent будет вызван из того же потока, что и тот, в котором вы регистрируете обратный вызов. Если вы, возможно, можете переключать потоки, вы бы хотели удалить ThreadStatic и добавить дополнительную логику, чтобы определить, какие обратные вызовы подходят для любого данного повышения. Надеюсь, это поможет — я повторю, что это абсолютно непроверено, поэтому действуйте с осторожностью. 🙂