c# – 对于WPF窗口中介服务,使用Simple Injector按键解析实例的替代方法是什么?

看看“
Resolve instances by key”部分

在Simple Injector网站上,我使用了建议的IRequestHandlerFactory实现来改进下面的代码,但注意事项如下:

Note: The need for keyed registration can be an indication of ambiguity in the application design and a sign of a Liskov Substitution Principle violation. Take a good look if each keyed registration shouldn’t have its own unique interface, or perhaps each registration should implement its own version of a generic interface.

Note: Please remember the previous note about ambiguity in the application design. In the given example the design would probably be better of by using a generic IRequestHandler<TRequest> interface. This would allow the implementations to be batch registered using a single line of code, saves you from using keys, and results in a configuration the is verifiable by the container.

让我很好奇

问:注释(IRequestHandler< TRequest>)中的实际实现如何查找我的情况?我花了一段时间试图找出它可能是什么但不能
提出一种有效的方法.

我现在有什么

为了解耦视图模型,我目前有一个视图和对话框创建器,它通过MVVMLight的Messenger监听消息并基于这些消息将
创建所需的Windows或对话框.

我注入了一个IWindowResolver,它是一个工厂(虽然更多的服务定位器),它只是获取请求的Window类型
在组合根上使用Simple Injector工厂模式.

我喜欢RegisterViewHandler,它很清楚,在一个地方它将Message和相关的窗口和消息处理程序拼接在一起
而不是在代码中展开(例如HandleEmailPopupMessage不需要知道要获得的窗口的确切类型和模式
HandleEmailPopupMessage使用的也可以是通用的,用于简单的窗口创建和消息发送.但是,我相信IWindowResolver
可能不是一个定位器,可能会更多地将一些注册推送到组合根.

次要问题 – 是某种IRequestHandler< TRequest>更简单/更强大/更有用,或者简单地将Dictionary推向工厂级别,就像Simple Injector doco中的其他示例一样,是否足够整理?

// Current IWindowResolver
interface IWindowResolver
{
   Window CreateWindow<TWindow>(TWindow windowType) where TWindow : class;
}

// Current Simple Injector IWindowResolver implementation
[UsedImplicitly]
private sealed class SimpleInjectorWindowFactory : IWindowFactory
{
   private readonly Container _container;

   public SimpleInjectorWindowFactory(Container container)
   {
      _container = container;
   }

   public Window CreateWindow<TWindow>(TWindow windowType) where TWindow : class
   {
      return _container.GetInstance<TWindow>() as Window;
   }
}

public class ShowEmailPopupFormMessage
{
   public ShowEmailPopupFormMessage()
   {
      Params = new ParamsMessage();
   }

   public class ParamsMessage
   {
      public string CustomerName { get; set; }
      public string EmailTo { get; set; }
   }

   public ParamsMessage Params { get; set; }
}

// Current ViewConstructor
class ViewConstructor
{
   IWindowResolver _windowResolver;
   Dictionary<Type, Type> _viewMap = new Dictionary<Type, Type>(); // Maps a message type to a particular window/view type

   public ViewConstructor(IWindowResolver windowResolver)
   {
      _windowResolver = windowResolver;
      RegisterViewHandler<ShowEmailPopupFormMessage, EmailPopupWindow>(HandleEmailPopupMessage);
   }

   private void RegisterViewHandler<TMessage, TWindow>(Action<TMessage> messageAction)
      where TMessage : class
      where TWindow : Window
   {
      if (_viewMap.ContainsKey(typeof(TMessage)))
      {
         throw new ArgumentException("View already registered");
      }

      // Store the map of Message type to Window type
      _viewMap[typeof(TMessage)] = typeof(TWindow);

      // Register with the message handler
      Messenger.Default.Register(this, messageAction);
   }

   private void HandleEmailPopupMessage(ShowEmailPopupFormMessage msg)
   {
      var frm = GetMappedWindow(msg.GetType());

      // We know that the View and it's associated ViewModel are now created
      // so we can send some initialization parameters to the view and or ViewModel
      Messenger.Send(msg.Params);

      frm.ShowDialog();
   }

   private Window GetMappedWindow<TMessage>(TMessage messageType)
   {
      var windowType = _viewMap[typeof(TMessage)];

      var frm = _windowResolver.CreateWindow(windowType);

      if (frm == null)
      {
         throw new ApplicationException("Window is not of the specified Type!");
      }

      // Hookup common events such as cleanup events
      frm.Unloaded += FormOnUnloaded;

      return frm;
   }

   private static void FormOnUnloaded(object sender, RoutedEventArgs eArgs)
   {
      var frm = sender as Window;

      if (frm == null)
      {
         return;
      }

      // Cleanup the ViewModel 
      var dataContext = frm.DataContext as ICleanup;

      if (dataContext != null)
      {
         dataContext.Cleanup();
      }
   }
}

public class EmailPopupWindow : Window
{
   // Window knows how to set it's datacontext's ViewModel (in this case EmailPopupVm) using the ViewModelLocator declared in XML.
   // The window does not handle any messages.
}

// The View Model for the EmailPopupWindow. ViewModelBase is from MVVMLight
public class EmailPopupVm : ViewModelBase
{
   public EmailPopupVm()
   {
      Messenger.Register<ShowEmailPopupFormMessage.ParamsMessage>(HandleParamsMessage);
   }

   private void HandleParamsMessage(ShowEmailPopupFormMessage.ParamsMessage msg)
   {
      // Initialize the ViewModel with the parameters
      this.CustomerName = msg.CustomerName;
      this.EmailTo = msg.EmailTo;
   }
}

更新

为清楚起见,ViewModel(现在添加到上面的示例代码中)实际上处理了ShowEmailPopupFormMessage.ParamsMessage.窗口无视任何消息.

最佳答案 总是很难深入了解特定设计的细节,并在该应用程序的上下文中提炼出实际上正确的内容,因为通常缺少许多细节.但无论如何我都会试一试,所以我提前道歉,如果我有点偏离这里.

在我看来,你错过了可以处理消息的类的抽象;我们称它为IMessageHandler< TMessage>:

public interface IMessageHandler<TMessage>
{
    void Handle(TMessage message);
}

接下来,似乎窗口也链接到某些消息.所以我会为windows定义一个通用接口,例如:

public interface IWindow<TMessage>
{
    void ShowDialog();
}

接下来,您可能需要一些允许您发送和发送消息的抽象(虽然MVVMLight可能已经提供给您,但我并不熟悉它):

public interface IMessageDispatcher
{
    void Dispatch(object message);
}

基于IMessageHandler< TMessage>抽象,我们现在可以创建一个可以处理ShowEmailPopupFormMessage的处理程序:

public class ShowEmailPopupFormMessageHandler : IMessageHandler<ShowEmailPopupFormMessage>
{
    private readonly IWindow<ShowEmailPopupFormMessage> frm;
    public ShowEmailPopupFormMessageHandler(IWindow<ShowEmailPopupFormMessage> frm) {
        this.frm = frm;
    }

    public void Handle(ShowEmailPopupFormMessage message) {
        Messenger.Send(msg.Params);
        frm.ShowDialog();
    }
}

您的EmailPopupWindow会处理ShowEmailPopupFormMessage消息,我们应该让它实现IWindow< ShowEmailPopupFormMessage>:

public class EmailPopupWindow : Window, IWindow<ShowEmailPopupFormMessage>
{
    // window stuff here
}

现在剩下的就是在组合根中连接所有内容:

// Composition Root
container.Register(typeof(IWindow<>), applicationAssemblies);
container.Register(typeof(IMessageHandler<>), applicationAssemblies);
container.RegisterSingleton<IMessageDispatcher>(
    new SimpleInjectorMessageDispatcher(container));
container.RegisterInitializer<Window>(frm => {
    frm.Unloaded += FormOnUnloaded;
});

注意,IWindow< T>都是和IMessageHandler< T>使用批量注册连接实现.另请注意,Window.Unload事件的注册是在组合根目录中完成的.

SimpleInjectorMessageDispatcher也是组合根的一部分:

private sealed class SimpleInjectorMessageDispatcher : IMessageDispatcher
{
    private readonly Container container;
    public SimpleInjectorMessageDispatcher(Container container) {
        this.container = container;
    }

    public void Dispatch(object message) {
        Type handlerType = typeof(IMessageHandler<>).MakeGenericType(message.GetType());
        dynamic handler = this.container.GetInstance(handlerType);
        handler.Handle((dynamic)message);
    }
}

就像我说的那样,我可能会稍微偏离一点(或者一英里),但是希望能给你一些关于如何使用泛型键入来解决这个问题的想法.通用类型的优势在于它为您提供了一致的设计,允许在类型的定义中刻录元数据,简化应用横切关注点(使用装饰器),并允许轻松注册.但同样的约束适用于通用抽象;他们必须遵循SOLID原则.所以他们必须集中精力和狭隘(最好只有一个成员).

点赞