Maxim Gershkovich
Maxim Gershkovich

Reputation: 47179

Akka.NET and MVVM

I am playing around with using Akka.NET in a new WPF .NET Framework application I am currently working on.

Mostly the process of using actors in your application seems pretty self explanitory, however when it comes to actually utilising the actor output at the application view level I have gotten a bit stuck.

Specifically there appear to be two options on how you might handle receiving and processing events in your actor.

  1. Create an actor with publically exposed event handlers. So maybe something like this:

    public class DoActionActor : ReceiveActor
    {
        public event EventHandler<EventArgs> MessageReceived;
    
        private readonly ActorSelection _doActionRemoteActor;
    
        public DoActionActor(ActorSelection doActionRemoteActor)
        {
            this._doActionRemoteActor = doActionRemoteActor ?? throw new ArgumentNullException("doActionRemoteActor must be provided.");
    
            this.Receive<GetAllStuffRequest>(this.HandleGetAllStuffRequestReceived);
            this.Receive<GetAllStuffResponse>(this.HandleGetAllStuffResponseReceived);
        }
    
        public static Props Props(ActorSystem actorSystem, string doActionRemoteActorPath)
        {
           ActorSelection doActionRemoteActor = actorSystem.ActorSelection(doActionRemoteActorPath);
           return Akka.Actor.Props.Create(() => new DoActionActor(doActionRemoteActor));
        }
    
        private void HandleGetAllStuffResponseReceived(GetAllTablesResponse obj)
        { 
            this.MessageReceived?.Invoke(this, new EventArgs());
        }
        private void HandleGetAllStuffRequestReceived(GetAllTablesRequest obj)
        {
            this._doActionRemoteActor.Tell(obj, this.Sender);
        }
    }
    

So basically you can then create your view and invoke any calls by doing something like this _doActionActor.Tell(new GetStuffRequest()); and then handle the output through the event handler. This works well but seems to break the 'Actors 'everywhere' model' that Akka.NET encourages and I am not sure about the concurrency implications from such an approach.

  1. The alternative appears to be to actually make it such that my ViewModels are actors themselves. So basically I have something that looks like this.

    public abstract class BaseViewModel : ReceiveActor, IViewModel
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        public abstract Props GetProps();
    
        protected void RaisePropertyChanged(PropertyChangedEventArgs eventArgs)
        {
            this.PropertyChanged?.Invoke(this, eventArgs);
        }
    }
    
    
    
    public class MainWindowViewModel : BaseViewModel
    {
        public MainWindowViewModel()
        {
            this.Receive<GetAllTablesResponse>(this.HandleGetAllTablesResponseReceived);
    
            ActorManager.Instance.Table.Tell(new GetAllTablesRequest(1), this.Self);
        }
    
        public override Props GetProps()
        {
            return Akka.Actor.Props.Create(() => new MainWindowViewModel());
        }
    
        private void HandleGetAllTablesResponseReceived(GetAllTablesResponse obj)
        {
    
        }
    }
    

This way I can handle actor events directly in actors themselves (which are actually my view models).

The problem I run into when trying to do this is correctly configuring my Ioc (Castle Windsor) to correctly build Akka.NET instances.

So I have some code to create the Akka.NET object that looks like this

        Classes.FromThisAssembly()
                .BasedOn<BaseViewModel>()
                .Configure(config => config.UsingFactoryMethod((kernel, componentModel, context) =>
                {
                    var props = Props.Create(context.RequestedType);
                    var result = ActorManager.Instance.System.ActorOf(props, context.RequestedType.Name);
                    return result;
                }))

This works great at actually creating an instance of IActorRef BUT unfortunately I cannot cast the actor reference back to the actual object I need (in this case BaseViewModel).

So if I try to do this return (BaseViewModel)result; I get an invalid cast exception. Which obviously makes sense because I am getting an IActorRef object not a BaseViewModel.

So in conclusion I am hoping to get two questions answered.

  1. What is the best way to deal with Akka.NET actors in MVVM applications, specifically when it comes to handling messages received and handling displaying the output.

  2. Is there a way to correctly configure my Ioc system to both create an IActorRef instance and add it to the system BUT return an instance of the actual parent actor object concrete implementation of BaseViewModel?

Upvotes: 3

Views: 478

Answers (1)

Maxim Gershkovich
Maxim Gershkovich

Reputation: 47179

Below is the current solution that I am using in the hope someone might propose something a bit better.

Basically I have abandoned my attempt at making my views actors and currently settled on using an interface to communicate between the ViewModel and Actor.

The current solution looks like this:

public class MainWindowViewModel : BaseViewModel, ITableResponseHandler
{
    public void HandleResponse(IEnumerable<Entity> allEntities) { }
}

public interface ITableResponseHandler
{
    void HandleResponse(IEnumerable<Entity> allEntities);
}

public class MyActor : ReceiveActor
{
    public MyActor(ITableResponseHandler viewModel) 
    {
        this.Receive<GetAllEntitiesResponse>(this.HandleGetAllEntitiesResponseReceived);
    }

    private void HandleGetAllEntitiesResponseReceived(GetAllTablesResponse obj)
    {
        this._ViewModel.HandleTablesResponse(obj.Result);
    }

}

While I don't feel this is ideal it basically lets me avoid all the extra complexity of trying to make my view models themselves actors while sufficently decoupling the actor from the view.

I hope someone else has faced this problem and might be able to provide some insight at a better solution for handling Akka.NET output in a MVVM application.

Upvotes: 1

Related Questions