coravel
coravel copied to clipboard
TargetException: Non-static method requires a target when trying to broadcast an event
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
What does 1WidgetList` look like?
@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
Did you make sure to register the WidgetList
class (or others) as a transient service? Not sure what else it might be?
@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.
@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.
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 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.
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 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.