Skip to content

Data Management

JVerbruggen edited this page Jan 11, 2021 · 7 revisions

This wiki will contain information about how data is managed within the application.

The problem

One of the first problems that were encountered during this project, was about data management. How can we store data so that it is easily accessible throughout the whole application?

Data managers

We will have to manage each type of data, or each class, within its own data manager. Instances of a class can be registered to this manager and can be asked for later on.

public class SignalManager : ItemManager<Signal>
SignalManager signalManager = new SignalManager(); // Create our signal manager
Signal signal = new Signal(); // Create a new signal

signalManager.Register(signal); // Register a signal to a manager instance
IEnumerable<Signal> signals = signalManager.GetAll(); // Returns a list of all signals that were previously registered to that manager instance

But because we are talking about lots of different data types, we will have a lot of data managers. Also, how do we persist registered data within these data managers? This data will be lost after we discard the old manager because it only registers the data to a certain instance. It will not be available when we create a new instance. Frankly, we will have to manage these managers again. This is where singletons come in.

Singletons

Singletons manage these data managers for us. A singleton works by returning the same instance of a data manager (or really any class of any kind) every time a certain type is requested. By registering data to a data manager that is managed by a singleton, we will persist the registered data when we call for that data manager elsewhere.

In this application, we go one step further. We don't have to register an instance to a singleton at all; We can let the singleton make an instance for us. By supplying a type to the singleton, it can make an instance by the use of reflection and return that instance to the caller. And; if it already exists, it will just return that existing instance. We will never have to worry about making an instance and registering that to the singleton, before requesting the singleton for an instance.

InstanceManager.Singleton<SignalManager>().GetInstance();

As seen above, the InstanceManager supplies this singleton functionality to us. But, how will abstract classes be managed? Abstract classes can't be instantiated. How will the singleton know how to handle a request for an abstract class?

Abstract data managers

When a singleton comes across an abstract class, it simply doesn't know how to supply it... by default. The InstanceManager that supplies the singleton, has an extra singleton function. A function to specify the instance it should return.

InstanceManager.Singleton<AbstractSignalManager>(new SignalManager()).GetInstance();

If we want the singleton to supply us with an abstract signal manager, we can define the instance before requesting it. Next time, when requesting an AbstractSignalManager instance, a SignalManager instance will be returned. If registering this instance of an abstract class is forgotten, the next time it is requested, it will simply return null.

InstanceManager.Singleton<AbstractSignalManager>().GetInstance(); // returns SignalManager instance

Factory pattern

If you don't simply want to return an instance of a type, you can also let the singleton functionality use a factory.

public class TrackAssetManagerFactory : ManagerFactory<TrackAssetManager, TrackAsset>
{
    public override TrackAssetManager CreateDefault()
    {
        var trackAssetManager = new TrackAssetManagerDefault();

        // Do some logic with this manager

        return trackAssetManager;
    }
}

A factory can be registered to the InstanceManager by the following call:

InstanceManager.RegisterFactory(new TrackAssetManagerFactory());

The singleton functionality will automatically search through the factories to see if any of them are providing the type that is requested, and return an instance through the CreateDefault() function if it is.

Configuration

The configuration of abstract classes and singletons can be defined in any layer above the services layer in your application. It is important to run these definitions before they are used, as abstract classes will simply return null.

In the forms app, this is done in the Dependencies class in the root folder. Factories are also defined here.

public class Dependencies
{
    public static void RegisterFactories()
    {
        InstanceManager.RegisterFactory(new TrackAssetManagerFactory());
        InstanceManager.RegisterFactory(new SignalFactory());
    }

    public static void RegisterDependencies()
    {
        // Add abstract singletons here with implementing type
        // Registering here will override a factory with the same type

        // InstanceManager.Singleton<TrackAssetManager>(new TrackAssetManagerLimited());
    }
}

This configuration can have any format you'd like it to have. The only thing that is important is that abstract types are registered before used.

Diagram overview

To clarify all concepts above, here's a brief overview of the class structure.

Clone this wiki locally