Search code examples
c#wpfdependency-injectionunity-containercircular-dependency

Solving a circular dependency between a client and server


I have a client and a server class written in C#. Both need to communicate with each other (In future several Clients will communicate with the Server). But I run into a circular dependency. Because I'm using unity for dependency injection my programm freezes and crashes.

I tried to use an interface between client and server as a messageprovider. But it crashes anyway because the implementation of the interface has a reference to client und server and they have a reference to the interface.

The project ist really big, but I tried to cut it down:

   public class Client
{
    private Server server;

    public Client(Server server)
    {
        this.server=server;
    }

    public SendToServer(string message)
    {
        server.Receive(message);
    }

    public Receive(string message)
    {

    }
}

public class Server
{
    private Client client;

    public Server(Client client)
    {
        this.client=client;
    }

    public SendToClient(string message)
    {
        client.Receive(message);
    }

    public Receive(string message)
    {

    }
}

All this runs inside an UserControl in WPF. The Client gets injected into the constructor of the view. Unity injects then the Server into the Client constructor which will trigger the Server constructor which will trigger again the client constructor.

public partial class ClientView: UserControl
{
    public ClientView(Client client)
    {
        this.DataContext = client;
        InitializeComponent();
    }

}

Solution

  • At a glance this looks suspicious, but if you're using this composition you need to break the dependency cycle somehow.

    One way is to create abstractions of the dependencies and make multiple implementations of one that isn't dependent of the other. E.g.

    public interface IServer { void Receive(string message); void SendToClient(string message); }
    public interface IClient { void Receive(string message); void SendToServer(string message); }
    
    public class Client : IClient
    {
        // This will be set as SimpleServer-implementation
        private readonly IServer _server;
    
        public Client(IServer server) => _server = server;
    
        public void SendToServer(string message) => _server.Receive(message);
    
        public void Receive(string message) => Console.WriteLine($"Client received: {message}");
    }
    
    public class Server : IServer
    {
        private readonly IClient _client;
    
        public Server(IClient client) => _client = client;
    
        public void SendToClient(string message) => _client.Receive(message);
    
        public void Receive(string message) => Console.WriteLine($"Server received: {message}");
    }
    
    public class SimpleServer : IServer
    {
        public void SendToClient(string message) { }
    
        public void Receive(string message) => Console.WriteLine($"SimpleServer received: {message}");
    }
    
    void Main()
    {
        var container = new UnityContainer();
        container.RegisterType<IServer, Server>();
        container.RegisterType<IClient, Client>(new InjectionConstructor(container.Resolve<SimpleServer>()));
    
        var server = container.Resolve<IServer>();
        var client = container.Resolve<IClient>();
    
        server.SendToClient("Foo");
        client.SendToServer("Bar");
    }
    

    Another way do deal with this is to defer the initalization of one dependency with the help of a factory. This way we can lazy load the dependency the first time we use it. In the following example I've used a Func delegate as a factory and wrapped it in a Lazy.

    public interface IServer { void Receive(string message); void SendToClient(string message); }
    public interface IClient { void Receive(string message); void SendToServer(string message); }
    
    public class Client : IClient
    {
        private readonly IServer _server;
    
        public Client(IServer server) => _server = server;
    
        public void SendToServer(string message) => _server.Receive(message);
    
        public void Receive(string message) => Console.WriteLine($"Client received: {message}");
    }
    
    public class Server : IServer
    {
        private readonly Lazy<IClient> _client;
    
        public Server(Func<IClient> clientFactory) 
            => _client = new Lazy<IClient>(() => clientFactory());
    
        public void SendToClient(string message) => _client.Value.Receive(message);
    
        public void Receive(string message) => Console.WriteLine($"Server received: {message}"); }
    }
    
    void Main()
    {
        var container = new UnityContainer();
        container.RegisterType<IClient, Client>();
        container.RegisterType<IServer, Server>();
        var server = container.Resolve<IServer>();
        var client = container.Resolve<IClient>();
        server.SendToClient("Foo");
        client.SendToServer("Bar");
    }