2

Can someone who is either familiar with Fluxor or C# Dependency Injection help me with an issue that I've struggled with for several days please.

When you use Fluxor within a Blazor Component you include the following to raise an Action:

@Inherits Blazor.Fluxor.Components.FluxorComponent
@Inject IDisptacher dispatcher

That allows you to raise an Action within the Component with: Dispatcher.Dispatch(new MyAction(MyParameter));

In addition to dispatching Fluxor Actions from a Blazor Component (which I have working well), I need to be able to dispatch an Action from a regular class in a .cs file. I've tried Injection as shown in the following TestClass but a NullReferenceException is raised when Dispatcher.Dispatch is hit.

using Fluxor;
using Microsoft.AspNetCore.Components;

namespace ICET.Store
{
    public class TestClass
    {
        [Inject]
        private IDispatcher Dispatcher { get; set; }

        public void onMessage()
        {
            // Other code removed
            Dispatcher.Dispatch(new LoggedInAction(phoneNumber));
        }
    }
}

Clearly something has to instantiate the Dispatcher declared with private IDispatcher Dispatcher { get; set; } but I thought that instantiation happened in the Startup.cs with the following:

services.AddFluxor(options =>
{
   options.UseDependencyInjection(typeof(Startup).Assembly)
{);

Clearly I missing something here but I'm stumped with what that is. Can someone give me some guidance about how a Fluxor Action can be raised within a Class included in a regular .cs file. Thanks.

Using Constructor Injection as suggested by Peter Morris I've altered my Test Class to:

public class TestClass
    {
        IDispatcher _dispatcher;

        public TestClass(IDispatcher dispatcher)
        {
            _dispatcher = dispatcher;    
        }
    
        public void onMessage()
        {
            // Other code removed

            _dispatcher.Dispatch(new LoggedInAction());
        }
    }

However, what I'm struggling with now is when I instantiate my Class how do I get a reference to a Fluxor Disptacher that I can pass to the TestClass Constructor? I thought as Fluxor is implemented as a Service ie;

services.AddFluxor(options =>
{
   options.UseDependencyInjection(typeof(Startup).Assembly)
{);

... that's it somehow available globally to reference.

Next update ...

My TestClass has been altered to implement an Interface and is now registered as a Scoped Service in Startup.cs

public interface ITestClass
    {
        public void InitiateAction();

    }


    public class TestClass : ITestClass
    {
        IDispatcher _dispatcher;

        public TestClass(IDispatcher dispatcher)
        {
            _dispatcher = dispatcher;
        }

        public void InitiateAction()
        {
            _dispatcher.Dispatch(new LoggedInAction("123456"));
        }
    }

Startup.cs

services.AddScoped<ITestClass, TestClass>();

As a result of these changes I was expecting the TestClass would have been instantiated and I'd be able to call its InitiateActionmethod to Dispatch an Action. However, it remains stubbornly null in the following code:

class DeviceListener : Cometd.Bayeux.Client.IMessageListener
    {
        private readonly IDispatcher _dispatcher;
        private ITestClass _testClass;

       public void onMessage(Cometd.Bayeux.Client.IClientSessionChannel mChannel, Cometd.Bayeux.IMessage message)
        {
            _testClass.InitiateAction();
}

Update ... My DeviceListener class implements the Cometd.Bayeux.Client.IMessageListener interface and defines a Callback which is executed when an unsolicited message is received from a server process. It's referenced in Startup.cs:

listener = mClient.getChannel("/v2/me/devices");
listener.subscribe(new GWS.DeviceListener());

The class is structured as follows:

    class DeviceListener : Cometd.Bayeux.Client.IMessageListener
    
        {
            private readonly IDispatcher _dispatcher;
    
            public DeviceListener(IDispatcher dispatcher)
        {
            _dispatcher = dispatcher;
        }

public void onMessage(Cometd.Bayeux.Client.IClientSessionChannel mChannel, Cometd.Bayeux.IMessage message)
            {
                _dispatcher.Dispatch(new LoggedInAction("12345");
    
            }
        }

All illustrated in the above DeviceListener, I want to be able to dispatch a Fluxor Action from within onMessage.

The above DeviceListener shows a parameterized Constructor but that actually causes an argument compile error against listener.subscribe(new GWS.DeviceListener()); in Startup.cs

Peter
  • 47
  • 6
  • DeviceListener._testClass isn't being set. Create a parameterised constructor and inject it. You must then make sure you resolve your instance of DeviceListener through IServiceProvider or an `@inject` in a component. – Peter Morris Jul 06 '20 at 14:24
  • @peter I truly appreciate your help but the brevity of your comments is leaving me more confused than anything. After spending another day on this I'm no closer to solving this issue. – Peter Jul 07 '20 at 01:57
  • Prev comment continued ... Would you mind putting a simple example together shows how the Dispatcher.dispatch method can be used in a class which is completely standalone and not related to a Component or MVC Controller. I don't expect you to teach me how to code but, as you wrote Fluxor.Blazor.Web nobody knows it better than you and a sample would help others who may find this conversation in the future. I'd be forever in your debt :) – Peter Jul 07 '20 at 02:10
  • This isn't really about Fluxor, it's about dependency injection. Are you creating your DeviceListener instance yourself, or is it being injected into something? – Peter Morris Jul 07 '20 at 07:31
  • Thanks @Peter I appreciate it may be a DI rather than Fluxor question but I just can't get my head around it. I've not worked with DI before so forgive me if I'm missing something obvious. My main question has just been updated with details of my DeviceListener and what I'm trying to achieve. – Peter Jul 07 '20 at 09:42
  • Instead of manually creating DeviceListener in Startup.cs, inject it into App.razor and use it to subscribe in OnInitialized. As long as you have your dependencies in your constructors, or use `@inject` then everything will be dealt with for you. – Peter Morris Jul 07 '20 at 11:38
  • @Peter I'm relieved to report that I've now solved this problem. Phew! Your suggestion to move the DeviceListener initialisation code to the App.razor, OnInitialized event was the turning point and once I figured out how the DI was working, I've built upon that success. Many thanks for your help and guidance. – Peter Jul 08 '20 at 04:20

1 Answers1

1

In a non component use constructor injection.

The problem is probably that your object isn't being created by the dependency container, so no code is fixing up the [Inject]

Peter Morris
  • 20,174
  • 9
  • 81
  • 146
  • Thanks Peter, I appreciate your interest and suggestion but I'm still struggling. I've added to my question and would appreciate your comments or, better still, some sample code I could use to help my understanding. Thanks – Peter Jul 05 '20 at 22:10
  • AdfDependencyInjection just registers reducers, effects, and features in the IServiceCollection. You need to use the current IServiceProvider to create an instance of TestClass, that will automatically inject IDispatcher into the constructor. Or you can have a TestClass property in a component marked [Inject]. Does that help? If not, please explain what you are trying to do. – Peter Morris Jul 05 '20 at 22:39
  • My Blazor application has an listener that receives CometD messages from a server process within a Contact Centre. These messages can be uninitiated, ie; an inbound phone call. When a "Ringing" message is received, I need to Dispatch a Fluxor Action so various Components react appropriately to the message. Will update my main Question in a moment ... – Peter Jul 05 '20 at 23:41