mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-02-16 18:47:57 -05:00
Agent configuration support classes (WIP) (#632)
* Additional SQL Agent config classes (WIP) * Fix build breaks * Clean up job step code * Add VS Code build files * Move changes to other machine * More of the action execution classes * More execution processing refactors * More refactoring * Disable tests for WIP merge * Fix break on Release config * Stage changes to other machine.
This commit is contained in:
@@ -18,6 +18,7 @@ using Microsoft.SqlServer.Management.Smo;
|
||||
using Microsoft.SqlServer.Management.Smo.Agent;
|
||||
using Microsoft.SqlServer.Management.UI;
|
||||
using Microsoft.SqlTools.ServiceLayer.Admin;
|
||||
using Microsoft.SqlTools.ServiceLayer.Management;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Agent
|
||||
{
|
||||
@@ -567,7 +568,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
|
||||
{
|
||||
this.actions.CloseOnUserCancel = true;
|
||||
this.actions.QuitOnError = true;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -582,19 +582,20 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
|
||||
for (int i = 0; i < this.urnParameters.Length; i++)
|
||||
{
|
||||
Job job = this.smoServer.GetSmoObject(urnParameters[i]) as Job;
|
||||
|
||||
|
||||
string selectedStep = null;
|
||||
|
||||
DataTable dtSteps = GetJobDataSteps(job);
|
||||
if (dtSteps == null || dtSteps.Rows == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dtSteps.Rows.Count > 1) //check if job is multi step job
|
||||
{
|
||||
// selectedStep = ShowStepDialog(job, dtSteps);
|
||||
if (selectedStep == null) //check if the job was canceled
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//Copy the LastRunTime of the job into prevRunTime before the job started.
|
||||
@@ -602,16 +603,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
|
||||
this.actions.AddAction(new StartJobAction(job, selectedStep));
|
||||
this.actions.AddAction(new WaitForJobToFinishAction(job, prevRunTime));
|
||||
}
|
||||
|
||||
if (this.actions.Count <= 0)
|
||||
{
|
||||
//This is a workaround for the class dialog invocation problem
|
||||
//This dialog is invoked from two places
|
||||
//JobsActivityMonitor (JobsPanel.cs) OnStartJobAtStep method
|
||||
// and SSMS Context menu mpu\ssms\shared\SqlMgmt\src\RunningFormsTable.cs method StartFormExecution
|
||||
//It is no way for us to prevent the dialog execution from the context menu (when job was canceled), besides redisgning the architecture
|
||||
//this.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -623,13 +614,14 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
|
||||
private DataTable GetJobDataSteps(Job job)
|
||||
{
|
||||
if (job == null || job.Parent == null || job.Parent.Parent == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// perform an enumerator query to get the steps. We could use the
|
||||
// SMO step object but this is too inefficient as it generates a batch
|
||||
// per step.
|
||||
Request request = new Request();
|
||||
|
||||
request.Fields = new string[] {"Name", "ID", "SubSystem"};
|
||||
request.Urn = job.Urn + "/Step";
|
||||
request.OrderByList = new OrderBy[] {new OrderBy("ID", OrderBy.Direction.Asc)};
|
||||
@@ -716,7 +708,9 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
|
||||
while (!this.abortEvent.WaitOne(WaitForJobToFinishAction.ServerPollingInterval))
|
||||
{
|
||||
if (actions.Progress.IsAborted)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
this.job.Refresh();
|
||||
// If this job hasn't started yet then don't check for its status
|
||||
@@ -725,7 +719,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
|
||||
switch (this.job.CurrentRunStatus)
|
||||
{
|
||||
case JobExecutionStatus.Idle:
|
||||
|
||||
actions.Progress.UpdateActionProgress(index, 100);
|
||||
|
||||
// see if the job succeeded.
|
||||
@@ -744,36 +737,29 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
|
||||
}
|
||||
|
||||
jobFinished = true;
|
||||
|
||||
break;
|
||||
|
||||
case JobExecutionStatus.Suspended:
|
||||
|
||||
actions.Progress.UpdateActionProgress(index, "AgentActionSR.Suspended");
|
||||
break;
|
||||
|
||||
case JobExecutionStatus.BetweenRetries:
|
||||
|
||||
actions.Progress.UpdateActionProgress(index, "AgentActionSR.BetweenRetries");
|
||||
break;
|
||||
|
||||
case JobExecutionStatus.Executing:
|
||||
|
||||
actions.Progress.UpdateActionProgress(index, "AgentActionSR.Executing");
|
||||
break;
|
||||
|
||||
case JobExecutionStatus.PerformingCompletionAction:
|
||||
|
||||
actions.Progress.UpdateActionProgress(index, "AgentActionSR.PerformingCompletionAction");
|
||||
break;
|
||||
|
||||
case JobExecutionStatus.WaitingForStepToFinish:
|
||||
|
||||
actions.Progress.UpdateActionProgress(index, "AgentActionSR.WaitingForStepToFinish");
|
||||
break;
|
||||
|
||||
case JobExecutionStatus.WaitingForWorkerThread:
|
||||
|
||||
actions.Progress.UpdateActionProgress(index, "AgentActionSR.WaitingForWorkerThread");
|
||||
break;
|
||||
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Agent
|
||||
{
|
||||
/// <summary>
|
||||
/// Execution mode enumeration Success if execution succeeded of Failure otherwise for now.
|
||||
/// This enumeration might be refined more as there are needs for it
|
||||
/// </summary>
|
||||
public enum ExecutionMode
|
||||
{
|
||||
/// <summary>
|
||||
/// indicates that the operation failed
|
||||
/// </summary>
|
||||
Failure = 0,
|
||||
|
||||
/// <summary>
|
||||
/// indicates that the operation succeded
|
||||
/// </summary>
|
||||
Success,
|
||||
|
||||
/// <summary>
|
||||
/// indicates that the operation was canceled
|
||||
/// </summary>
|
||||
Cancel
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Agent
|
||||
{
|
||||
public class PreProcessExecutionInfo
|
||||
{
|
||||
private RunType runType;
|
||||
private string script;
|
||||
private PreProcessExecutionInfo() {}
|
||||
|
||||
internal PreProcessExecutionInfo(RunType runType)
|
||||
{
|
||||
this.runType = runType;
|
||||
}
|
||||
|
||||
public RunType RunType
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.runType;
|
||||
}
|
||||
}
|
||||
|
||||
public string Script
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.script;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.script = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// IExecutionAwareSqlControlCollection allows control's container to do pre and post
|
||||
/// processing of the execution commands
|
||||
/// </summary>
|
||||
public interface IExecutionAwareSqlControlCollection : ISqlControlCollection
|
||||
{
|
||||
/// <summary>
|
||||
/// called before dialog's host executes actions on all panels in the dialog one by one.
|
||||
/// If something fails inside this function and the execution should be aborted,
|
||||
/// it can either raise an exception [in which case the framework will show message box with exception text]
|
||||
/// or set executionResult out parameter to be ExecutionMode.Failure
|
||||
/// NOTE: it might be called from worker thread
|
||||
/// </summary>
|
||||
/// <param name="executionInfo">information about execution action</param>
|
||||
/// <param name="executionResult">result of the execution</param>
|
||||
/// <returns>
|
||||
/// true if regular execution should take place, false if everything
|
||||
/// has been done by this function
|
||||
/// NOTE: in case of returning false during scripting operation
|
||||
/// PreProcessExecutionInfo.Script property of executionInfo parameter
|
||||
/// MUST be set by this function [if execution result is success]
|
||||
/// </returns>
|
||||
bool PreProcessExecution(PreProcessExecutionInfo executionInfo, out ExecutionMode executionResult);
|
||||
|
||||
/// <summary>
|
||||
/// called when the host received Cancel request. NOTE: this method can return while
|
||||
/// operation is still being canceled
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// true if the host should do standard cancel for the currently running view or
|
||||
/// false if the Cancel operation was done entirely inside this method and there is nothing
|
||||
/// extra that should be done
|
||||
/// </returns>
|
||||
bool Cancel();
|
||||
|
||||
/// <summary>
|
||||
/// called after dialog's host executes actions on all panels in the dialog one by one
|
||||
/// NOTE: it might be called from worker thread
|
||||
/// </summary>
|
||||
/// <param name="executionMode">result of the execution</param>
|
||||
/// <param name="runType">type of execution</param>
|
||||
void PostProcessExecution(RunType runType, ExecutionMode executionMode);
|
||||
|
||||
/// <summary>
|
||||
/// called before dialog's host executes OnReset method on all panels in the dialog one by one
|
||||
/// NOTE: it might be called from worker thread
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// true if regular execution should take place, false if everything
|
||||
/// has been done by this function
|
||||
/// </returns>
|
||||
bool PreProcessReset();
|
||||
|
||||
/// <summary>
|
||||
/// called after dialog's host executes OnReset method on all panels in the dialog one by one
|
||||
/// NOTE: it might be called from worker thread
|
||||
/// </summary>
|
||||
void PostProcessReset();
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Agent
|
||||
{
|
||||
#region interfaces
|
||||
/// <summary>
|
||||
/// Interface that supports the delegation of individual actions in the progress dialog
|
||||
/// to individual classes.
|
||||
/// </summary>
|
||||
public interface IProgressItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Perform the action for this class
|
||||
/// </summary>
|
||||
/// <param name="actions">Actions collection</param>
|
||||
/// <param name="index">array index of this particular action</param>
|
||||
/// <returns></returns>
|
||||
ProgressStatus DoAction(ProgressItemCollection actions, int index);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Agent
|
||||
{
|
||||
/// <summary>
|
||||
/// defines notion of sitable object
|
||||
/// </summary>
|
||||
public interface IObjectWithSite
|
||||
{
|
||||
void SetSite(System.IServiceProvider sp);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ISqlControlCollection allows access to a collection of dialog views
|
||||
/// </summary>
|
||||
public interface ISqlControlCollection : IObjectWithSite
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,347 +0,0 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Text;
|
||||
using Microsoft.SqlServer.Management.Sdk.Sfc;
|
||||
using Microsoft.SqlServer.Management.Smo;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Agent
|
||||
{
|
||||
// these map to the values for @execution_status
|
||||
// that can be passed to sp_help_job (except the first one!)
|
||||
// also the same as the smo enum JobExecutionStatus.
|
||||
internal enum EnumStatus
|
||||
{
|
||||
All = -1,
|
||||
NotIdleOrSuspended = 0,
|
||||
Executing = 1,
|
||||
WaitingForWorkerThread = 2,
|
||||
BetweenRetries = 3,
|
||||
Idle = 4,
|
||||
Suspended = 5,
|
||||
WaitingForStepToFinish = 6,
|
||||
PerformingCompletionAction = 7
|
||||
}
|
||||
|
||||
//
|
||||
// these values map to CompletionResult values, except the first.
|
||||
//
|
||||
internal enum EnumCompletionResult
|
||||
{
|
||||
All = -1,
|
||||
Failed = 0,
|
||||
Succeeded = 1,
|
||||
Retry = 2,
|
||||
Cancelled = 3,
|
||||
InProgress = 4,
|
||||
Unknown = 5
|
||||
}
|
||||
|
||||
//
|
||||
// for boolean job properties
|
||||
//
|
||||
internal enum EnumThreeState
|
||||
{
|
||||
All,
|
||||
Yes,
|
||||
No
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// JobsFilter class - used to allow user to set filtering options for All Jobs Panel
|
||||
/// </summary>
|
||||
internal class JobActivityFilter : IFilterDefinition
|
||||
{
|
||||
/// <summary>
|
||||
/// constructor
|
||||
/// </summary>
|
||||
public JobActivityFilter()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
#region Properties
|
||||
|
||||
private DateTime lastRunDate = new DateTime();
|
||||
private DateTime nextRunDate = new DateTime();
|
||||
private string name = string.Empty;
|
||||
private string category = string.Empty;
|
||||
private EnumStatus status = EnumStatus.All;
|
||||
private EnumThreeState enabled = EnumThreeState.All;
|
||||
private EnumThreeState runnable = EnumThreeState.All;
|
||||
private EnumThreeState scheduled = EnumThreeState.All;
|
||||
private EnumCompletionResult lastRunOutcome = EnumCompletionResult.All;
|
||||
|
||||
private bool filterdefinitionEnabled = false;
|
||||
|
||||
public EnumCompletionResult LastRunOutcome
|
||||
{
|
||||
get
|
||||
{
|
||||
return lastRunOutcome;
|
||||
}
|
||||
set
|
||||
{
|
||||
lastRunOutcome = value;
|
||||
}
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return name;
|
||||
}
|
||||
set
|
||||
{
|
||||
name = value.Trim();
|
||||
}
|
||||
}
|
||||
|
||||
public EnumThreeState Enabled
|
||||
{
|
||||
get
|
||||
{
|
||||
return enabled;
|
||||
}
|
||||
set
|
||||
{
|
||||
enabled = value;
|
||||
}
|
||||
}
|
||||
|
||||
public EnumStatus Status
|
||||
{
|
||||
get
|
||||
{
|
||||
return status;
|
||||
}
|
||||
set
|
||||
{
|
||||
status = value;
|
||||
}
|
||||
}
|
||||
|
||||
public DateTime LastRunDate
|
||||
{
|
||||
get
|
||||
{
|
||||
return lastRunDate;
|
||||
}
|
||||
set
|
||||
{
|
||||
lastRunDate = value;
|
||||
}
|
||||
}
|
||||
|
||||
public DateTime NextRunDate
|
||||
{
|
||||
get
|
||||
{
|
||||
return nextRunDate;
|
||||
}
|
||||
set
|
||||
{
|
||||
nextRunDate = value;
|
||||
}
|
||||
}
|
||||
|
||||
public string Category
|
||||
{
|
||||
get
|
||||
{
|
||||
return category;
|
||||
}
|
||||
set
|
||||
{
|
||||
category = value.Trim();
|
||||
}
|
||||
}
|
||||
|
||||
public EnumThreeState Runnable
|
||||
{
|
||||
get
|
||||
{
|
||||
return runnable;
|
||||
}
|
||||
set
|
||||
{
|
||||
runnable = value;
|
||||
}
|
||||
}
|
||||
|
||||
public EnumThreeState Scheduled
|
||||
{
|
||||
get
|
||||
{
|
||||
return scheduled;
|
||||
}
|
||||
set
|
||||
{
|
||||
scheduled = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IFilterDefinition - interface implementation
|
||||
/// <summary>
|
||||
/// resets values of this object to default contraint values
|
||||
/// </summary>
|
||||
void IFilterDefinition.ResetToDefault()
|
||||
{
|
||||
lastRunDate = new DateTime();
|
||||
nextRunDate = new DateTime();
|
||||
name = string.Empty;
|
||||
category = string.Empty;
|
||||
enabled = EnumThreeState.All;
|
||||
status = EnumStatus.All;
|
||||
runnable = EnumThreeState.All;
|
||||
scheduled = EnumThreeState.All;
|
||||
lastRunOutcome = EnumCompletionResult.All;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// checks if the filter is the same with the default filter
|
||||
/// </summary>
|
||||
bool IFilterDefinition.IsDefault()
|
||||
{
|
||||
return (lastRunDate.Ticks == 0 &&
|
||||
nextRunDate.Ticks == 0 &&
|
||||
name.Length == 0 &&
|
||||
category.Length == 0 &&
|
||||
enabled == EnumThreeState.All &&
|
||||
status == EnumStatus.All &&
|
||||
runnable == EnumThreeState.All &&
|
||||
scheduled == EnumThreeState.All &&
|
||||
lastRunOutcome == EnumCompletionResult.All);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// creates a shallow clone
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
object IFilterDefinition.ShallowClone()
|
||||
{
|
||||
JobActivityFilter clone = new JobActivityFilter();
|
||||
|
||||
clone.LastRunDate = this.LastRunDate;
|
||||
clone.NextRunDate = this.NextRunDate;
|
||||
clone.Name = this.Name;
|
||||
clone.Category = this.Category;
|
||||
clone.Enabled = this.Enabled;
|
||||
clone.Status = this.Status;
|
||||
clone.Runnable = this.Runnable;
|
||||
clone.Scheduled = this.Scheduled;
|
||||
clone.LastRunOutcome = this.LastRunOutcome;
|
||||
|
||||
(clone as IFilterDefinition).Enabled = (this as IFilterDefinition).Enabled;
|
||||
return clone;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// setup-s filter definition based on a template
|
||||
/// </summary>
|
||||
/// <param name="template"></param>
|
||||
void IFilterDefinition.ShallowCopy(object template)
|
||||
{
|
||||
System.Diagnostics.Debug.Assert(template is JobActivityFilter);
|
||||
|
||||
JobActivityFilter f = template as JobActivityFilter;
|
||||
|
||||
this.LastRunDate = f.LastRunDate;
|
||||
this.NextRunDate = f.NextRunDate;
|
||||
this.Name = f.Name;
|
||||
this.Category = f.Category;
|
||||
this.Enabled = f.Enabled;
|
||||
this.Status = f.Status;
|
||||
this.Runnable = f.Runnable;
|
||||
this.Scheduled = f.Scheduled;
|
||||
this.LastRunOutcome = f.LastRunOutcome;
|
||||
|
||||
(this as IFilterDefinition).Enabled = (template as IFilterDefinition).Enabled;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// tells us if filtering is enabled or diabled
|
||||
/// a disabled filter lets everything pass and filters nothing out
|
||||
/// </summary>
|
||||
bool IFilterDefinition.Enabled
|
||||
{
|
||||
get
|
||||
{
|
||||
return filterdefinitionEnabled;
|
||||
}
|
||||
set
|
||||
{
|
||||
filterdefinitionEnabled = value;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Build filter
|
||||
|
||||
private void AddPrefix(StringBuilder sb, bool clauseAdded)
|
||||
{
|
||||
if (clauseAdded)
|
||||
{
|
||||
sb.Append(" and ( ");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(" ( ");
|
||||
}
|
||||
}
|
||||
|
||||
private void AddSuffix(StringBuilder sb)
|
||||
{
|
||||
sb.Append(" ) ");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// fetch an xpath clause used for filtering
|
||||
/// jobs fetched by the enumerator.
|
||||
/// note that all other properties must be filtered on the client
|
||||
/// because enumerator will not filter properties that are fetched
|
||||
/// at post-process time. We can't even filter on the job name here
|
||||
/// since we have to do a case-insensitive "contains" comparision on the name.
|
||||
/// </summary>
|
||||
public string GetXPathClause()
|
||||
{
|
||||
if (this.enabled == EnumThreeState.All)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
bool clauseAdded = false;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append("[");
|
||||
|
||||
//
|
||||
// enabled clause
|
||||
//
|
||||
if (this.enabled != EnumThreeState.All)
|
||||
{
|
||||
AddPrefix(sb, clauseAdded);
|
||||
sb.Append("@IsEnabled = " + (this.enabled == EnumThreeState.Yes ? "true() " : "false() "));
|
||||
AddSuffix(sb);
|
||||
clauseAdded = true;
|
||||
}
|
||||
|
||||
sb.Append("]");
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,644 +0,0 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Collections;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Xml;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Diagnostics;
|
||||
using System.Collections.Specialized;
|
||||
using Microsoft.SqlServer.Management.Smo;
|
||||
using Microsoft.SqlServer.Management.Common;
|
||||
using Microsoft.SqlServer.Management.Diagnostics;
|
||||
using Microsoft.SqlTools.ServiceLayer.Admin;
|
||||
using Microsoft.SqlTools.ServiceLayer.Agent;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Management
|
||||
{
|
||||
/// <summary>
|
||||
/// base class that can be used to derived from for the main classes [containers] of the dialogs
|
||||
/// </summary>
|
||||
public class ManagementActionBase : IDisposable
|
||||
{
|
||||
#region Members
|
||||
|
||||
/// <summary>
|
||||
/// selected node as specified to SelectNode method
|
||||
/// </summary>
|
||||
//private TreeNode selectedNode;
|
||||
|
||||
/// <summary>
|
||||
/// service provider of our host. We should direct all host-specific requests to the services
|
||||
/// implemented by this provider
|
||||
/// </summary>
|
||||
private IServiceProvider serviceProvider;
|
||||
|
||||
/// <summary>
|
||||
/// data container with initialization-related information
|
||||
/// </summary>
|
||||
private CDataContainer dataContainer;
|
||||
//whether we assume complete ownership over it.
|
||||
//We set this member once the dataContainer is set to be non-null
|
||||
private bool ownDataContainer = true;
|
||||
|
||||
//if derived class tries to call a protected method that relies on service provider,
|
||||
//and the service provider hasn't been set yet, we will cache the values and will
|
||||
//propagate them when we get the provider set
|
||||
//private System.Drawing.Icon cachedIcon = null;
|
||||
private string cachedCaption = null;
|
||||
|
||||
//SMO Server connection that MUST be used for all enumerator calls
|
||||
//We'll get this object out of CDataContainer, that must be initialized
|
||||
//property by the initialization code
|
||||
private ServerConnection serverConnection;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public ManagementActionBase()
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable implementation
|
||||
|
||||
void IDisposable.Dispose()
|
||||
{
|
||||
//BUGBUG - do we need finalizer
|
||||
Dispose(true);//call protected virtual method
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// do the deterministic cleanup
|
||||
/// </summary>
|
||||
/// <param name="disposing"></param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
//dispose CDataContainer if needed
|
||||
if (this.dataContainer != null)
|
||||
{
|
||||
if (this.ownDataContainer)
|
||||
{
|
||||
this.dataContainer.Dispose();
|
||||
}
|
||||
this.dataContainer = null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IObjectWithSite implementation
|
||||
|
||||
public virtual void SetSite(IServiceProvider sp)
|
||||
{
|
||||
if (sp == null)
|
||||
{
|
||||
throw new ArgumentNullException("sp");
|
||||
}
|
||||
|
||||
//allow to be sited only once
|
||||
if (this.serviceProvider == null)
|
||||
{
|
||||
//cache the service provider
|
||||
this.serviceProvider = sp;
|
||||
|
||||
//call protected virtual method to enable derived classes to do initialization
|
||||
//OnHosted();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IExecutionAwareSqlControlCollection implementation
|
||||
|
||||
/// <summary>
|
||||
/// called before dialog's host executes actions on all panels in the dialog one by one.
|
||||
/// If something fails inside this function and the execution should be aborted,
|
||||
/// it can either raise an exception [in which case the framework will show message box with exception text]
|
||||
/// or set executionResult out parameter to be ExecutionMode.Failure
|
||||
/// NOTE: it might be called from worker thread
|
||||
/// </summary>
|
||||
/// <param name="executionInfo">information about execution action</param>
|
||||
/// <param name="executionResult">result of the execution</param>
|
||||
/// <returns>
|
||||
/// true if regular execution should take place, false if everything
|
||||
/// has been done by this function
|
||||
/// NOTE: in case of returning false during scripting operation
|
||||
/// PreProcessExecutionInfo.Script property of executionInfo parameter
|
||||
/// MUST be set by this function [if execution result is success]
|
||||
/// </returns>
|
||||
public bool PreProcessExecution(PreProcessExecutionInfo executionInfo, out ExecutionMode executionResult)
|
||||
{
|
||||
//we start from failure
|
||||
executionResult = ExecutionMode.Failure;
|
||||
|
||||
//OK, we do server switching for scripting for SQL/Analysis Server execution here
|
||||
RunType runType = executionInfo.RunType;
|
||||
if (IsScripting(runType))
|
||||
{
|
||||
if (!PreProcessScripting(executionInfo, out executionResult))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (DataContainer != null)
|
||||
{
|
||||
//we take over execution here. We substitute the server here for AMO and SQL
|
||||
//dialogs
|
||||
if (DataContainer.ContainerServerType == CDataContainer.ServerType.SQL)
|
||||
{
|
||||
ExecuteForSql(executionInfo, out executionResult);
|
||||
return false;//execution of the entire control was done here
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// call virtual function to do regular execution
|
||||
return DoPreProcessExecution(executionInfo.RunType, out executionResult);
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// whether we own our DataContainer or not. Depending on this value it will or won't be
|
||||
/// disposed in our Dispose method
|
||||
/// </summary>
|
||||
protected virtual bool OwnDataContainer
|
||||
{
|
||||
get
|
||||
{
|
||||
//by default we own it
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// called by IExecutionAwareSqlControlCollection.PreProcessExecution to enable derived
|
||||
/// classes to take over execution of the dialog and do entire execution in this method
|
||||
/// rather than having the framework to execute dialog views one by one.
|
||||
///
|
||||
/// NOTE: it might be called from non-UI thread
|
||||
/// </summary>
|
||||
/// <param name="runType"></param>
|
||||
/// <param name="executionResult"></param>
|
||||
/// <returns>
|
||||
/// true if regular execution should take place, false if everything,
|
||||
/// has been done by this function
|
||||
/// </returns>
|
||||
protected virtual bool DoPreProcessExecution(RunType runType, out ExecutionMode executionResult)
|
||||
{
|
||||
//ask the framework to do normal execution by calling OnRunNOw methods
|
||||
//of the views one by one
|
||||
executionResult = ExecutionMode.Success;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// called before dialog's host executes OnReset method on all panels in the dialog one by one
|
||||
/// NOTE: it might be called from worker thread
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// true if regular execution should take place, false if everything
|
||||
/// has been done by this function
|
||||
/// </returns>
|
||||
/// <returns></returns>
|
||||
protected virtual bool DoPreProcessReset()
|
||||
{
|
||||
if ((this.dataContainer != null) && this.dataContainer.IsNewObject)
|
||||
{
|
||||
this.dataContainer.Reset();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// called after dialog's host executes OnReset method on all panels in the dialog one by one
|
||||
/// NOTE: it might be called from worker thread
|
||||
/// </summary>
|
||||
protected virtual void DoPostProcessReset()
|
||||
{
|
||||
//nothing in the base class
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called to intercept scripting operation
|
||||
/// </summary>
|
||||
/// <param name="executionInfo"></param>
|
||||
/// <param name="executionResult"></param>
|
||||
/// <returns>
|
||||
/// true if regular execution should take place, false the script
|
||||
/// has been created by this function
|
||||
/// </returns>
|
||||
protected virtual bool PreProcessScripting(PreProcessExecutionInfo executionInfo, out ExecutionMode executionResult)
|
||||
{
|
||||
//we don't do anything here, but we enable derived classes to do something...
|
||||
executionResult = ExecutionMode.Success;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// CDataContainer accessor
|
||||
/// </summary>
|
||||
protected CDataContainer DataContainer
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.dataContainer;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.dataContainer = value;
|
||||
this.ownDataContainer = OwnDataContainer; //cache the value
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// /// <summary>
|
||||
// /// SMO Server connection that MUST be used for all enumerator calls
|
||||
// /// We'll get this object out of CDataContainer, that must be initialized
|
||||
// /// property by the initialization code
|
||||
// /// </summary>
|
||||
protected ServerConnection ServerConnection
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.serverConnection == null && this.dataContainer != null &&
|
||||
this.dataContainer.ContainerServerType == CDataContainer.ServerType.SQL)
|
||||
{
|
||||
this.serverConnection = this.dataContainer.ServerConnection;
|
||||
}
|
||||
|
||||
//it CAN be null here if dataContainer hasn't been set or the container type is non SQL
|
||||
return this.serverConnection;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// checks whether given run time represents one of scripting options
|
||||
/// </summary>
|
||||
/// <param name="runType"></param>
|
||||
/// <returns></returns>
|
||||
protected static bool IsScripting(RunType runType)
|
||||
{
|
||||
return(runType != RunType.RunNow && runType != RunType.RunNowAndExit);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// calls DoPreProcessExecution and if it returns false, then it will execute all initialized views
|
||||
/// one by one.
|
||||
/// This method allows derived classes to do both preprocessing and normal execution in a way
|
||||
/// that the framework would normally do. Use this method for special cases when derived class
|
||||
/// should really handle entire execution while doing exactly the same actions as the framework
|
||||
/// would do
|
||||
/// </summary>
|
||||
/// <param name="runType"></param>
|
||||
/// <returns>
|
||||
/// result of the execution. It will let exception fly out if it was raised during execution
|
||||
/// </returns>
|
||||
protected ExecutionMode DoPreProcessExecutionAndRunViews(RunType runType)
|
||||
{
|
||||
ExecutionMode executionResult;
|
||||
if (DoPreProcessExecution(runType, out executionResult))
|
||||
{
|
||||
//true return value means that we need to do execution ourselves
|
||||
//executionResult = PanelExecutionHandler.Run(runType, this);
|
||||
}
|
||||
|
||||
return executionResult;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// determines whether we need to substitute SMO/AMO server objects with the
|
||||
/// temporary ones while doing scripting
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private bool NeedToSwitchServer
|
||||
{
|
||||
get
|
||||
{
|
||||
ServerSwitchingAttribute switchingAttrib =
|
||||
Utils.GetCustomAttribute(this, typeof(ServerSwitchingAttribute)) as ServerSwitchingAttribute;
|
||||
|
||||
if (DataContainer == null)
|
||||
{
|
||||
if (switchingAttrib != null)
|
||||
{
|
||||
return switchingAttrib.NeedToSwtichServerObject;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
if (DataContainer.ContainerServerType == CDataContainer.ServerType.SQL)
|
||||
{
|
||||
if (switchingAttrib != null)
|
||||
{
|
||||
return switchingAttrib.NeedToSwtichServerObject;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;//switch by default in SQL case
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual ServerConnection GetServerConnectionForScript()
|
||||
{
|
||||
return this.dataContainer.Server.ConnectionContext;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// builds a script string from SMO string collections. This function should
|
||||
/// probably be moved to SMO. Also we need a way to specify the tsql batch
|
||||
/// separator which for now is GO
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private string BuildSqlScript()
|
||||
{
|
||||
SqlSmoObject sqlDialogSubject = null;
|
||||
try
|
||||
{
|
||||
sqlDialogSubject = this.DataContainer.SqlDialogSubject;
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
//We may not have a valid dialog subject here (such as if the object hasn't been created yet)
|
||||
//so in that case we'll just ignore it as that's a normal scenario.
|
||||
}
|
||||
|
||||
StringCollection sc = GetServerConnectionForScript().CapturedSql.Text;
|
||||
//Scripting may happen on either the server ExecutionManager or the
|
||||
//ExecutionManager of the object itself. So we make sure to check
|
||||
//the subject text if the server ExecutionManager didn't have any
|
||||
//scripts after doing the scripting
|
||||
if (sc.Count == 0 && sqlDialogSubject != null)
|
||||
{
|
||||
sc = sqlDialogSubject.ExecutionManager.ConnectionContext.CapturedSql.Text;
|
||||
}
|
||||
int i;
|
||||
StringBuilder script = new StringBuilder(4096);
|
||||
|
||||
if (sc != null)
|
||||
{
|
||||
for (i = 0; i < sc.Count; i ++)
|
||||
{
|
||||
script.AppendFormat("{0}\r\nGO\r\n", sc[i].ToString());
|
||||
}
|
||||
}
|
||||
|
||||
return script.ToString();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// called when we need to script a Sql server dlg.
|
||||
/// </summary>
|
||||
/// <param name="executionInfo"></param>
|
||||
/// <param name="executionResult"></param>
|
||||
private void ExecuteForSql(PreProcessExecutionInfo executionInfo, out ExecutionMode executionResult)
|
||||
{
|
||||
Microsoft.SqlServer.Management.Smo.Server oldServer = null;
|
||||
if (NeedToSwitchServer)
|
||||
{
|
||||
// We use a new instance of the SMO Server object every time we script
|
||||
// so that any changes that are made to the SMO server while scripting are
|
||||
// not kept when the script operation is completed.
|
||||
oldServer = DataContainer.Server;
|
||||
|
||||
//BUGBUG - see if we can use copy ctor instead
|
||||
DataContainer.Server = new Microsoft.SqlServer.Management.Smo.Server(DataContainer.ServerConnection);
|
||||
}
|
||||
|
||||
String szScript = null;
|
||||
bool isScripting = IsScripting(executionInfo.RunType);
|
||||
var executionModeOriginal = GetServerConnectionForScript().SqlExecutionModes;
|
||||
//For Azure the ExecutionManager is different depending on which ExecutionManager
|
||||
//used - one at the Server level and one at the Database level. So to ensure we
|
||||
//don't use the wrong execution mode we need to set the mode for both (for on-prem
|
||||
//this will essentially be a no-op)
|
||||
SqlExecutionModes subjectExecutionModeOriginal = executionModeOriginal;
|
||||
SqlSmoObject sqlDialogSubject = null;
|
||||
try
|
||||
{
|
||||
sqlDialogSubject = this.DataContainer.SqlDialogSubject;
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
//We may not have a valid dialog subject here (such as if the object hasn't been created yet)
|
||||
//so in that case we'll just ignore it as that's a normal scenario.
|
||||
}
|
||||
|
||||
if (sqlDialogSubject != null)
|
||||
{
|
||||
subjectExecutionModeOriginal = sqlDialogSubject.ExecutionManager.ConnectionContext.SqlExecutionModes;
|
||||
}
|
||||
try
|
||||
{
|
||||
SqlExecutionModes newMode = isScripting
|
||||
? SqlExecutionModes.CaptureSql
|
||||
: SqlExecutionModes.ExecuteSql;
|
||||
//now, do the execution itself
|
||||
GetServerConnectionForScript().SqlExecutionModes = newMode;
|
||||
if (sqlDialogSubject != null)
|
||||
{
|
||||
sqlDialogSubject.ExecutionManager.ConnectionContext.SqlExecutionModes = newMode;
|
||||
}
|
||||
|
||||
executionResult = DoPreProcessExecutionAndRunViews(executionInfo.RunType);
|
||||
|
||||
if (isScripting)
|
||||
{
|
||||
if (executionResult == ExecutionMode.Success)
|
||||
{
|
||||
szScript = BuildSqlScript();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
GetServerConnectionForScript().SqlExecutionModes = executionModeOriginal;
|
||||
|
||||
if (isScripting)
|
||||
{
|
||||
GetServerConnectionForScript().CapturedSql.Clear();
|
||||
}
|
||||
|
||||
if (sqlDialogSubject != null)
|
||||
{
|
||||
sqlDialogSubject.ExecutionManager.ConnectionContext.SqlExecutionModes = subjectExecutionModeOriginal;
|
||||
if (isScripting)
|
||||
{
|
||||
sqlDialogSubject.ExecutionManager.ConnectionContext.CapturedSql.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
//see if we need to restore the server
|
||||
if (oldServer != null)
|
||||
{
|
||||
DataContainer.Server = oldServer;
|
||||
}
|
||||
}
|
||||
|
||||
if (isScripting)
|
||||
{
|
||||
executionInfo.Script = szScript;
|
||||
}
|
||||
}
|
||||
|
||||
// #region ICustomAttributeProvider
|
||||
// object[] System.Reflection.ICustomAttributeProvider.GetCustomAttributes(bool inherit)
|
||||
// {
|
||||
// //we merge attributes from 2 sources: type attributes and the derived classes, giving preference
|
||||
// //to the derived classes
|
||||
// return GetMergedArray(DoGetCustomAttributes(inherit), GetType().GetCustomAttributes(inherit));
|
||||
// }
|
||||
// object[] System.Reflection.ICustomAttributeProvider.GetCustomAttributes(Type attributeType, bool inherit)
|
||||
// {
|
||||
// //we merge attributes from 2 sources: type attributes and the derived classes, giving preference
|
||||
// //to the derived classes
|
||||
// return GetMergedArray(DoGetCustomAttributes(attributeType, inherit),
|
||||
// GetType().GetCustomAttributes(attributeType, inherit));
|
||||
// }
|
||||
// bool System.Reflection.ICustomAttributeProvider.IsDefined(Type attributeType, bool inherit)
|
||||
// {
|
||||
// //we merge attributes from 2 sources: type attributes and the derived classes, giving preference
|
||||
// //to the derived classes
|
||||
// if (!DoIsDefined(attributeType, inherit))
|
||||
// {
|
||||
// return GetType().IsDefined(attributeType, inherit);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
// #endregion
|
||||
// #region ICustomAttributeProvider helpers
|
||||
// protected virtual object[] DoGetCustomAttributes(bool inherit)
|
||||
// {
|
||||
// return GetMergedArray(DoGetCustomAttributes(typeof(ScriptTypeAttribute), inherit),
|
||||
// DoGetCustomAttributes(typeof(DialogScriptableAttribute), inherit));
|
||||
// }
|
||||
// protected virtual object[] DoGetCustomAttributes(Type attributeType, bool inherit)
|
||||
// {
|
||||
// //if the type specifies this attribute, we don't bother - it overrides
|
||||
// //our behavior
|
||||
// object[] typeAttribs = GetType().GetCustomAttributes(attributeType, inherit);
|
||||
// if (typeAttribs != null && typeAttribs.Length > 0)
|
||||
// {
|
||||
// return null;
|
||||
// }
|
||||
// //we expose default custom attribute for script type
|
||||
// if (attributeType.Equals(typeof(ScriptTypeAttribute)))
|
||||
// {
|
||||
// string scriptType = ScriptType;
|
||||
// if (scriptType != null)
|
||||
// {
|
||||
// return new object[] {new ScriptTypeAttribute(scriptType)};
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// return null;
|
||||
// }
|
||||
// }
|
||||
// else if (attributeType.Equals(typeof(DialogScriptableAttribute)))
|
||||
// {
|
||||
// bool canScriptToWindow = true;
|
||||
// bool canScriptToFile = true;
|
||||
// bool canScriptToClipboard = true;
|
||||
// bool canScriptToJob = true;
|
||||
// GetScriptableOptions(out canScriptToWindow,
|
||||
// out canScriptToFile,
|
||||
// out canScriptToClipboard,
|
||||
// out canScriptToJob);
|
||||
// return new object[] {new DialogScriptableAttribute(canScriptToWindow,
|
||||
// canScriptToFile,
|
||||
// canScriptToClipboard,
|
||||
// canScriptToJob)};
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
// protected virtual bool DoIsDefined(Type attributeType, bool inherit)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// /// <summary>
|
||||
// /// detects whether script types are applicable for this dlg or not. By default
|
||||
// /// the framework relies on DialogScriptableAttribute set on the dlg class and won't
|
||||
// /// call this method if the attribute is specified
|
||||
// /// By default we assume that all script types are enabled
|
||||
// /// </summary>
|
||||
// /// <param name="?"></param>
|
||||
// /// <param name="?"></param>
|
||||
// /// <param name="?"></param>
|
||||
// protected virtual void GetScriptableOptions(out bool canScriptToWindow,
|
||||
// out bool canScriptToFile,
|
||||
// out bool canScriptToClipboard,
|
||||
// out bool canScriptToJob)
|
||||
// {
|
||||
// canScriptToWindow = canScriptToFile = canScriptToClipboard = canScriptToJob = true;
|
||||
// }
|
||||
// #endregion
|
||||
// protected IServiceProvider ServiceProvider
|
||||
// {
|
||||
// get
|
||||
// {
|
||||
// if (this.serviceProvider == null)
|
||||
// {
|
||||
// STrace.Assert(false, "Cannot work without service provider!");
|
||||
// STrace.LogExThrow();
|
||||
// //BUGBUG - should we have our own exception here?
|
||||
// throw new InvalidOperationException();
|
||||
// }
|
||||
// return this.serviceProvider;
|
||||
// }
|
||||
// }
|
||||
// /// <summary>
|
||||
// /// returns combination of the given 2 arrays
|
||||
// /// </summary>
|
||||
// /// <param name="array1"></param>
|
||||
// /// <param name="array2"></param>
|
||||
// /// <returns></returns>
|
||||
// protected object[] GetMergedArray(object[] array1, object[] array2)
|
||||
// {
|
||||
// if (array1 == null)
|
||||
// {
|
||||
// return array2;
|
||||
// }
|
||||
// else if (array2 == null)
|
||||
// {
|
||||
// return array1;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// object[] finalReturnValue = new object[array1.Length + array2.Length];
|
||||
// array1.CopyTo(finalReturnValue, 0);
|
||||
// array2.CopyTo(finalReturnValue, array1.Length);
|
||||
// return finalReturnValue;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
@@ -1,331 +0,0 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Collections;
|
||||
using System.Threading;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Agent
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows for the mapping of objects that implement IProgressItem to individual items in the
|
||||
/// progress dialog.
|
||||
/// </summary>
|
||||
public class ProgressItemCollection : ICollection
|
||||
{
|
||||
#region internal helper classes
|
||||
/// <summary>
|
||||
/// Allows us to map an action to its index in the progress dialog.
|
||||
/// </summary>
|
||||
public class ActionIndexMap
|
||||
{
|
||||
/// <summary>
|
||||
/// action
|
||||
/// </summary>
|
||||
public IProgressItem Action;
|
||||
/// <summary>
|
||||
/// index
|
||||
/// </summary>
|
||||
public int Index;
|
||||
|
||||
public ActionIndexMap(IProgressItem action)
|
||||
{
|
||||
this.Action = action;
|
||||
// index isn't known yet
|
||||
this.Index = -1;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region private data members
|
||||
/// <summary>
|
||||
/// list of actions we will perform.
|
||||
/// </summary>
|
||||
private ArrayList actions = new ArrayList();
|
||||
#endregion
|
||||
|
||||
#region construction
|
||||
public ProgressItemCollection()
|
||||
{
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region properties
|
||||
|
||||
private bool closeOnUserCancel = false;
|
||||
/// <summary>
|
||||
/// Indicates whether to close the dialog immediately if the user cancels an operation
|
||||
/// </summary>
|
||||
public bool CloseOnUserCancel
|
||||
{
|
||||
get
|
||||
{
|
||||
return closeOnUserCancel;
|
||||
}
|
||||
set
|
||||
{
|
||||
closeOnUserCancel = value;
|
||||
}
|
||||
}
|
||||
|
||||
private bool automaticClose = false;
|
||||
/// <summary>
|
||||
/// Indicates whether to automatically close the dialog when all actions are complete
|
||||
/// successfully.
|
||||
/// </summary>
|
||||
public bool CloseOnSuccessfulCompletion
|
||||
{
|
||||
get
|
||||
{
|
||||
return automaticClose;
|
||||
}
|
||||
set
|
||||
{
|
||||
automaticClose = value;
|
||||
}
|
||||
}
|
||||
|
||||
private bool quitOnError = false;
|
||||
/// <summary>
|
||||
/// Indicates whether the operation should be terminated if any individual step fails.
|
||||
/// </summary>
|
||||
public bool QuitOnError
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.quitOnError;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.quitOnError = value;
|
||||
}
|
||||
}
|
||||
private OperationStatus operationStatus = OperationStatus.Invalid;
|
||||
/// <summary>
|
||||
/// Indicates the status of the operation.
|
||||
/// </summary>
|
||||
public OperationStatus OperationStatus
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.operationStatus;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Progress object this action collection will work with
|
||||
/// </summary>
|
||||
private IProgress progress = null;
|
||||
public IProgress Progress
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.progress;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (this.progress != value)
|
||||
{
|
||||
this.progress = value;
|
||||
if (this.progress != null)
|
||||
{
|
||||
// add the actions to the progress dialog, and
|
||||
// fixup our event handler
|
||||
FixUpActionsToProgress();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region public overrides
|
||||
/// <summary>
|
||||
/// Generate a string representaion of this object. It will convert all of it's IProgressItem members
|
||||
/// to strings in a new line.
|
||||
/// </summary>
|
||||
/// <returns>string description of the actions this object contains</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
// if there are no actions then just return the default ToString
|
||||
if (this.actions == null || this.actions.Count == 0)
|
||||
{
|
||||
return base.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
// convert all of the actions to strings on their own line
|
||||
StringBuilder sb = new StringBuilder(((ActionIndexMap)actions[0]).Action.ToString());
|
||||
for (int i = 1; i < this.actions.Count; i++)
|
||||
{
|
||||
sb.AppendFormat(CultureInfo.InvariantCulture, "\r\n{0}", ((ActionIndexMap)actions[i]).Action.ToString());
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ICollection implementation
|
||||
/// <summary>
|
||||
/// Gets the number of actions in this collection
|
||||
/// </summary>
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.actions.Count;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// not supported
|
||||
/// </summary>
|
||||
public bool IsSynchronized
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// not supported
|
||||
/// </summary>
|
||||
public object SyncRoot
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
public void CopyTo(IProgressItem[] array, int start)
|
||||
{
|
||||
this.actions.CopyTo(array, start);
|
||||
}
|
||||
public void CopyTo(Array array, int start)
|
||||
{
|
||||
this.actions.CopyTo(array, start);
|
||||
}
|
||||
public IEnumerator GetEnumerator()
|
||||
{
|
||||
return this.actions.GetEnumerator();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region public methods
|
||||
/// <summary>
|
||||
/// Add an action to the collection
|
||||
/// </summary>
|
||||
/// <param name="action">action to be added</param>
|
||||
public void AddAction(IProgressItem action)
|
||||
{
|
||||
ActionIndexMap map = new ActionIndexMap(action);
|
||||
this.actions.Add(map);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region internal implementation
|
||||
/// <summary>
|
||||
/// delegate called when the progress dialog wants us to perform work on a new thread.
|
||||
/// </summary>
|
||||
private void DoWorkOnThread()
|
||||
{
|
||||
if (this.Progress == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
System.Threading.Thread.CurrentThread.Name = "Worker thread for " + progress.GetType();
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{ }
|
||||
|
||||
// default to succeeded.
|
||||
operationStatus = OperationStatus.Success;
|
||||
|
||||
// carry out each action.
|
||||
foreach (ActionIndexMap map in this.actions)
|
||||
{
|
||||
// abort if the user has decided to cancel.
|
||||
if (this.Progress.IsAborted)
|
||||
{
|
||||
this.Progress.UpdateActionStatus(map.Index, ProgressStatus.Aborted);
|
||||
operationStatus = OperationStatus.Aborted;
|
||||
break;
|
||||
}
|
||||
ProgressStatus stepStatus = ProgressStatus.Invalid;
|
||||
try
|
||||
{
|
||||
// perform the action.
|
||||
stepStatus = map.Action.DoAction(this, map.Index);
|
||||
this.Progress.UpdateActionStatus(map.Index, stepStatus);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// fail the step with errors, add the error messages to the control.
|
||||
this.Progress.AddActionException(map.Index, e);
|
||||
this.Progress.UpdateActionStatus(map.Index, ProgressStatus.Error);
|
||||
stepStatus = ProgressStatus.Error;
|
||||
}
|
||||
if (stepStatus == ProgressStatus.Error)
|
||||
{
|
||||
// see if we're supposed to fail if any step fails
|
||||
if (this.QuitOnError == true)
|
||||
{
|
||||
// fail and quit
|
||||
this.operationStatus = OperationStatus.Error;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.operationStatus = OperationStatus.CompletedWithErrors;
|
||||
}
|
||||
}
|
||||
else if (stepStatus != ProgressStatus.Success)
|
||||
{
|
||||
this.operationStatus = OperationStatus.CompletedWithErrors;
|
||||
}
|
||||
}
|
||||
|
||||
// tell the dialog we're finishing.
|
||||
this.Progress.WorkerThreadExiting(operationStatus);
|
||||
|
||||
// close the dialog if asked to. We have to put this after
|
||||
// the WorkerThreadExiting call because the progress dialog
|
||||
// won't allow itself to be closed until worker thread says
|
||||
// it's finished.
|
||||
if ((this.CloseOnSuccessfulCompletion && (this.operationStatus == OperationStatus.Success)) ||
|
||||
(this.CloseOnUserCancel && this.Progress.IsAborted))
|
||||
{
|
||||
//((Form)this.Progress).BeginInvoke(new CloseProgressWindowCallback(CloseProgressWindowHandler), new object[] { this.Progress });
|
||||
}
|
||||
}
|
||||
|
||||
private delegate void CloseProgressWindowCallback(IProgress progress);
|
||||
private void CloseProgressWindowHandler(IProgress progress)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the actions to an IProgress interface.
|
||||
/// </summary>
|
||||
private void FixUpActionsToProgress()
|
||||
{
|
||||
if (this.Progress == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// add actions
|
||||
foreach (ActionIndexMap map in this.actions)
|
||||
{
|
||||
map.Index = this.Progress.AddAction(map.Action.ToString());
|
||||
}
|
||||
// add our delegate
|
||||
this.Progress.WorkerThreadStart = new ThreadStart(this.DoWorkOnThread);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,301 +0,0 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Threading;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Agent
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumeration for status of individual actions
|
||||
/// </summary>
|
||||
public enum ProgressStatus
|
||||
{
|
||||
Invalid = -1,
|
||||
NotStarted, // Not started
|
||||
InProgress, // In progress
|
||||
Success, // Completed
|
||||
SuccessWithInfo, // Completed, display additional info
|
||||
Warning, // Completed with warning, display exceptions
|
||||
Error, // Not completed because of error, display exceptions
|
||||
Aborted, // Aborted
|
||||
RolledBack, // Rolled back because of a subsequent error
|
||||
StatusCount // = Number of status values - For validation only
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumeration for status of the overall operation
|
||||
/// </summary>
|
||||
public enum OperationStatus
|
||||
{
|
||||
Invalid = -1,
|
||||
InProgress, // In progress
|
||||
Success, // Completed successfully
|
||||
CompletedWithErrors, // Completed with non-fatal errors
|
||||
Error, // Not completed because of error
|
||||
Aborted, // Abort complete
|
||||
StatusCount // = Number of status values - For validation only
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface defining core functionality of a progress control container
|
||||
///
|
||||
/// NOTE: Refer to the comments for each method/property individually to determine
|
||||
/// whether it is thread-safe and may be used from the worker thread. Also note
|
||||
/// that some members are asynchronous.
|
||||
/// </summary>
|
||||
public interface IProgress
|
||||
{
|
||||
//-----------
|
||||
// Properties
|
||||
//-----------
|
||||
|
||||
/// <summary>
|
||||
/// The property that determines if the user should be allowed to abort the
|
||||
/// operation. The default value is true.
|
||||
///
|
||||
/// NOTE: This property is not thread safe. Set from UI thread.
|
||||
/// </summary>
|
||||
bool AllowAbort
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The confirmation prompt to display if the user hits the Abort button.
|
||||
/// The abort prompt should be worded such that the abort is confirmed
|
||||
/// if the user hits "Yes".
|
||||
///
|
||||
/// NOTE: This property is not thread safe. Set from UI thread.
|
||||
/// </summary>
|
||||
string AbortPrompt
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The ThreadStart delegate. The method referenced by this delegate is run by
|
||||
/// the worker thread to perform the operation.
|
||||
///
|
||||
/// NOTE: This property is not thread safe. Set from UI thread.
|
||||
/// </summary>
|
||||
ThreadStart WorkerThreadStart
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Aborted status of the operation.
|
||||
///
|
||||
/// NOTE: This property is thread safe and may be called from the worker
|
||||
/// thread. Accessing this property may cause caller to block if UI
|
||||
/// is waiting for user confirmation of abort.
|
||||
/// </summary>
|
||||
bool IsAborted
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This property determines whether updates should be allowed for
|
||||
/// actions. If this is set to false, any calls that are made
|
||||
/// to add or update an action are ignored. The default value is true.
|
||||
///
|
||||
/// NOTE: This property is thread safe and may be called from the worker
|
||||
/// thread.
|
||||
/// </summary>
|
||||
bool ActionUpdateEnabled
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
//--------
|
||||
// Methods
|
||||
//--------
|
||||
|
||||
/// <summary>
|
||||
/// Add an action to the displayed list of actions. Actions are
|
||||
/// displayed in the order they are added. An action can be referenced
|
||||
/// in future calls using the zero-based index that is returned.
|
||||
///
|
||||
/// The description must be a non-empty string.
|
||||
///
|
||||
/// NOTE: This method is not thread safe. Call from the UI thread.
|
||||
/// </summary>
|
||||
/// <param name="description">Description of the action</param>
|
||||
/// <returns>The index of the newly added action.</returns>
|
||||
int AddAction(string description);
|
||||
|
||||
/// <summary>
|
||||
/// Add an action to the displayed list of actions. This is meant
|
||||
/// to be called from the worker thread to add an action after
|
||||
/// the operation is already in progress.
|
||||
///
|
||||
/// Actions are displayed in the order they are added. Use the
|
||||
/// zero-based index of the action based on past actions added
|
||||
/// to reference it in future calls. The description must be a
|
||||
/// non-empty string.
|
||||
///
|
||||
/// NOTE: This method is thread safe and asynchronous. It may be
|
||||
/// called from the worker thread.
|
||||
/// </summary>
|
||||
/// <param name="description">Description of the action</param>
|
||||
void AddActionDynamic(string description);
|
||||
|
||||
/// <summary>
|
||||
/// Update the description of an action
|
||||
///
|
||||
/// The description must be a non-empty string.
|
||||
///
|
||||
/// NOTE: This method is thread safe and asynchronous. It may be
|
||||
/// called from the worker thread.
|
||||
/// </summary>
|
||||
/// <param name="actionIndex">Index of the action</param>
|
||||
/// <param name="description">New description of the action</param>
|
||||
void UpdateActionDescription(int actionIndex, string description);
|
||||
|
||||
/// <summary>
|
||||
/// Update the status of an action
|
||||
///
|
||||
/// NOTE: This method is thread safe and asynchronous. It may be
|
||||
/// called from the worker thread.
|
||||
/// </summary>
|
||||
/// <param name="actionIndex">Index of the action</param>
|
||||
/// <param name="status">New status of the action</param>
|
||||
void UpdateActionStatus(int actionIndex, ProgressStatus status);
|
||||
|
||||
/// <summary>
|
||||
/// Update the progress of an action in terms of percentage complete
|
||||
///
|
||||
/// NOTE: This method is thread safe and asynchronous. It may be
|
||||
/// called from the worker thread.
|
||||
/// </summary>
|
||||
/// <param name="actionIndex">Index of the action</param>
|
||||
/// <param name="percentComplete">Percentage of the action that is complete (0-100)</param>
|
||||
void UpdateActionProgress(int actionIndex, int percentComplete);
|
||||
|
||||
/// <summary>
|
||||
/// Update the progress of an action with a text description of
|
||||
/// the progress
|
||||
///
|
||||
/// The description must be a non-empty string.
|
||||
///
|
||||
/// NOTE: This method is thread safe and asynchronous. It may be
|
||||
/// called from the worker thread.
|
||||
/// </summary>
|
||||
/// <param name="actionIndex">Index of the action</param>
|
||||
/// <param name="description">Description of progress</param>
|
||||
void UpdateActionProgress(int actionIndex, string description);
|
||||
|
||||
/// <summary>
|
||||
/// Add an exception to an action
|
||||
///
|
||||
/// Exceptions are displayed in the action grid only for actions
|
||||
/// with "Error" or "Warning" status.
|
||||
///
|
||||
/// NOTE: This method is thread safe and asynchronous. It may be
|
||||
/// called from the worker thread.
|
||||
/// </summary>
|
||||
/// <param name="actionIndex">Index of the action</param>
|
||||
/// <param name="e">Exception to be added</param>
|
||||
void AddActionException(int actionIndex, Exception e);
|
||||
|
||||
/// <summary>
|
||||
/// Add an info string to an action in the progress report control
|
||||
///
|
||||
/// Information strings are displayed in the action grid only for
|
||||
/// actions with "SuccessWithInfo" status. The info string must
|
||||
/// be a non-empty string. It should not be formatted or contain
|
||||
/// newline characters.
|
||||
///
|
||||
/// NOTE: This method is thread safe and asynchronous. It may be
|
||||
/// called from the worker thread.
|
||||
/// </summary>
|
||||
/// <param name="actionIndex">Index of the action</param>
|
||||
/// <param name="infoString">Information string to be added</param>
|
||||
void AddActionInfoString(int actionIndex, string infoString);
|
||||
|
||||
/// <summary>
|
||||
/// Call this method when the worker thread performing the operation
|
||||
/// is about to exit. The final result of the operation is supplied in
|
||||
/// the form of a OperationStatus value.
|
||||
///
|
||||
/// NOTE: This method is thread safe and asynchronous. It may be
|
||||
/// called from the worker thread.
|
||||
/// </summary>
|
||||
/// <param name="result">Result of the operation</param>
|
||||
void WorkerThreadExiting(OperationStatus result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumeration for status of the progress report control w.r.t the operation
|
||||
/// </summary>
|
||||
[System.Runtime.InteropServices.ComVisible(false)]
|
||||
public enum ProgressCtrlStatus
|
||||
{
|
||||
Invalid = -1,
|
||||
InProgress, // In progress
|
||||
Success, // Completed successfully
|
||||
CompletedWithErrors, // Completed with non-fatal errors
|
||||
Error, // Not completed because of error
|
||||
Aborting, // User clicked "Abort", aborting operation
|
||||
Aborted, // Abort complete
|
||||
Closed, // User clicked "Close"
|
||||
StatusCount // = Number of status values - For validation only
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delegate used with ProgressCtrlStatusChanged event.
|
||||
/// </summary>
|
||||
public delegate void ProgressCtrlStatusChangedEventHandler(object source, ProgressCtrlStatusChangedEventArgs e);
|
||||
|
||||
/// <summary>
|
||||
/// EventArgs class for use with ProgressCtrlStatusChanged event
|
||||
/// </summary>
|
||||
sealed public class ProgressCtrlStatusChangedEventArgs : EventArgs
|
||||
{
|
||||
//------------------
|
||||
// Public Properties
|
||||
//------------------
|
||||
public ProgressCtrlStatus Status
|
||||
{
|
||||
get { return m_status; }
|
||||
set { m_status = value; }
|
||||
}
|
||||
|
||||
//-------------
|
||||
// Private Data
|
||||
//-------------
|
||||
private ProgressCtrlStatus m_status = ProgressCtrlStatus.Invalid;
|
||||
}
|
||||
|
||||
// Enumeration for progress action grid columns
|
||||
internal enum ProgressActionColumn
|
||||
{
|
||||
Invalid = -1,
|
||||
ActionStatusBitmap,
|
||||
ActionDescription,
|
||||
ActionStatusText,
|
||||
ActionMessage,
|
||||
ActionColumnCount // = Number of columns - For validation only
|
||||
}
|
||||
|
||||
// Enumeration for progress action display filter
|
||||
internal enum ProgressActionDisplayMode
|
||||
{
|
||||
Invalid = -1,
|
||||
DisplayAllActions,
|
||||
DisplayErrors,
|
||||
DisplaySuccess,
|
||||
DisplayWarnings,
|
||||
ActionDisplayModeCount // = Number of display modes - For validation only
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Agent
|
||||
{
|
||||
/// <summary>
|
||||
/// what type of actions does the worker know to execute
|
||||
/// </summary>
|
||||
public enum RunType
|
||||
{
|
||||
RunNow = 0,
|
||||
RunNowAndExit,
|
||||
ScriptToFile,
|
||||
ScriptToWindow,
|
||||
ScriptToClipboard,
|
||||
ScriptToJob
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Agent
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom attribute that can be applied on particular DB commander to
|
||||
/// indicate whether the base class should switch SMO servers before
|
||||
/// execution or not.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public sealed class ServerSwitchingAttribute : Attribute
|
||||
{
|
||||
private bool needToSwitch = false;
|
||||
|
||||
private ServerSwitchingAttribute() {}
|
||||
|
||||
public ServerSwitchingAttribute(bool needToSwitchServer)
|
||||
{
|
||||
this.needToSwitch = needToSwitchServer;
|
||||
}
|
||||
|
||||
public bool NeedToSwtichServerObject
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.needToSwitch;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user