1

I'm using SignalR with StackExchange Redis. What I gather from the basic usage doc is the way to implement this is using a singleton. I've never used this patter before. My class looks like this:

public class RedisInstance
{
    public static readonly RedisInstance Instance = new RedisInstance();
    public static ConnectionMultiplexer Redis;

    public RedisInstance()
    {
        Redis = ConnectionMultiplexer.Connect("localhost");
    }
}

I've been using it like this

public class DashHub : Hub
{
    public void MapUser(int userId)
    {
        var user = new AppUser() {Id = userId, ConnectionIds = new List<string>() {Context.ConnectionId } };
        var db = RedisInstance.Redis.GetDatabase();

        var userJson = JsonConvert.SerializeObject(user);

        db.StringSet(userId.ToString(), userJson);
    }

But I was thinking this is rather constricting if I build this out and down the line want to use something other than Redis, so I was going to do this

public class DatabaseRepository
{
    IDatabase db;

    public DatabaseRepository()
    {
        this.db = RedisInstance.Redis.GetDatabase();
    }

    public void Insert(string key, string value)
    {
        db.StringSet(key, value);
    }
}

and then reference this in my other classes. Am I using these patterns appropriately here? Are there any issues with using a singleton in this way? This may seem straightforward but I'm just learning this and want to make sure.

1

When your code has a singleton, I would assume that anyone can use that singleton. Otherwise, it is not a singleton (a class that has by coincidence only one instance is not a singleton. A class that by design can only have one instance is a singleton).

No you say you "wrap a singleton". The wrapper would again be a singleton. (You can of course have a class with many instances that each hold a reference to the same singleton, but that wouldn't be a wrapper).

So you have a singleton, and a wrapper singleton which behind the scenes refers to the same singleton. I would be worried that being able to use the same singleton in two different ways would cause confusion.

0

Try something like (pseudo code):

public Interface IKeyValueRepository
{
    void Insert(string key, string value);
     //Put other methods here
}

//Redis Implementation
public RedisRepository : IKeyValueRepository
{
    private static ConnectionMultiplexer _redis;

    public RedisRepository ()
    {
         _redis = ConnectionMultiplexer.Connect("localhost");
    }

    public void Insert(string key, string value)
    {
        var db = _redis.GetDatabase();       
        db.StringSet(key, value);
    }
    //And so forth
}

Then you should be able to swap thing out pretty easily and mock the repository as well.

  • so using a singleton isn't correct in this case? When they say "store and re-use this(ConnectionMultiplexer)" they mean just make it a static field in a class? – ygetarts Aug 4 '16 at 21:35
  • It depends on how you want to use the connection. If you want to maintain it across instances, use static , if you want multiple connection instances for each new repository, then it would just be private and not static. – Jon Raynor Aug 5 '16 at 13:18
0

I've not had much experience using redis so this code may not be 100% correct. But it would be better if you used dependency injection to manage your singleton. That way you aren't having to code your class to be a singleton and if you want/need to change it at any point it will be very simple. eg

DI layer

builder.RegisterType<RedisInstance>().As<IRedisInstance>().SingleInstance();
builder.RegisterType<DatabaseRepository>().As<IDatabaseRepository>();

Redis Instance

public class RedisInstance : IRedisInstance
{
    public static ConnectionMultiplexer Redis;

    public RedisInstance()
    {
        Redis = ConnectionMultiplexer.Connect("localhost");
    }
}

DatabaseRepository

public class DatabaseRepository: IDatabaseRepository
{
   private readonly IRedisInstance db;

   public DatabaseRepository(IRedisInstance redisInstance)
   {
      this.db = redisInstance;
   }

   public void Insert(string key, string value)
   {
     this.db.Redis.StringSet(key, value);
   }
}

Usage

public class DashHub : Hub
{
  private readonly IDatabaseRepository repo;

  public DashHub(IDatabaseRepository repo)
  {
   this.repo = repo;
   }
    public void MapUser(int userId)
    {
        var db = this.repo.Insert("blah", "blah");
    }
}

Additional

I'm not sure why you would want your data access to be in singleton mode as you would want your connection to close and having a singleton will mean that multiple requests will be accessing the same instance. So you can now change your dependency injection to be like so:

builder.RegisterType<RedisInstance>().As<IRedisInstance>().InstancePerLifetimeScope();
  • well, that's part of my question. reading the redis docs, i was asking if what they are saying means i should be using a singleton or not. – ygetarts Aug 8 '16 at 15:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.