2

I'm trying to create a context for my redis database without using Entity Framework. My connection depends on a ConnectionMultiplexer class, so for unit testing I'm trying to use dependency injection. I'm not sure how to create the using statement for my context without the ConnectionMultiplexer parameter.

Am I doing dependency injection wrong? How can I restructure so I can use the "using" context correctly and have it resolve the dependency for me?

GameSession.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using StackExchange.Redis;

namespace Bored.GameService.GameSession
{
    public class GameSessionContext : IGameSession, IDisposable
    {
        private IDatabase conn;
        private readonly IConnectionMultiplexer _muxer;

        public GameSessionContext(IConnectionMultiplexer muxer)
        {
            _muxer = muxer;
            string connectionString = Startup.Configuration.GetConnectionString("redis");
            _muxer = ConnectionMultiplexer.Connect(connectionString);
            //conn = muxer.GetDatabase();
            //conn.StringSet("foo", "bar");
            //var value = conn.StringGet("foo");
            //Console.WriteLine(value);
        }

        public void GetGameState()
        {
            throw new NotImplementedException();
        }

        public void AddGameState()
        {
            throw new NotImplementedException();
        }

        public void Dispose()
        {
            _muxer.Dispose();
        }
    }
}

Here is where I'm registering the dependency in my startup.cs

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IConnectionMultiplexer, ConnectionMultiplexer>();

    services.AddSignalR();

    services.AddCors(options =>
    {
        options.AddPolicy("ClientPermission", policy =>
        {
            policy.AllowAnyHeader()
                  .AllowAnyMethod()
                  .WithOrigins("http://localhost:3000")
                  .AllowCredentials();
        });
    });
}

Here is how I'm using it in my GameServiceAPI class:

using Bored.GameService.Clients;
using Bored.GameService.GameSession;
using Bored.GameService.Models;
using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;

namespace Bored.GameService.GameServiceAPI
{
    public class GameServiceHub : Hub<IGameClient>
    {
        public Task SendMessage(GameMessage message)
        {
// Throws an error because GameSessionContext has no IConnectionMultiplexer parameter passed in.
            using (var context = new GameSessionContext())
            {
                // var gameState = context.GetGameState(context);
                // context.AddGameState(gameState);
                // Clients.All.ReceiveMessage(gameState);
            }
            return Clients.All.ReceiveMessage(message);
        }
    }
}
4
  • 1
    Why not register both GameSessionContext and GameServiceHub with DI, giving GameServiceHub a constructor dependency on GameSessionContext ? Commented Dec 19, 2020 at 16:27
  • I believe this would work. After looking at it a bit more, the method I'm trying to mock for my unit tests is static and the interface does not have it so I believe I have other issues besides the DI. Commented Dec 19, 2020 at 17:54
  • I think my best approach is a combination of your answer and this stackoverflow.com/a/28326983/7331107 where I create a wrapper for where I interact with redis and only deal with that. Thank you! Commented Dec 19, 2020 at 17:57
  • @DavidBrowne-Microsoft I'm going to post the code I made to get it to work, if you want to copy that and make your own answer, I'll accept yours since that helped fix it! Commented Dec 19, 2020 at 18:26

1 Answer 1

1

Thanks to @DavidBrowne's suggestion for registering both the GameServiceHub and GameSessionContext with DI.

Here's the code I used to get this to work:

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IConnectionMultiplexer>(ConnectionMultiplexer.Connect(Configuration.GetConnectionString("redis")));
    services.AddScoped<IGameSessionContext, GameSessionContext>();

    services.AddSignalR();

    services.AddCors(options =>
    {
        options.AddPolicy("ClientPermission", policy =>
        {
            policy.AllowAnyHeader()
                  .AllowAnyMethod()
                  .WithOrigins("http://localhost:3000")
                  .AllowCredentials();
        });
});

GameSessionContext.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using StackExchange.Redis;

namespace Bored.GameService.GameSession
{
    public class GameSessionContext : IGameSessionContext
    {
        private IDatabase conn;
        private readonly IConnectionMultiplexer _muxer;

        
        public GameSessionContext(IConnectionMultiplexer muxer)
        {
            _muxer = muxer;
        }

        public string GetGameState()
        {
            conn = _muxer.GetDatabase();
            conn.StringSet("foo", "Here's game state");
            return conn.StringGet("foo");
        }

        public void AddGameState()
        {
            throw new NotImplementedException();
        }
    }
}

GameServiceHub.cs

using Bored.GameService.Clients;
using Bored.GameService.GameSession;
using Bored.GameService.Models;
using Microsoft.AspNetCore.SignalR;
using System;
using System.Threading.Tasks;

namespace Bored.GameService.GameServiceAPI
{
    public class GameServiceHub : Hub<IGameClient>
    {
        private IGameSessionContext _context;
        public GameServiceHub(IGameSessionContext context)
        {
            _context = context;
        }

        public Task SendMessage(GameMessage message)
        {
            var state = _context.GetGameState();
            Console.WriteLine(state);
                // var gameState = context.GetGameState(context);
                // context.AddGameState(gameState);
            return Clients.All.ReceiveMessage(message);
        }
    }
}

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.