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?