mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-02-13 11:38:33 -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:
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Management
|
||||
{
|
||||
internal enum ConfigAction
|
||||
{
|
||||
Create,
|
||||
Update,
|
||||
Drop
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,344 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Text;
|
||||
using Microsoft.SqlServer.Management.Diagnostics;
|
||||
using Microsoft.SqlTools.ServiceLayer.Agent;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Management
|
||||
{
|
||||
/// <summary>
|
||||
/// defines mandatory interface that an action must implement
|
||||
/// </summary>
|
||||
public interface IManagementAction
|
||||
{
|
||||
ExecutionMode LastExecutionResult { get; }
|
||||
|
||||
/// <summary>
|
||||
/// performs custom action when user requests to cancel execution.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
void OnCancel(object sender);
|
||||
|
||||
/// <summary>
|
||||
/// Overridable function that allow a derived class to implement its
|
||||
/// OnScript functionality.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <returns>text of the generated script</returns>
|
||||
string OnScript(object sender);
|
||||
|
||||
/// <summary>
|
||||
/// Overridable function that allow a derived class to implement its
|
||||
/// OnRunNow functionality
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
void OnRunNow(object sender);
|
||||
|
||||
/// <summary>
|
||||
/// Overridable function that allow a derived class to implement
|
||||
/// a finalizing action after a RunNow or RunNowAndClose were sucesfully executed
|
||||
/// NOTE: same as OnGatherUiInformation, this method is always called from UI thread
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
void OnTaskCompleted(object sender, ExecutionMode executionMode, RunType executionType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This class is responsible for executing panels one by one.
|
||||
/// It is reused by ViewSwitcherControlsManager and treepanelform classes
|
||||
/// </summary>
|
||||
internal class ExecutionHandlerDelegate
|
||||
{
|
||||
private object cancelCriticalSection = new object();
|
||||
private IManagementAction managementAction;
|
||||
|
||||
public ExecutionHandlerDelegate(IManagementAction managementAction)
|
||||
{
|
||||
this.managementAction = managementAction;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name="runType"></param>
|
||||
/// <param name="sender"></param>
|
||||
/// <returns>execution result</returns>
|
||||
public ExecutionMode Run(RunType runType, object sender)
|
||||
{
|
||||
//dispatch the call to the right method
|
||||
switch (runType)
|
||||
{
|
||||
case RunType.RunNow:
|
||||
this.managementAction.OnRunNow(sender);
|
||||
break;
|
||||
|
||||
case RunType.ScriptToWindow:
|
||||
this.managementAction.OnScript(sender);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException("SRError.UnexpectedRunType");
|
||||
}
|
||||
|
||||
if((this.managementAction.LastExecutionResult == ExecutionMode.Failure) ||
|
||||
(this.managementAction.LastExecutionResult == ExecutionMode.Cancel))
|
||||
{
|
||||
return this.managementAction.LastExecutionResult;
|
||||
}
|
||||
|
||||
// if we're here, everything went fine
|
||||
return ExecutionMode.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// performs custom action wen user requests a cancel
|
||||
/// this is called from the UI thread
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
public void Cancel(object sender)
|
||||
{
|
||||
lock (this.cancelCriticalSection)
|
||||
{
|
||||
this.managementAction.OnCancel(sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// manager that hooks up tree view with the individual views
|
||||
/// </summary>
|
||||
internal sealed class ExecutonHandler : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// handler that we delegate execution related tasks to
|
||||
/// </summary>
|
||||
private ExecutionHandlerDelegate panelExecutionHandler;
|
||||
|
||||
/// <summary>
|
||||
/// class that describes available views
|
||||
/// </summary>
|
||||
private ISqlControlCollection viewsHolder;
|
||||
|
||||
/// <summary>
|
||||
/// class that describes available views that is also aware of execution
|
||||
/// </summary>
|
||||
private IExecutionAwareManagementAction managementAction;
|
||||
|
||||
/// <summary>
|
||||
/// result of the last execution
|
||||
/// </summary>
|
||||
private ExecutionMode executionResult;
|
||||
|
||||
/// <summary>
|
||||
/// text of the generated script if RunNow method was called last time with scripting option
|
||||
/// </summary>
|
||||
private StringBuilder script;
|
||||
|
||||
/// <summary>
|
||||
/// index of the panel that is being executed
|
||||
/// </summary>
|
||||
private int currentlyExecutingPanelIndex;
|
||||
|
||||
/// <summary>
|
||||
/// creates instance of the class and returns service provider that aggregates the provider
|
||||
/// provider with extra services
|
||||
/// </summary>
|
||||
/// <param name="serviceProvider">service provider from the host</param>
|
||||
/// <param name="aggregatedProvider">
|
||||
/// aggregates service provider that is derived from the host service provider and
|
||||
/// is extended with extra services and/or overriden services. The host should be
|
||||
/// using this provider whenever it has to specify an IServiceProvider to a component
|
||||
/// that will be managed by this class
|
||||
/// </param>
|
||||
public ExecutonHandler(IExecutionAwareManagementAction managementAction)
|
||||
{
|
||||
this.managementAction = managementAction;
|
||||
this.panelExecutionHandler = new ExecutionHandlerDelegate(managementAction);
|
||||
}
|
||||
|
||||
#region public interface
|
||||
|
||||
public ExecutionMode ExecutionResult
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.executionResult;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// text of the generated script if RunNow method was called last time with scripting option
|
||||
/// </summary>
|
||||
public string ScriptTextFromLastRun
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.script != null)
|
||||
{
|
||||
return this.script.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// we call the run now implementaion of the management action.
|
||||
/// If any exception is generated we stop the execution and we set the execution mode flag to failure.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
public void RunNow(RunType runType, object sender)
|
||||
{
|
||||
try
|
||||
{
|
||||
// reset some internal vars
|
||||
this.executionResult = ExecutionMode.Failure;
|
||||
this.currentlyExecutingPanelIndex = -1; // will become 0 if we're executing on view by view basis
|
||||
|
||||
// ensure that we have valid StringBulder for scripting
|
||||
if (IsScripting(runType))
|
||||
{
|
||||
EnsureValidScriptBuilder();
|
||||
}
|
||||
|
||||
// do preprocess action. It is possible to do entire execution from inside this method
|
||||
if (this.managementAction != null)
|
||||
{
|
||||
PreProcessExecutionInfo preProcessInfo = new PreProcessExecutionInfo(runType);
|
||||
if (!this.managementAction.PreProcessExecution(preProcessInfo, out this.executionResult))
|
||||
{
|
||||
// In case of scripting preProcessInfo.Script must contain text of the script
|
||||
if (executionResult == ExecutionMode.Success && IsScripting(runType) && preProcessInfo.Script != null)
|
||||
{
|
||||
this.script.Append(preProcessInfo.Script);
|
||||
}
|
||||
|
||||
return; // result of execution is in executionResult
|
||||
}
|
||||
}
|
||||
// NOTE: post process action is done in finally block below
|
||||
|
||||
// start executing
|
||||
this.executionResult = this.panelExecutionHandler.Run(runType, sender);
|
||||
}
|
||||
#region error handling
|
||||
|
||||
catch (OutOfMemoryException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (System.Threading.ThreadAbortException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
this.executionResult = ExecutionMode.Cancel;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ProcessExceptionDuringExecution(e, this.currentlyExecutingPanelIndex);
|
||||
|
||||
return;
|
||||
}
|
||||
finally
|
||||
{
|
||||
//do postprocess action
|
||||
if (this.managementAction != null)
|
||||
{
|
||||
this.managementAction.PostProcessExecution(runType, this.executionResult);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Kicks off Cancel operation
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
public void InitiateCancel(object sender)
|
||||
{
|
||||
if (this.managementAction != null)
|
||||
{
|
||||
if (!this.managementAction.Cancel())
|
||||
{
|
||||
//everything was done inside this method
|
||||
this.executionResult = ExecutionMode.Cancel;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//otherwise do cancel ourselves
|
||||
// if everything goes OK, Run() method will return with Cancel result
|
||||
this.panelExecutionHandler.Cancel(sender);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// is called by the host to do post execution actions
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="executionMode"></param>
|
||||
/// <param name="executionType"></param>
|
||||
public void OnTaskCompleted(object sender, ExecutionMode executionResult, RunType executionType)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// enables deterministic cleanup
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
IDisposable managementActionAsDisposable = this.managementAction as IDisposable;
|
||||
if (managementActionAsDisposable != null)
|
||||
{
|
||||
managementActionAsDisposable.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region private helpers
|
||||
|
||||
/// <summary>
|
||||
/// determines whether given run type corresponds to scripting or not
|
||||
/// </summary>
|
||||
/// <param name="runType"></param>
|
||||
/// <returns></returns>
|
||||
private bool IsScripting(RunType runType)
|
||||
{
|
||||
return (runType == RunType.ScriptToClipboard ||
|
||||
runType == RunType.ScriptToFile ||
|
||||
runType == RunType.ScriptToWindow ||
|
||||
runType == RunType.ScriptToJob);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ensure that we have valid StringBulder for scripting
|
||||
/// </summary>
|
||||
private void EnsureValidScriptBuilder()
|
||||
{
|
||||
if (this.script == null)
|
||||
{
|
||||
this.script = new StringBuilder(256);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.script.Length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// helper function that is called when we caught an exception during execution
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
/// <param name="failedViewIndex">-1 indicates that we don't know</param>
|
||||
private void ProcessExceptionDuringExecution(Exception e, int failedViewIndex)
|
||||
{
|
||||
//show the error
|
||||
this.executionResult = ExecutionMode.Failure;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// 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.Management
|
||||
{
|
||||
/// <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
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
//
|
||||
// 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.Management
|
||||
{
|
||||
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 IExecutionAwareManagementAction : ISqlControlCollection, IManagementAction
|
||||
{
|
||||
/// <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 host executes actions
|
||||
/// </summary>
|
||||
/// <param name="executionMode">result of the execution</param>
|
||||
/// <param name="runType">type of execution</param>
|
||||
void PostProcessExecution(RunType runType, ExecutionMode executionMode);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// 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.Management
|
||||
{
|
||||
#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
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// 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.Management
|
||||
{
|
||||
/// <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
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,784 @@
|
||||
//
|
||||
// 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.Collections;
|
||||
using System.Collections.Specialized;
|
||||
using System.Data;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using Microsoft.SqlServer.Management.Common;
|
||||
using Microsoft.SqlServer.Management.Diagnostics;
|
||||
using Microsoft.SqlServer.Management.Smo;
|
||||
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
|
||||
/// </summary>
|
||||
public class ManagementActionBase : IDisposable, IExecutionAwareManagementAction
|
||||
{
|
||||
#region Members
|
||||
|
||||
/// <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;
|
||||
|
||||
private ExecutionHandlerDelegate cachedPanelExecutionHandler;
|
||||
|
||||
#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 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 SQL containers
|
||||
if (DataContainer.ContainerServerType == CDataContainer.ServerType.SQL)
|
||||
{
|
||||
ExecuteForSql(executionInfo, out executionResult);
|
||||
return false; // execution of the entire action was done here
|
||||
}
|
||||
}
|
||||
|
||||
// call virtual function to do regular execution
|
||||
return DoPreProcessExecution(executionInfo.RunType, out executionResult);
|
||||
}
|
||||
|
||||
/// <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>
|
||||
public void PostProcessExecution(RunType runType, ExecutionMode executionResult)
|
||||
{
|
||||
//delegate to the protected virtual method
|
||||
DoPostProcessExecution(runType, 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>
|
||||
public bool Cancel()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#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 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>
|
||||
protected virtual void DoPostProcessExecution(RunType runType, ExecutionMode executionResult)
|
||||
{
|
||||
//nothing to do in the base class
|
||||
}
|
||||
|
||||
/// <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 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;
|
||||
}
|
||||
|
||||
StringBuilder script = new StringBuilder(4096);
|
||||
if (sc != null)
|
||||
{
|
||||
for (int 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 action
|
||||
/// </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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// returns internal helper class that we delegate execution of the panels one by one when
|
||||
/// we do it ourselves during scripting
|
||||
/// </summary>
|
||||
private ExecutionHandlerDelegate PanelExecutionHandler
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.cachedPanelExecutionHandler == null)
|
||||
{
|
||||
this.cachedPanelExecutionHandler = new ExecutionHandlerDelegate(this);
|
||||
}
|
||||
return this.cachedPanelExecutionHandler;
|
||||
}
|
||||
}
|
||||
|
||||
// #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)
|
||||
{
|
||||
|
||||
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;
|
||||
// }
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// execution mode by default for now is success
|
||||
/// </summary>
|
||||
private ExecutionMode m_executionMode = ExecutionMode.Success;
|
||||
|
||||
/// <summary>
|
||||
/// execution mode accessor
|
||||
/// </summary>
|
||||
protected ExecutionMode ExecutionMode
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_executionMode;
|
||||
}
|
||||
set
|
||||
{
|
||||
m_executionMode = value;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual ExecutionMode LastExecutionResult
|
||||
{
|
||||
get
|
||||
{
|
||||
return ExecutionMode;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overridable function that allow a derived class to implement
|
||||
/// a finalizing action after a RunNow or RunNowAndClose where sucesfully executed
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
public virtual void OnTaskCompleted(object sender, ExecutionMode executionMode, RunType executionType)
|
||||
{
|
||||
//nothing
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Overridable function that allow a derived class to implement its
|
||||
/// OnRunNow functionality
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
public virtual void OnRunNow(object sender)
|
||||
{
|
||||
//nothing
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overridable function that allow a derived class to implement its
|
||||
/// OnScript functionality.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
public virtual string OnScript(object sender)
|
||||
{
|
||||
//redirect to the single scripting virtual method by default
|
||||
return Script();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// derived class should override this method if it does same action for all types of scripting,
|
||||
/// because all ILaunchFormHostedControl scripting methods implemented in this class simply
|
||||
/// call this method
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected virtual string Script()
|
||||
{
|
||||
// redirect to the RunNow method. Our host should be turning script capture on and off for
|
||||
// OLAP/SQL servers and composing the text of the resulting script by itself
|
||||
OnRunNow(this);
|
||||
|
||||
// null is a special value. It means that we want to indicate that we didn't want to generate
|
||||
// script text
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// performs custom action wen user requests a cancel
|
||||
/// this is called from the UI thread and generally executes
|
||||
/// smoServer.Cancel() or amoServer.Cancel() causing
|
||||
/// the worker thread to inttrerupt its current action
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
public virtual void OnCancel(object sender)
|
||||
{
|
||||
if (this.dataContainer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.dataContainer.Server != null)
|
||||
{
|
||||
// TODO: uncomment next line when SMO server will have support for Cancel
|
||||
// this.dataContainer.Server.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using Microsoft.SqlTools.ServiceLayer.TaskServices;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Management
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility functions for working with Management classes
|
||||
/// </summary>
|
||||
public static class ManagementUtils
|
||||
{
|
||||
public static RunType asRunType(TaskExecutionMode taskExecutionMode)
|
||||
{
|
||||
if (taskExecutionMode == TaskExecutionMode.Script)
|
||||
{
|
||||
return RunType.ScriptToWindow;
|
||||
}
|
||||
else
|
||||
{
|
||||
return RunType.RunNow;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,331 @@
|
||||
//
|
||||
// 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.Management
|
||||
{
|
||||
/// <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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
//
|
||||
// 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.Management
|
||||
{
|
||||
/// <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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// 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.Management
|
||||
{
|
||||
/// <summary>
|
||||
/// what type of actions does the worker know to execute
|
||||
/// </summary>
|
||||
public enum RunType
|
||||
{
|
||||
RunNow = 0,
|
||||
RunNowAndExit,
|
||||
ScriptToFile,
|
||||
ScriptToWindow,
|
||||
ScriptToClipboard,
|
||||
ScriptToJob
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// 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.Management
|
||||
{
|
||||
/// <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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// //
|
||||
// // Copyright (c) Microsoft. All rights reserved.
|
||||
// // Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
// //
|
||||
|
||||
// using System;
|
||||
|
||||
// /// <summary>
|
||||
// /// Defines static values for both Yukon and Shiloh
|
||||
// /// </summary>
|
||||
// namespace Microsoft.SqlTools.ServiceLayer.Management
|
||||
// {
|
||||
// public class SqlLimits
|
||||
// {
|
||||
// internal SqlLimits()
|
||||
// {
|
||||
// }
|
||||
|
||||
// //Define Version 9 Limits
|
||||
// //Currently MAX is the same for both nVarchar and Varchar.
|
||||
// public static readonly int VarcharMax = 1073741824;
|
||||
// public static readonly int SysName = 128;
|
||||
|
||||
// //Define Pre-Version 9 Limits
|
||||
// public static readonly int CommandDimensionMaxLength=3200;
|
||||
// }
|
||||
// }
|
||||
Reference in New Issue
Block a user