IoC - Devo utilizzare i contenitori figlio per creare dipendenze con scope?

1

Ho un certo numero di gestori di comandi definiti per lavorare con i database su vari server nella nostra rete. Mi chiedo se dovrei progettare ogni comando per passare le seguenti informazioni negli oggetti comando, o se qualcuno di questi dovrebbe essere iniettato come dipendenze scopate.

  • Informazioni sul server (nome del server, credenziali di accesso, ecc.)
  • Il nome del database.

Opzione 1: passa le informazioni con i comandi

// Note: I am passing ISqlServerInfo and databaseName into every method
public interface IDatabaseAnalyzer
{
    DatabaseState GetDatabaseState(ISqlServerInfo serverInfo, string databaseName);
    double GetDatabaseSize(ISqlServerInfo serverInfo, string databaseName);
}

// Note: I am passing ISqlServerInfo and databaseName into every method
public interface ISqlExecutor
{
    void Execute(ISqlServerInfo serverInfo, string databaseName, string sql);
}

public class BackupDatabaseCommand
{
    public string DatabaseName { get; set; }
    public ISqlServerInfo SqlServerInfo { get; set; }
    public string DestinationFolder { get; set; }
    public bool CopyOnly { get; set; }
}

public class BackupDatabase : ICommandHandler<BackupDatabaseCommand>
{
    public BackupDatabase(IDatabaseAnalyzer databaseAnalyzer,
                          ISqlExecutor sqlExecutor)
    {
        _databaseAnalyzer = databaseAnalyzer;
        _sqlExecutor = sqlExecutor;
    }

    public void Execute(BackupDatabaseCommand command) 
    {
        var dbState = _databaseAnalyzer.GetDatabaseState(command.SqlServerInfo, command.DatabaseName);
        if (dbState != DatabaseState.Online)
            throw new CommandFailedException();

        string sql = $"backup database [{command.DatabaseName}] ";
                     $"to disk = '{command.DestinationFolder}\{command.DatabaseName}_{DateTime.Now}.bak'";
        if (command.CopyOnly)
            sql += " with copy_only";

        _sqlExecutor.Execute(command.SqlServerInfo, command.DatabaseName, sql);
    }
}

public class BackupAllDatabases : ICommandHandler<BackupAllDatabasesCommand>
{
    public BackupAllDatabases(ISqlServerService sqlServerService
                              IDatabaseService databaseService,
                              ICommandHandler<BackupDatabaseCommand> backupDatabaseHandler)
    {
        _sqlServerService = sqlServerService;
        _databaseService = databaseService;
        _backupDatabaseHandler = backupDatabaseHandler;
    }

    public void Execute(BackupAllDatabasesCommand command) 
    {
        var databases = _databaseService.GetAllDatabases();
        foreach (DatabaseInfo databaseInfo in databases)
        {
            var sqlServerInfo = _sqlServerService.Get(databaseInfo.SqlServerId);
            _backupDatabaseHandler.Execute(new BackupDatabaseCommand
            {
                DatabaseName = databaseInfo.DatabaseName,
                SqlServerInfo = sqlServerInfo,
                DestinationFolder = command.DestinationFolder,
                CopyOnly = command.CopyOnly
            });
        }
    }
}

Contro:

  • Ogni metodo nei servizi IDatabaseAnalyzer e ISqlExecutor richiede l'inoltro delle informazioni sul server e sul database. Ovviamente queste dipendenze sono per questi servizi.

Opzione 2: come l'opzione 1, ma usa fabbriche per i servizi

// Note: Much cleaner methods now...
public interface IDatabaseAnalyzer
{
    DatabaseState GetDatabaseState();
    double GetDatabaseSize();
}

public interface IDatabaseAnalyzerFactory
{
    IDatabaseAnalyzer Create(ISqlServerInfo serverInfo, string databaseName);
}

// Note: Much cleaner methods now...
public interface ISqlExecutor
{
    void Execute(string sql);
}

public interface ISqlExecutorFactory
{
    IDatabaseAnalyzer Create(ISqlServerInfo serverInfo, string databaseName);
}

public class BackupDatabaseCommand
{
    public string DatabaseName { get; set; }
    public ISqlServerInfo SqlServerInfo { get; set; }
    public string DestinationFolder { get; set; }
    public bool CopyOnly { get; set; }
}

public class BackupDatabase : ICommandHandler<BackupDatabaseCommand>
{
    public BackupDatabase(IDatabaseAnalyzerFactory databaseAnalyzerFactory,
                          ISqlExecutorFactory sqlExecutorFactory)
    {
        _databaseAnalyzerFactory = databaseAnalyzerFactory;
        _sqlExecutorFactory = sqlExecutorFactory;
    }

    public void Execute(BackupDatabaseCommand command) 
    {
        var dbAnalyzer = _databaseAnalyzerFactory.Create(command.SqlServerInfo, command.DatabaseName);
        var dbState = dbAnalyzer.GetDatabaseState();
        if (dbState != DatabaseState.Online)
            throw new CommandFailedException();

        string sql = $"backup database [{command.DatabaseName}] ";
                     $"to disk = '{command.DestinationFolder}\{command.DatabaseName}_{DateTime.Now}.bak'";
        if (command.CopyOnly)
            sql += " with copy_only";

        var sqlExecutor = _sqlExecutorFactory.Create(command.SqlServerInfo, command.DatabaseName);
        sqlExecutor.Execute(sql);
    }
}

public class BackupAllDatabases : ICommandHandler<BackupAllDatabases>
{
    public BackupAllDatabases(ISqlServerService sqlServerService
                              IDatabaseService databaseService,
                              ICommandHandler<BackupDatabaseCommand> backupDatabaseHandler)
    {
        _sqlServerService = sqlServerService;
        _databaseService = databaseService;
        _backupDatabaseHandler = backupDatabaseHandler;
    }

    public void Execute(BackupAllDatabases command) 
    {
        var databases = _databaseService.GetAllDatabases();
        foreach (DatabaseInfo databaseInfo in databases)
        {
            var sqlServerInfo = _sqlServerService.Get(databaseInfo.SqlServerId);
            _backupDatabaseHandler.Execute(new BackupDatabaseCommand
            {
                DatabaseName = databaseInfo.DatabaseName,
                SqlServerInfo = sqlServerInfo,
                DestinationFolder = command.DestinationFolder,
                CopyOnly = command.CopyOnly
            });
        }
    }
}

Pro:

  • I servizi di supporto sembrano molto più puliti.

Contro:

  • È un po 'fastidioso dover creare un'istanza delle mie dipendenze usando una fabbrica.

Opzione 3: utilizzare un contenitore secondario per creare l'ambito di dipendenza

public interface IDatabaseAnalyzer
{
    DatabaseState GetDatabaseState();
    double GetDatabaseSize();
}

public interface ISqlExecutor
{
    void Execute(string sql);
}

public class BackupDatabaseCommand
{
    public string DestinationFolder { get; set; }
    public bool CopyOnly { get; set; }
}

public class BackupDatabase : ICommandHandler<BackupDatabaseCommand>
{
    // Note: IDatabaseInfo is now a dependency
    public BackupDatabase(IDatabaseInfo databaseInfo,
                          IDatabaseAnalyzer databaseAnalyzer, 
                          ISqlExecutor sqlExecutor)
    {
        _databaseInfo = databaseInfo;
        _databaseAnalyzer = databaseAnalyzer;
        _sqlExecutor = sqlExecutor;
    }

    public void Execute(BackupDatabaseCommand command) 
    {
        var dbState = _databaseAnalyzer.GetDatabaseState();
        if (dbState != DatabaseState.Online)
            throw new CommandFailedException();

        string sql = $"backup database [{_databaseInfo.DatabaseName}] "+ 
                     $"to disk = '{command.DestinationFolder}\{_databaseInfo.DatabaseName}_{DateTime.Now}.bak'";
        if (command.CopyOnly)
            sql += " with copy_only";

        _sqlExecutor.Execute(sql);
    }
}

public class BackupAllDatabases : ICommandHandler<BackupAllDatabases>
{
    public BackupAllDatabases(IContainer container,
                              ISqlServerService sqlServerService
                              IDatabaseService databaseService)
    {
        _container = container;
        _sqlServerService = sqlServerService;
        _databaseService = databaseService;
    }

    public void Execute(BackupAllDatabases command) 
    {
        var databases = _databaseService.GetAllDatabases();
        foreach (DatabaseInfo databaseInfo in databases)
        {
            var sqlServerInfo = _sqlServerService.Get(databaseInfo.SqlServerId);

            // Create child container and inject the dependencies needed
            var childContainer = _container.CreateChildContainer();
            childContainer.RegisterInstance<ISqlServerInfo>(sqlServerInfo);
            childContainer.RegisterInstance<IDatabaseInfo>(databaseInfo);

            var backupDatabase = childContainer.Resolve<ICommandHandler<BackupDatabaseCommand>>();

            backupDatabase.Execute(new BackupDatabaseCommand
            {
                DestinationFolder = command.DestinationFolder,
                CopyOnly = command.CopyOnly
            });
        }
    }
}

Pro:

  • Come i servizi di supporto, il comando del database ora ha le informazioni del database / server come dipendenza.

Contro:

  • Il comando BackupAllDatabases ha accesso a IContainer, che sembra divino.
  • Non è troppo ovvio cosa sta succedendo o perché sto creando un contenitore figlio.

Qualcuno ha un consiglio alternativo su questo?

    
posta Justin Hopper 30.04.2018 - 05:39
fonte

0 risposte