coravel icon indicating copy to clipboard operation
coravel copied to clipboard

TargetException: Non-static method requires a target when trying to broadcast an event

Open MrYossu opened this issue 2 years ago • 9 comments

I'm just trying events, and have run into a problem. I'm using Blazor server-side (.NET5 in VS2019) and have an event as follows...

  public class NewWidgetCreated : IEvent {
    public Widget Widget { get; set; }

    public NewWidgetCreated(Widget widget) =>
      Widget = widget;
  }

I have registered the event and the class that is to subscribe to it in Startup...

IServiceProvider provider = app.ApplicationServices;
IEventRegistration registration = provider.ConfigureEvents();
registration
  .Register<NewWidgetCreated>()
  .Subscribe<WidgetList>();

However, I am getting an exception trying to broadcast an event for this. I'm trying to broadcast as follows...

await _dispatcher.Broadcast(new NewWidgetCreated(_widget));

However, this throws TargetException: Non-static method requires a target.

I have checked that _dispatcher is not null, and _widget is non-null and correctly populated.

Anyone any idea why I'm getting this? Thanks

MrYossu avatar Dec 20 '21 20:12 MrYossu

What does 1WidgetList` look like?

jamesmh avatar Dec 20 '21 20:12 jamesmh

@jamesmh Sorry, didn't include it as I didn't think it was relevant. The event isn't broadcast, so the code that's supposed to pick it up wont get called. However, the .razor file looks like this...

@using CoravelPlay.Data

<ul>
  @foreach (Widget w in _widgets) {
    <li>@w.Name</li>
  }
</ul>

...and the .razor.cs file looks like this...

namespace CoravelPlay.Pages {
  public partial class WidgetList : IListener<NewWidgetCreated> {
    private readonly List<Widget> _widgets = new();

    public Task HandleAsync(NewWidgetCreated broadcasted) {
      _widgets.Add(broadcasted.Widget);
      return Task.CompletedTask;
    }
  }
}

Any ideas? Thanks

MrYossu avatar Dec 20 '21 20:12 MrYossu

Did you make sure to register the WidgetList class (or others) as a transient service? Not sure what else it might be?

jamesmh avatar Dec 22 '21 05:12 jamesmh

@jamesmh Thanks for the response. My bad, I missed the warning about adding WidgetList as a transient. That got rid of the error.

However, I've hit something else that I can't understand. Please see this sample. As far as I can see, I'm not doing anything odd here, but every time HandleAsync is called, the Widgets collection is reset, meaning it only contains the Initial widget, so I never see any widgets that were added by previous events.

I tried adding a private int, and that also gets reset to zero every time HandleAsync is called, which makes me wonder if Coravel is doing something I'm not expecting.

Any ideas? Thanks again.

MrYossu avatar Dec 22 '21 15:12 MrYossu

@jamesmh I have discovered something that explains why the variables are back at their initial values, but I'm not sure why it's happening.

I just updated the sample, and added a second button that will add the widget using the old-fashioned method of firing an EventHandler<Widget>, and having the parent component catch this and pass the widget down to the list child component. That works fine.

Now, if you put a breakpoint on the first line of each of the two methods in Widgetlist, you'll see the hash code of the component. Every time you use the manual button, you get the same hash code, ie the same component. However, when you use the Coravel event, you get a different hash code every time you click the button. This means that it's creating a new WidgetList component every time you click the button, but not using it in the UI.

Any ideas? I'm baffled! Thanks again.

MrYossu avatar Dec 22 '21 18:12 MrYossu

The List<Widget> in the listener is re-created most likely because the listener class itself WidgetList is configured as a transient or scoped service?

What you'll need to do is have some kind of global instance of the List<Widget>. Change WidgetList to WidgetListListener and have another singleton/static service injected WidgetList. WidgetList might have a method like AddWidget, RemoveWidget that the listener can call.

Basically, wherever your list of widgets is being stored needs to be a static instance of some kind.

Let me know if that helps :)

jamesmh avatar Dec 24 '21 15:12 jamesmh

@jamesmh Thanks for the reply. This is a Blazor app, and (following the docs and your first suggestion) I added the WidgetList component as a transient service.

Sounds like you are saying that the Blazor component itself shouldn't be a listener, but should have an injected listener. Is that correct?

Thanks again.

MrYossu avatar Dec 26 '21 15:12 MrYossu

You could make the blazor component a listener, but make sure that it's a singleton service. I'm not familiar enough with Blazor to know if that makes sense though.

Otherwise, you'd want another singleton service that has the list of widgets in it, and that service would be injected into both (a) the listener and (b) the blazor component. The listener would call "addWidget", etc. (write) and thee blazor component would read the collection from that singleton service.

jamesmh avatar Dec 26 '21 22:12 jamesmh

@jamesmh Thanks for the reply. I don't think making a Blazor component a service makes sense, so I guess we'd have to have the list as a service.

Thanks again.

MrYossu avatar Dec 27 '21 14:12 MrYossu