看看“
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原则.所以他们必须集中精力和狭隘(最好只有一个成员).