lundi 24 août 2015

Singleton in DependencyInjection

I am struggling with concept of singletons in dependency injection. I am not sure whether classes should be implemented in way to support singleton / per instance instancing for classes intended to be used as singletons or whether they should rely on proper settings of instancing by programmer.

Following class will work as expected if it will be marked as singleton in Dependency container

...
builder.RegisterType<ApplicationSettings>().AsSelf().SingleInstance();
...


/// <summary>
/// This allows to create many ApplicationSettings instances which each of them will have its collection of settings. 
/// Thus we cannot guarantee that one of class has complete settings
/// </summary>
public class ApplicationSettings
{
    private readonly object _locker = new object();
    private readonly Dictionary<string, object> _settings;
    private readonly ILog _log;

    public ApplicationSettings(ILog log)
    {
        _log = log;
        _settings = LoadSettings();
        Thread.Sleep(3000); //inner hardwork, e.g. cashing of something
    }

    public object GetSettings(string key)
    {
        lock (_locker)
        {
            return _settings.ContainsKey(key) ? _settings[key] : null;
        }
    }

    public void SetSettings(string key, object value)
    {
        lock (_locker)
        {
            _settings.Remove(key);
            _settings.Add(key, value);
        }
    }

    public void Remove(string key)
    {
        lock (_locker)
        {
            _settings.Remove(key);
        }
    }

    public void Save()
    {
        Thread.Sleep(5000); //Saving somewhere
    }

    private Dictionary<string, object> LoadSettings()
    {
        Thread.Sleep(5000); //Long loading from somewhere
        return new Dictionary<string, object>();
    }
}

All classes which will need to use ApplicationSettings class will share one instance and thus Settings will contain all information when saving to somewhere. On the other hand if programmer do not mark class as SingleInstance there will be an issue when saving because if it will be implemented as replacing whole collection in storage place not all settings will be saved. Thus correct functionality strongly depends on programmer knowledge of class and using it as singleton.

In second example I am using static field for Settings which allows me to use class as singleton or as instancing per instance without effecting core functionality (I mean that not all settings will be saved if more instances of ApplicationSettings2 will be used)

/// <summary>
/// This allows to create many ApplicationSettings2 instances which each of them will share same collection of settings. 
/// </summary>
public class ApplicationSettings2
{
    private static readonly object Locker = new object();
    private static readonly Dictionary<string, object> Settings;
    private readonly ILog _log;

    static ApplicationSettings2()
    {
        Settings = LoadSettings();
        Thread.Sleep(3000); //inner hardwork, e.g. cashing of something
    }


    public ApplicationSettings2(ILog log)
    {
        _log = log;
    }

    public object GetSettings(string key)
    {
        lock (Locker)
        {
            return Settings.ContainsKey(key) ? Settings[key] : null;
        }
    }

    public void SetSettings(string key, object value)
    {
        lock (Locker)
        {
            Settings.Remove(key);
            Settings.Add(key, value);
        }
    }

    public void Remove(string key)
    {
        lock (Locker)
        {
            Settings.Remove(key);
        }
    }

    public void Save()
    {
        Thread.Sleep(5000); //Saving somewhere
    }

    private static Dictionary<string, object> LoadSettings()
    {
        Thread.Sleep(5000);
        return new Dictionary<string, object>();
    }
}

Both approach of class usage

...
builder.RegisterType<ApplicationSettings>().AsSelf().SingleInstance();
...

or

...
builder.RegisterType<ApplicationSettings>().AsSelf();
...

will lead to same expected functionality. Only difference is that not singleton instancing mode will lead to slower functionality (there is a sleep in ctor), but in the end of the day changing instancing mode does not break anything.

My questions:

  • Who is responsible for defining which class should be used as singleton in dependency container configuration?
  • Where should be this information about instancing stored?
  • Should I implement classes which are intending to be used as singleton to be able to work even in environment where it is instancing per instance?
  • If I am adding 3rd party DLL's class to my dependency container configuration, who is responsible to inform me how should I instancing this class? (How programmer can know which kind of instancing should he use? Should be this information mandatory part of documentation, or should programmer just use "try/use" approach?)
  • Should be "singleton" used only as way how to make system faster but should be singletons to be able to work even if they are instanced many times? (in dependency container configuration SingleInstance keyword is missing)

Aucun commentaire:

Enregistrer un commentaire