Prism 事件聚合 - 订阅者未触发

发布于 2024-08-12 10:46:04 字数 1518 浏览 3 评论 0原文

我正在努力使用 Prism 实现事件聚合。我有几个模块,我希望每个模块都订阅事件,告诉它们何时被请求。我开始做一个简单的例子,在 shell 中包含订阅者和发布者。那里没有问题。现在;当我将订阅者移至我的模块时,他们不会被触发。更奇怪的是,它实际上已经工作了几次——所有这些我都在断点处等待。所以在我看来这是某种竞争条件,但我不明白为什么。

假设:我不需要在任何地方设置 IEventAggregator - 例如在 IoC 容器中注册?这是内置于 Prism 中的,因此我只有一个事件聚合器实例,对吗?

所以,问题基本上是我应该如何/在哪里/何时设置我的订阅者。东西等有具体的顺序吗?在我的简化示例中,我有一个模块 MyModule。 Bootstrapper 会将 MyModule 添加到目录中 - 使其初始化:

catalog.AddModule(typeof(MyModule));

MyModule 将存储聚合器并使用它来订阅 MyModuleRequestedEvent。它还使用菜单注册表在应用程序菜单中注册。这个想法是,最终单击菜单应该触发该事件 - 通知 MyModule 它已被请求。然后我希望 MyModule 负责弄清楚下一步该做什么。

public MyModule(IEventAggregator aggregator, IApplicationMenuRegistry menu)
{
    _applicationMenu = menu;
    _aggregator = aggregator;
}

public void Initialize()
{
    var evnt = _aggregator.GetEvent<MyModuleRequestedEvent>();
    evnt.Subscribe(MyModuleRequested);
    _applicationMenu.RegisterMenuItem("MyModule", evnt);
}

public void MyModuleRequested(bool b)
{
    MessageBox.Show("MyModule requested");
}

现在,我的 shell 中有一个按钮可以发布此事件。解析后,shell 会获得相同的(?)事件聚合器。

public Shell(IEventAggregator aggregator)
{
    InitializeComponent();
    var evnt = aggregator.GetEvent<MyModuleRequestedEvent>();
    EventTriggerButton.Click += (s, e) => evnt.Publish(true);
}

注:

  • 已验证该事件已发布。在 shell 中添加订阅者也会使该订阅者收到事件。
  • 再次; MyModule 中的订阅者不会被触发。然而,奇怪的是,这种情况出现过几次。
  • 我不使用事件的输入。看起来你需要一些输入类型,所以我只是使用了一个虚拟布尔值。我可以摆脱这个吗..?

I'm working on implementing an event aggregation with Prism. I have a few modules, and I want each of them to subscribe to events that tells them when they are requested. I started out doing an all plain example with both subscribed and publisher in the shell. No problems there. Now; when I move the subscribers out to my modules they don't get triggered. What's even more odd is that it actually has worked a few times - all of which I've been pending in a breakpoint. So it seems to me to be some race condition, but I don't understand why.

Assumption made: I don't need to set up the IEventAggregator anywhere - e.g. registering in the IoC container? This is built into Prism such that I only have one instance of the event aggregator, right?

So, the question is basically how/where/when I should set up my subscribers. Is there a specific order on stuff etc? In my simplified example I have one module MyModule. The Bootstrapper will add MyModule to the catalog - making it initialized:

catalog.AddModule(typeof(MyModule));

MyModule will store the aggregator and use this for subscribing to the MyModuleRequestedEvent. It also uses a menu registry to register in the application menu. The idea is that eventually clicking in the menu should trigger the event - notifying MyModule that it has been requested. Then I want it to be MyModule's responsibility to figure out what to do further.

public MyModule(IEventAggregator aggregator, IApplicationMenuRegistry menu)
{
    _applicationMenu = menu;
    _aggregator = aggregator;
}

public void Initialize()
{
    var evnt = _aggregator.GetEvent<MyModuleRequestedEvent>();
    evnt.Subscribe(MyModuleRequested);
    _applicationMenu.RegisterMenuItem("MyModule", evnt);
}

public void MyModuleRequested(bool b)
{
    MessageBox.Show("MyModule requested");
}

Now, I have a button in my shell which will publish this event. The shell gets the same (?) event aggregator when resolved.

public Shell(IEventAggregator aggregator)
{
    InitializeComponent();
    var evnt = aggregator.GetEvent<MyModuleRequestedEvent>();
    EventTriggerButton.Click += (s, e) => evnt.Publish(true);
}

Notes:

  • Have verified that the event is published. Adding a subscriber in the shell too will make this subscriber receive the event.
  • Again; the subscriber in MyModule isn't triggered. However, it has - strangely - been on a few occasions.
  • I don't use the input to the event. It seemed like you needed to have some input-type, so I just went with a dummy bool. Can I get rid of this..?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

落花浅忆 2024-08-19 10:46:04

Prism 事件聚合器使用弱引用来链接到事件。这是为了防止事件处理程序发生内存泄漏。

一旦模块初始化程序运行,它就会被释放,因此您的事件处理程序将在事件被触发之前被销毁。您可以告诉 Prism 通过使用 Subscribe 的重载来保留事件处理程序。

evnt.Subscribe(MyModuleRequested, true);

作为一种模式,我倾向于将任何事件订阅者放在一个单独的类中,并从模​​块的 Initialize 方法中调用该类。这样,当模块仍然被破坏时,事件保持活动状态但分离。

The Prism Event Aggregator uses Weak References to link to the events. This is to prevent memory leaks from event handlers.

Once a module initializer has been run it's disposed of, so your event handler is being destroyed before the event is being fired. You can tell Prism to keep the event handler around by using an overload of Subscribe.

evnt.Subscribe(MyModuleRequested, true);

As a pattern, I tend to put any event subscribers in a separate class, and call that class from the modules Initialize method. That way the events stay alive but separate while the module is still destroyed.

清风挽心 2024-08-19 10:46:04

所以,我只是得到了一个理论,但现在没有时间测试它......明天会做。

问题:将模块添加到 ModuleCatalogue 中会让它们保持活力吗?我以为会的。因此 - MyModule 应该保持活动状态 - 然后在事件发布时被触发。

protected override IModuleCatalog GetModuleCatalog()
{
    var catalog = new ModuleCatalog();
    catalog.AddModule(typeof(MyModule));
    return catalog;
}

但是,如果这不能使模块保持活动状态,那么显然它将很难响应该事件。模块对象死亡,但它不会取消订阅 - 因此我将在 EventAggregator 列表中看到订阅者,但订阅者不再存在。还;我提到过,它实际上偶尔会起作用 - 如果垃圾收集器在事件触发之前没有时间清除垃圾,就会出现这种情况。

听起来是这样吗?如果是这样 - 我还没有想到解决方案,所以欢迎您在不同的回复线程中提出一个解决方案

。 ModuleCatalogue 到底是什么?只是为初始化保留一个列表然后扔掉?

So, I just got a theory, but no time to test it right now.. Will do tomorrow.

Question: Will adding modules to the ModuleCatalogue keep them alive? I assumed it would. Hence - the MyModule should stay alive - and then will be triggered when the event is published.

protected override IModuleCatalog GetModuleCatalog()
{
    var catalog = new ModuleCatalog();
    catalog.AddModule(typeof(MyModule));
    return catalog;
}

However, if this doesn't keep the module alive it is obvious that it will have a hard time respond to the event. The module-object dies, but it doesn't unsubscribe - hence I'll see the subscriber in the EventAggregator list, but the subscriber isn't around anymore. Also; I mentioned that I it does in fact work occasionally - which would be the case if the garbage collector didn't have time to take out the trash before the event is triggered.

Does this sound like the case? If so - I haven't thought of a solution yet so you're welcome to suggest one in a different reply-thread..

So; what is the ModuleCataloge anyway? Just a list kept for Initialization and then thrown away?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文