mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-27 09:35:38 -05:00
generic way to support scriptable operations (#438)
* implemented a generic way to support scriptable operations
This commit is contained in:
@@ -52,7 +52,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
/// this is used when the backup dialog is launched in the context of a backup device
|
||||
/// The InitialBackupDestination will be loaded in LoadData
|
||||
private string initialBackupDestination = string.Empty;
|
||||
|
||||
|
||||
// Helps in populating the properties of an Azure blob given its URI
|
||||
private class BlobProperties
|
||||
{
|
||||
@@ -163,6 +163,19 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The error occurred during backup operation
|
||||
/// </summary>
|
||||
public string ErrorMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public SqlTask SqlTask { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Execute backup
|
||||
/// </summary>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
//
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.SqlTools.ServiceLayer.TaskServices;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
@@ -12,7 +13,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts
|
||||
/// <summary>
|
||||
/// Restore request parameters
|
||||
/// </summary>
|
||||
public class RestoreParams : GeneralRequestDetails
|
||||
public class RestoreParams : GeneralRequestDetails, IScriptableRequestParams
|
||||
{
|
||||
/// <summary>
|
||||
/// Restore session id. The parameter is optional and if passed, an existing plan will be used
|
||||
@@ -140,6 +141,26 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts
|
||||
SetOptionValue(RestoreOptionsHelper.SelectedBackupSets, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The executation mode for the operation. default is execution
|
||||
/// </summary>
|
||||
public TaskExecutionMode TaskExecutionMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Same as Target Database name. Used by task manager to create task info
|
||||
/// </summary>
|
||||
public string DatabaseName
|
||||
{
|
||||
get
|
||||
{
|
||||
return TargetDatabaseName;
|
||||
}
|
||||
set
|
||||
{
|
||||
TargetDatabaseName = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -224,15 +224,10 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
if (restoreDataObject != null)
|
||||
{
|
||||
// create task metadata
|
||||
TaskMetadata metadata = new TaskMetadata();
|
||||
metadata.ServerName = connInfo.ConnectionDetails.ServerName;
|
||||
metadata.DatabaseName = restoreParams.TargetDatabaseName;
|
||||
metadata.Name = SR.RestoreTaskName;
|
||||
metadata.IsCancelable = true;
|
||||
metadata.Data = restoreDataObject;
|
||||
TaskMetadata metadata = TaskMetadata.Create(restoreParams, SR.RestoreTaskName, restoreDataObject, ConnectionServiceInstance);
|
||||
|
||||
// create restore task and perform
|
||||
SqlTask sqlTask = SqlTaskManagerInstance.CreateAndRun(metadata, this.restoreDatabaseService.RestoreTaskAsync, restoreDatabaseService.CancelTaskAsync);
|
||||
SqlTask sqlTask = SqlTaskManagerInstance.CreateAndRun<SqlTask>(metadata);
|
||||
response.TaskId = sqlTask.TaskId.ToString();
|
||||
}
|
||||
else
|
||||
@@ -285,8 +280,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
TaskMetadata metadata = new TaskMetadata();
|
||||
metadata.ServerName = connInfo.ConnectionDetails.ServerName;
|
||||
metadata.DatabaseName = connInfo.ConnectionDetails.DatabaseName;
|
||||
metadata.Data = backupOperation;
|
||||
metadata.IsCancelable = true;
|
||||
metadata.TaskOperation = backupOperation;
|
||||
|
||||
if (backupParams.IsScripting)
|
||||
{
|
||||
@@ -414,7 +408,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
/// <returns></returns>
|
||||
internal async Task<TaskResult> PerformBackupTaskAsync(SqlTask sqlTask)
|
||||
{
|
||||
IBackupOperation backupOperation = sqlTask.TaskMetadata.Data as IBackupOperation;
|
||||
IBackupOperation backupOperation = sqlTask.TaskMetadata.TaskOperation as IBackupOperation;
|
||||
TaskResult result = new TaskResult();
|
||||
|
||||
// Create a task to perform backup
|
||||
@@ -463,7 +457,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
/// <returns></returns>
|
||||
internal async Task<TaskResult> CancelBackupTaskAsync(SqlTask sqlTask)
|
||||
{
|
||||
IBackupOperation backupOperation = sqlTask.TaskMetadata.Data as IBackupOperation;
|
||||
IBackupOperation backupOperation = sqlTask.TaskMetadata.TaskOperation as IBackupOperation;
|
||||
TaskResult result = new TaskResult();
|
||||
|
||||
await Task.Factory.StartNew(() =>
|
||||
|
||||
@@ -7,13 +7,11 @@ using System;
|
||||
using System.Linq;
|
||||
using System.Data.Common;
|
||||
using System.Data.SqlClient;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlServer.Management.Common;
|
||||
using Microsoft.SqlServer.Management.Smo;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
|
||||
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.TaskServices;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
using System.Collections.Concurrent;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
@@ -28,107 +26,6 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
||||
public const string LastBackupTaken = "lastBackupTaken";
|
||||
private ConcurrentDictionary<string, RestoreDatabaseTaskDataObject> sessions = new ConcurrentDictionary<string, RestoreDatabaseTaskDataObject>();
|
||||
|
||||
/// <summary>
|
||||
/// Create a backup task for execution and cancellation
|
||||
/// </summary>
|
||||
/// <param name="sqlTask"></param>
|
||||
/// <returns></returns>
|
||||
internal async Task<TaskResult> RestoreTaskAsync(SqlTask sqlTask)
|
||||
{
|
||||
sqlTask.AddMessage(SR.TaskInProgress, SqlTaskStatus.InProgress, true);
|
||||
RestoreDatabaseTaskDataObject restoreDataObject = sqlTask.TaskMetadata.Data as RestoreDatabaseTaskDataObject;
|
||||
TaskResult taskResult = null;
|
||||
|
||||
if (restoreDataObject != null)
|
||||
{
|
||||
// Create a task to perform backup
|
||||
return await Task.Factory.StartNew(() =>
|
||||
{
|
||||
TaskResult result = new TaskResult();
|
||||
try
|
||||
{
|
||||
if (restoreDataObject.IsValid)
|
||||
{
|
||||
ExecuteRestore(restoreDataObject, sqlTask);
|
||||
result.TaskStatus = SqlTaskStatus.Succeeded;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.TaskStatus = SqlTaskStatus.Failed;
|
||||
if (restoreDataObject.ActiveException != null)
|
||||
{
|
||||
result.ErrorMessage = restoreDataObject.ActiveException.Message;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.ErrorMessage = SR.RestoreNotSupported;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result.TaskStatus = SqlTaskStatus.Failed;
|
||||
result.ErrorMessage = ex.Message;
|
||||
if (ex.InnerException != null)
|
||||
{
|
||||
result.ErrorMessage += Environment.NewLine + ex.InnerException.Message;
|
||||
}
|
||||
if (restoreDataObject != null && restoreDataObject.ActiveException != null)
|
||||
{
|
||||
result.ErrorMessage += Environment.NewLine + restoreDataObject.ActiveException.Message;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
taskResult = new TaskResult();
|
||||
taskResult.TaskStatus = SqlTaskStatus.Failed;
|
||||
}
|
||||
|
||||
return taskResult;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Async task to cancel restore
|
||||
/// </summary>
|
||||
public async Task<TaskResult> CancelTaskAsync(SqlTask sqlTask)
|
||||
{
|
||||
RestoreDatabaseTaskDataObject restoreDataObject = sqlTask.TaskMetadata.Data as RestoreDatabaseTaskDataObject;
|
||||
TaskResult taskResult = null;
|
||||
|
||||
|
||||
if (restoreDataObject != null && restoreDataObject.IsValid)
|
||||
{
|
||||
// Create a task for backup cancellation request
|
||||
return await Task.Factory.StartNew(() =>
|
||||
{
|
||||
|
||||
foreach (Restore restore in restoreDataObject.RestorePlan.RestoreOperations)
|
||||
{
|
||||
restore.Abort();
|
||||
}
|
||||
|
||||
|
||||
return new TaskResult
|
||||
{
|
||||
TaskStatus = SqlTaskStatus.Canceled
|
||||
};
|
||||
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
taskResult = new TaskResult();
|
||||
taskResult.TaskStatus = SqlTaskStatus.Failed;
|
||||
}
|
||||
|
||||
return taskResult;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates response which includes information about the server given to restore (default data location, db names with backupsets)
|
||||
/// </summary>
|
||||
@@ -166,7 +63,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
||||
{
|
||||
if (restoreDataObject != null && restoreDataObject.IsValid)
|
||||
{
|
||||
UpdateRestorePlan(restoreDataObject);
|
||||
restoreDataObject.UpdateRestoreTaskObject();
|
||||
|
||||
if (restoreDataObject != null && restoreDataObject.IsValid)
|
||||
{
|
||||
@@ -227,6 +124,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
||||
response.ErrorMessage += Environment.NewLine;
|
||||
response.ErrorMessage += ex.InnerException.Message;
|
||||
}
|
||||
Logger.Write(LogLevel.Normal, $"Failed to create restore plan. error: { response.ErrorMessage}");
|
||||
}
|
||||
return response;
|
||||
|
||||
@@ -295,63 +193,11 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a restore data object that includes the plan to do the restore operation
|
||||
/// </summary>
|
||||
/// <param name="requestParam"></param>
|
||||
/// <returns></returns>
|
||||
private void UpdateRestorePlan(RestoreDatabaseTaskDataObject restoreDataObject)
|
||||
{
|
||||
bool shouldCreateNewPlan = restoreDataObject.ShouldCreateNewPlan();
|
||||
|
||||
if (!string.IsNullOrEmpty(restoreDataObject.RestoreParams.BackupFilePaths))
|
||||
{
|
||||
restoreDataObject.AddFiles(restoreDataObject.RestoreParams.BackupFilePaths);
|
||||
}
|
||||
restoreDataObject.RestorePlanner.ReadHeaderFromMedia = restoreDataObject.RestoreParams.ReadHeaderFromMedia;
|
||||
|
||||
RestoreOptionFactory.Instance.SetAndValidate(RestoreOptionsHelper.SourceDatabaseName, restoreDataObject);
|
||||
RestoreOptionFactory.Instance.SetAndValidate(RestoreOptionsHelper.TargetDatabaseName, restoreDataObject);
|
||||
|
||||
if (shouldCreateNewPlan)
|
||||
{
|
||||
restoreDataObject.CreateNewRestorePlan();
|
||||
}
|
||||
|
||||
restoreDataObject.UpdateRestorePlan();
|
||||
|
||||
}
|
||||
|
||||
|
||||
private bool CanChangeTargetDatabase(RestoreDatabaseTaskDataObject restoreDataObject)
|
||||
{
|
||||
return DatabaseUtils.IsSystemDatabaseConnection(restoreDataObject.Server.ConnectionContext.DatabaseName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the restore operation
|
||||
/// </summary>
|
||||
/// <param name="requestParam"></param>
|
||||
public void ExecuteRestore(RestoreDatabaseTaskDataObject restoreDataObject, SqlTask sqlTask = null)
|
||||
{
|
||||
// Restore Plan should be already created and updated at this point
|
||||
UpdateRestorePlan(restoreDataObject);
|
||||
|
||||
if (restoreDataObject != null && CanRestore(restoreDataObject))
|
||||
{
|
||||
try
|
||||
{
|
||||
restoreDataObject.SqlTask = sqlTask;
|
||||
restoreDataObject.Execute();
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException(SR.RestoreNotSupported);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ using Microsoft.SqlServer.Management.Smo;
|
||||
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.TaskServices;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
||||
{
|
||||
@@ -61,7 +62,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
||||
/// <summary>
|
||||
/// Includes the plan with all the data required to do a restore operation on server
|
||||
/// </summary>
|
||||
public class RestoreDatabaseTaskDataObject : IRestoreDatabaseTaskDataObject
|
||||
public class RestoreDatabaseTaskDataObject : SmoScriptableTaskOperation, IRestoreDatabaseTaskDataObject
|
||||
{
|
||||
|
||||
private const char BackupMediaNameSeparator = ',';
|
||||
@@ -71,11 +72,12 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
||||
private bool? isTailLogBackupPossible = false;
|
||||
private bool? isTailLogBackupWithNoRecoveryPossible = false;
|
||||
private string backupMediaList = string.Empty;
|
||||
private Server server;
|
||||
|
||||
public RestoreDatabaseTaskDataObject(Server server, String databaseName)
|
||||
{
|
||||
PlanUpdateRequired = true;
|
||||
this.Server = server;
|
||||
this.server = server;
|
||||
this.Util = new RestoreUtil(server);
|
||||
restorePlanner = new DatabaseRestorePlanner(server);
|
||||
|
||||
@@ -105,11 +107,6 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
||||
/// </summary>
|
||||
public string SessionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sql task assigned to the restore object
|
||||
/// </summary>
|
||||
public SqlTask SqlTask { get; set; }
|
||||
|
||||
public string TargetDatabaseName
|
||||
{
|
||||
get
|
||||
@@ -180,7 +177,13 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
||||
/// <summary>
|
||||
/// Current sqlserver instance
|
||||
/// </summary>
|
||||
public Server Server;
|
||||
public override Server Server
|
||||
{
|
||||
get
|
||||
{
|
||||
return server;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recent exception that was thrown
|
||||
@@ -254,23 +257,36 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
||||
|
||||
}
|
||||
|
||||
public override void Execute(TaskExecutionMode mode)
|
||||
{
|
||||
UpdateRestoreTaskObject();
|
||||
|
||||
base.Execute(mode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the restore operations
|
||||
/// </summary>
|
||||
public void Execute()
|
||||
public override void Execute()
|
||||
{
|
||||
RestorePlan restorePlan = GetRestorePlanForExecutionAndScript();
|
||||
|
||||
if (restorePlan != null && restorePlan.RestoreOperations.Count > 0)
|
||||
if (IsValid && RestorePlan.RestoreOperations != null && RestorePlan.RestoreOperations.Any())
|
||||
{
|
||||
restorePlan.PercentComplete += (object sender, PercentCompleteEventArgs e) =>
|
||||
// Restore Plan should be already created and updated at this point
|
||||
|
||||
RestorePlan restorePlan = GetRestorePlanForExecutionAndScript();
|
||||
|
||||
if (restorePlan != null && restorePlan.RestoreOperations.Count > 0)
|
||||
{
|
||||
if (SqlTask != null)
|
||||
restorePlan.PercentComplete += (object sender, PercentCompleteEventArgs e) =>
|
||||
{
|
||||
SqlTask.AddMessage($"{e.Percent}%", SqlTaskStatus.InProgress);
|
||||
}
|
||||
};
|
||||
restorePlan.Execute();
|
||||
OnMessageAdded(new TaskMessage { Description = $"{e.Percent}%", Status = SqlTaskStatus.InProgress });
|
||||
};
|
||||
restorePlan.Execute();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException(SR.RestoreNotSupported);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -657,10 +673,6 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.restorePlan == null)
|
||||
{
|
||||
this.UpdateRestorePlan();
|
||||
}
|
||||
return this.restorePlan;
|
||||
}
|
||||
internal set
|
||||
@@ -797,20 +809,27 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
||||
{
|
||||
Database db = null;
|
||||
List<DbFile> ret = new List<DbFile>();
|
||||
if (!this.RestorePlanner.ReadHeaderFromMedia)
|
||||
try
|
||||
{
|
||||
db = this.Server.Databases[this.RestorePlanner.DatabaseName];
|
||||
if (!this.RestorePlanner.ReadHeaderFromMedia)
|
||||
{
|
||||
db = this.Server.Databases[this.RestorePlanner.DatabaseName];
|
||||
}
|
||||
if (restorePlan != null && restorePlan.RestoreOperations.Count > 0)
|
||||
{
|
||||
if (db != null && db.Status == DatabaseStatus.Normal)
|
||||
{
|
||||
ret = this.Util.GetDbFiles(db);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = this.Util.GetDbFiles(restorePlan.RestoreOperations[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (restorePlan != null && restorePlan.RestoreOperations.Count > 0)
|
||||
catch(Exception ex )
|
||||
{
|
||||
if (db != null && db.Status == DatabaseStatus.Normal)
|
||||
{
|
||||
ret = this.Util.GetDbFiles(db);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = this.Util.GetDbFiles(restorePlan.RestoreOperations[0]);
|
||||
}
|
||||
Logger.Write(LogLevel.Normal, $"Failed to get restore db files. error: {ex.Message}");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -1033,10 +1052,22 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the restore plan error message
|
||||
/// </summary>
|
||||
public override string ErrorMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ActiveException != null)
|
||||
{
|
||||
return ActiveException.Message;
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private bool IsAnyFullBackupSetSelected()
|
||||
{
|
||||
bool isSelected = false;
|
||||
@@ -1210,5 +1241,47 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancels the restore operations
|
||||
/// </summary>
|
||||
public override void Cancel()
|
||||
{
|
||||
foreach (Restore restore in RestorePlan.RestoreOperations)
|
||||
{
|
||||
restore.Abort();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a restore data object that includes the plan to do the restore operation
|
||||
/// </summary>
|
||||
/// <param name="requestParam"></param>
|
||||
/// <returns></returns>
|
||||
internal void UpdateRestoreTaskObject()
|
||||
{
|
||||
bool shouldCreateNewPlan = ShouldCreateNewPlan();
|
||||
|
||||
if (!string.IsNullOrEmpty(RestoreParams.BackupFilePaths) && RestoreParams.ReadHeaderFromMedia)
|
||||
{
|
||||
AddFiles(RestoreParams.BackupFilePaths);
|
||||
}
|
||||
else
|
||||
{
|
||||
RestorePlanner.BackupMediaList.Clear();
|
||||
}
|
||||
RestorePlanner.ReadHeaderFromMedia = RestoreParams.ReadHeaderFromMedia;
|
||||
|
||||
RestoreOptionFactory.Instance.SetAndValidate(RestoreOptionsHelper.SourceDatabaseName, this);
|
||||
RestoreOptionFactory.Instance.SetAndValidate(RestoreOptionsHelper.TargetDatabaseName, this);
|
||||
|
||||
if (shouldCreateNewPlan)
|
||||
{
|
||||
CreateNewRestorePlan();
|
||||
}
|
||||
|
||||
UpdateRestorePlan();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -538,10 +538,15 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
||||
},
|
||||
ValidateFunction = (IRestoreDatabaseTaskDataObject restoreDataObject, object currentValue, object defaultValue) =>
|
||||
{
|
||||
|
||||
string errorMessage = string.Empty;
|
||||
if (currentValue!= null && DatabaseUtils.IsSystemDatabaseConnection(currentValue.ToString()))
|
||||
{
|
||||
errorMessage = "Cannot restore to system database";
|
||||
}
|
||||
return new OptionValidationResult()
|
||||
{
|
||||
IsReadOnly = !restoreDataObject.CanChangeTargetDatabase
|
||||
IsReadOnly = !restoreDataObject.CanChangeTargetDatabase,
|
||||
ErrorMessage = errorMessage
|
||||
};
|
||||
},
|
||||
SetValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject, object value) =>
|
||||
|
||||
Reference in New Issue
Block a user