2013-06-18 21 views
5

Używam zamku Windsor już od jakiegoś czasu. Jest idealny do środowisk, w których dane znajdują się w bazie danych lub tym podobnych, gdzie wzór repozytorium lub wzorzec unitofwork działają dobrze.Zamek Windsor/DI i modele obiektów

Teraz mam inną sytuację: mam złożony model obiektu, który jest składany przez wiele pojedynczych PONO. Środowisko jest pod silnym wpływem COM, aby było bardziej wyraźne: Excel, Word PIO są mocno wykorzystywane.

Używam wzorca polecenia, zaimplementowałem ICommandHandler jak opisano here, ale z jedną różnicą. Ponieważ chcę składać polecenia do listy poleceń, aby wywoływać je w biegu, nie wiedząc nic oprócz opisanego ogólnego wzorca poleceń, nie wprowadza on kontekstu podczas wywoływania metody execute. Interfejs wygląda następująco:

public interface ICommand 
    { 
     void Execute(); 
     bool CanExecute(); 
    } 

Wykonywanie poleceń za pomocą tego interfejsu jest skuteczne i łatwe do zrozumienia. Z drugiej strony problemem jest wprowadzenie kontekstu z ctor, ponieważ dlatego pojemnik musi być wyraźnie nazwany na przykład dodaj parametry ctor.

Więc faktycznie mają dwa pytania:

  1. Czy to możliwe, aby wstrzyknąć - nazwijmy to kontekst, część modelu obiektowego - automatycznie przez zamku Windsor bez wywoływania pojemnik explictely?
  2. Jak wziąć udział w strukturze poleceń za pomocą DI? Wszelkie pomysły, jak osiągnąć możliwość definiowania listy zadań/działań lub tym podobnych, zgodnie z regułą RRR opisaną jako here?
+0

Łącze do opisu wzorca komend/obsługi (używając 'ICommandHandler '), ale w rzeczywistości używasz wzorca polecenia, który jest zupełnie inny, ponieważ polecenia w komendzie/wzorze obsługi są DTO bez zachowania, i nie zawierają żadnej metody "Execute". Posiadanie na nich metody 'Execute' wyłącza większość możliwości, jakie przynosi wzór poleceń/obsługi. Problemy z wstrzykiwaniem zależności do poleceń znikną, gdy wstrzykniesz zależności do procedur obsługi. W komendzie/procedurze obsługi komendy nie mają żadnych zależności (ponieważ nie mają żadnego zachowania). – Steven

+0

Dzięki za odpowiedź, Steven. Na pewno masz rację, mogę wprowadzić je do obsługi. Ale to nie rozwiązuje problemu, ponieważ wciąż muszę wstrzykiwać te same informacje i wywoływać kontener, aby moje dane były dostępne dla handler'a. Nie byłoby możliwe wykonywanie poleceń wsadowych i wykonywanie ich w wierszu. Czy coś złego? –

Odpowiedz

4

Infrastruktura:

public interface ICommandHandler<in T> 
{ 
    void Handle(T command); 
} 

public interface ICommandExecutor 
{ 
    CommandResult ExecuteCommand(Command command); 
    CommandResult ExecuteCommands(Command[] commands); 
} 

public abstract class Command 
{ 

} 

public class CommandExecutor : ICommandExecutor 
{ 
    private readonly IWindsorContainer _kernel; 

    public CommandExecutor(IWindsorContainer kernel) 
    { 
     Guard.AssertNotNull(() => kernel); 
     _kernel = kernel; 
    } 

    public CommandResult ExecuteCommand(Command command) 
    { 
     return ExecuteInternal(command); 
    } 

    public CommandResult ExecuteCommands(Command[] commands) 
    { 
     CommandResult result = null; 

     foreach (Command command in commands) 
     { 
      result = ExecuteInternal(command); 

      if (!result.IsExecuted) 
       return result; 
     } 

     return result ?? CommandResult.Executed("Command executed successfully"); 
    } 

    private CommandResult ExecuteInternal(Command command) 
    { 
     dynamic handler = FindHandlerForCommand(command); 

     try 
     { 
      handler.Handle(command as dynamic); 
      return CommandResult.Executed("Command executed successfully"); 
     } 
     finally 
     { 
      _kernel.Release(handler); 
     } 
    } 

    private object FindHandlerForCommand(Command command) 
    { 
     Type handlerType = typeof (ICommandHandler<>).MakeGenericType(command.GetType()); 
     dynamic handler = _kernel.Resolve(handlerType); 
     return handler; 
    } 
} 

Rejestracja:

 container.Register(Component.For<ICommandExecutor>().ImplementedBy<CommandExecutor>() 
      .Interceptors<ExceptionToCommandResult>() 
      .Interceptors<ExceptionLogger>() 
      .Interceptors<HandleWhenDeadlockVictim>() 
      .Interceptors<RetryCommand>() 
      .Interceptors<ContainerScopeWrapper>() 
      .Interceptors<TransactionWrapper>() 
      .Interceptors<SameNhibernateSessionAndTransactionWrapper>()); 

Przykład:

public class WriteComment : Command 
{ 
    public string GameToCommentId { get; set; } 

    public string Comment { get; set; } 
} 

public class WriteCommentCommandHandler : ICommandHandler<WriteComment> 
{ 
    private readonly IGameRepository _repository; 

    public WriteCommentCommandHandler(IGameRepository repository) 
    { 
     Guard.AssertNotNull(() => repository); 
     _repository = repository; 
    } 

    public void Handle(WriteComment command) 
    { 
     var game = _repository.Get(new Guid(command.GameToCommentId)); 

     game.WriteComment(command.Comment, DateTime.Now); 
    } 
} 

Wszystko to AOP rzeczy obsługuje transakcje i tak dalej. Executor komend jest bezstanowym singletonem, operatorzy określają swoje różne potrzeby. Executor poleceń jest tylko infrastrukturą i agnostykiem domeny, więc umieść go tam, gdzie chcesz poza domeną. To jest kod produkcyjny na dużym systemie, działa jak urok.

+0

Marius, wielkie dzięki. Całkiem wyczerpujące. Spróbuję! –