2

I am using C# on a pretty huge solution that user a Static Class to manage the Session on the database.

The performance are not so good because of the number of call to the database.

The company decide to introduce Redis as a very fast alternative. Of course it is so fast! But now I have to design a service that will replace gradually the original Static Class.

In the solution there is no DI system.

I am asking some guidance to design the new "Session Manager" that abstract the usage of the Static Class rather than the Redis Client. Based on the operation, It will internally call one method rather the other.

Is there any pattern to get advice from?

6
  • You can still use static methods in the new session manager which refer to a static instance of Redis. Just make sure Redis is thread-safe. If it isn't, then you must make it so.
    – Neil
    Nov 21 '17 at 15:28
  • 1
    I posted an answer to a very similar question 4 years ago. My answer isn't spectacular, but I would probably follow a very similar approach today. Take a look: softwareengineering.stackexchange.com/questions/195079/…
    – MetaFight
    Nov 21 '17 at 15:46
  • 1
    This question is really broad. Have you made an attempt at creating something yet? It sounds like you already understand the need to get away from the static class and build something a bit more testable and loosely-coupled. What's the problem? Nov 21 '17 at 16:28
  • You say there isn't a DI system. What is stopping you from adding one?
    – Becuzz
    Nov 21 '17 at 20:31
  • 2
    I've found Working Effectively with Legacy Code tremendously helpful for situations like this. Nov 22 '17 at 9:14
3

The solution provided in Mike Feather's Legacy Code book is very similar to the solutions given above, but without any need to change the consumers of your static class.

Let's start with the simple example given by John Wu, with small modification (I assume that the class is also marked static):

public static class SessionHelper
{
    public static void Foo()
    {
        //Implementation
    }

    public static void Bar()
    {
        //Implementation
    }
}

Extract the interface, create a new (non-static) class implementing it by moving all the implementation details from the static class into the new class, and remove its static modifiers. Also generate such a class using Redis instead of the database:

public interface ISessionHelper
{
    void Foo();
    void Bar();
}

class DatabaseSessionHelper : ISessionHelper
{
    public void Foo() 
    {
        //Implementation taken from the static class
    }

    public void Bar() 
    {
        //Implementation taken from the static class
    }
}


class RedisSessionHelper : ISessionHelper
{
    public void Foo() 
    {
        //new Implementation based on Redis
    }

    public void Bar() 
    {
        //new Implementation based on Redis
    }
}

Now go back to your original static class, add a new static member of the interface type, instantiate the DatabaseSessionHelper by default, but allow for a different instance to be injected, and forward all calls to the instance:

public static class SessionHelper
{
    private static ISessionHelper _Instance = new DatabaseSessionHelper();

    public static void SetInstance(ISessionHelper instance)
    {
        _Instance = instance;
    }

    public static void Foo()
    {
        _Instance.Foo();
    }

    public static void Bar()
    {
        _Instance.Bar();
    }
}

Now you can set the instance to Redis somewhere during the start-up of your application: SessionHelper.SetInstance(new RedisSessionHelper()). That's the only change required in the rest of the program, nothing else needs to be changed, the rest of the program will not need to know anything about the changes in the static class. That's the difference to the other solutions provided here, which require changes elsewhere.

3

Let's say you have a static library like this:

class SessionHelper
{
    static public void Foo()
    {
        //Implementation
    }

    static public void Bar()
    {
        //Implementation
    }
}

And a program that uses it, that you'd rather not monkey with too much:

class MassiveSpaghettiCode
{
    void DoSomethingNobodyUnderstands()
    {
        SessionHelper.Foo();
        SessionHelper.Bar();
    }
}

I would extract the static methods to an interface and copy the implementation code from the static class, only exposing each method as an interface member.

interface ISessionHelper
{
    void Foo();
    void Bar();
}

class SessionHelper : ISessionHelper
{
    public void Foo() 
    {
        //Implementation
    }

    public void Bar() 
    {
        //Implementation
    }
}

Then, expose an instance of the implementation via a member property with the same name as the original static class. This allows the legacy spaghetti code to continue to function without any modification. For bonus points, we can inject the instance if we want.

class MassiveSpaghettiCode
{
    protected readonly ISessionHelper _sessionHelper;

    protected ISessionHelper SessionHelper 
    {
        get
        {
            return _sessionHelper;
        }
    }            

    public MassiveSpaghettiCode() : this( new SessionHelper() ) //Default constructor requires no injection
    {
    }

    public MassiveSpaghettiCode(ISessionHelper sessionHelper)
    {
        _sessionHelper = sessionHelper; //Injected, or injectable at least
    }

    void DoSomethingNobodyUnderstands()
    {
        SessionHelper.Foo();  //Notice this code hasn't changed at all
        SessionHelper.Bar();
    }
}

Once you have the existing code extracted to an interface/implementation, write a series of integration tests and get it nice and stable. You want to make sure you understand what it means to fulfill the contract implied by that interface,not just syntactically but semantically.

Once you have a thorough understanding of the interface as implemented by the legacy code, create a new class, implementing the same interface, but with brand new code using your newer back end technology (Redis, or whatever). Run this class through exactly the same unit tests. Be sure you can explain any difference in test outcomes; ideally there shouldn't be any.

Once all of the tests pass identically, you're ready to switch it out, which is as easy as changing the one dependency.

P.S. I believe this is pretty much the approach Microsoft took when migrating everyone from classic ASP to ASP.NET, e.g. the Session and Response syntax looked the same but worked rather differently.

1

Create an interface for the functionality. I will use an example based on a static file system call, but the concepts are the same for your Redis problem.

public interface IFileSystem
{
   string ReadAllText(string path);
}

Now the real one:

public class RealFileSystem : IFileSystem
{
   public ReadAllText(string path)
   {
     System.IO.File.ReadAllText(path);
   }
}

An alternative (Fake):

public class FakeFileSystem: IFileSystem 
{
  public ReadAllText(string path)
  {
    return "Fake response from: " + path;
  }
}

Now, one will need to extract that dependency, either in the constructor or the method that is calling it.

As an example callout, let's use method level injection:

public string MethodLevelInjection(IFileSystem fileSystem, string path)
{
  fileSystem.ReadAllText(path);
}

Now, whoever is calling this method will have to create the proper instance and pass it in. The tests can use a fake implementation.

So, one will either have to use method level or constructor level dependency injection to pass the dependency in. One could introduce a dependency injection framework to handle the wire up, or do it manually. If this is the only case, maybe manually is the best until there are multiple instances of DI needed.

But now one has the ability to switch out the dependency.

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.