From caf196ea31a49bb9383d0476f4d22e77def1b088 Mon Sep 17 00:00:00 2001 From: Kevin Cunnane Date: Wed, 25 Oct 2017 12:06:00 -0700 Subject: [PATCH] Should await result of requestcontext.sendresult in case it errors (#520) - Refactored to use pattern where we wait on the binding queue action to complete, then send the result / error outside of that. --- .../Localization/sr.cs | 11 ++ .../Localization/sr.resx | 4 + .../Localization/sr.strings | 2 + .../Localization/sr.xlf | 5 + .../Scripting/ScriptingException.cs | 28 ++++ .../Scripting/ScriptingService.cs | 125 ++++++++++++------ 6 files changed, 131 insertions(+), 44 deletions(-) create mode 100644 src/Microsoft.SqlTools.ServiceLayer/Scripting/ScriptingException.cs diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs index 9cfdb072..da92b2f8 100755 --- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs @@ -2405,6 +2405,14 @@ namespace Microsoft.SqlTools.ServiceLayer } } + public static string ScriptingUnexpectedError + { + get + { + return Keys.GetString(Keys.ScriptingUnexpectedError); + } + } + public static string unavailable { get @@ -4600,6 +4608,9 @@ namespace Microsoft.SqlTools.ServiceLayer public const string ScriptingListObjectsCompleteParams_ConnectionString_Property_Invalid = "ScriptingListObjectsCompleteParams_ConnectionString_Property_Invalid"; + public const string ScriptingUnexpectedError = "ScriptingUnexpectedError"; + + public const string unavailable = "unavailable"; diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx index 0c7d3c4b..da0e2b8c 100755 --- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx +++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx @@ -1380,6 +1380,10 @@ Error parsing ScriptingListObjectsCompleteParams.ConnectionString property. + + Unexpected error while scripting: no result returned from script operation. + + Unavailable diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings index 1d7d1c16..307814b3 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings +++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings @@ -690,6 +690,8 @@ ScriptingParams_FilePath_Property_Invalid = Invalid directory specified by the S ScriptingListObjectsCompleteParams_ConnectionString_Property_Invalid = Error parsing ScriptingListObjectsCompleteParams.ConnectionString property. +ScriptingUnexpectedError = Unexpected error while scripting: no result returned from script operation. + diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf index 84b7d2c9..2fedb3b0 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf +++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf @@ -2311,6 +2311,11 @@ . Parameters: 0 - value (string), 1 - columnType (string) + + Unexpected error while scripting: no result returned from script operation. + Unexpected error while scripting: no result returned from script operation. + + \ No newline at end of file diff --git a/src/Microsoft.SqlTools.ServiceLayer/Scripting/ScriptingException.cs b/src/Microsoft.SqlTools.ServiceLayer/Scripting/ScriptingException.cs new file mode 100644 index 00000000..1de0bd6d --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/Scripting/ScriptingException.cs @@ -0,0 +1,28 @@ +// +// 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.Scripting +{ + public class ScriptingException : Exception + { + public ScriptingException() + : base() + { + } + + public ScriptingException(string message, Exception exception) + : base(message, exception) + { + } + + + public ScriptingException(string message) + : base(message) + { + } + } +} \ No newline at end of file diff --git a/src/Microsoft.SqlTools.ServiceLayer/Scripting/ScriptingService.cs b/src/Microsoft.SqlTools.ServiceLayer/Scripting/ScriptingService.cs index abefea30..f59da1f2 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Scripting/ScriptingService.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Scripting/ScriptingService.cs @@ -23,6 +23,7 @@ using Microsoft.SqlServer.Management.Smo; using Microsoft.SqlServer.Management.Common; using Microsoft.SqlServer.Management.Sdk.Sfc; using Microsoft.SqlTools.ServiceLayer.Utility; +using Microsoft.SqlTools.ServiceLayer.LanguageServices; namespace Microsoft.SqlTools.ServiceLayer.Scripting { @@ -227,55 +228,86 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting /// Runs the async task that performs the scripting operation. /// private void RunSelectTask(ConnectionInfo connInfo, ScriptingParams parameters, RequestContext requestContext) - { - ConnectionServiceInstance.ConnectionQueue.QueueBindingOperation( - key: ConnectionServiceInstance.ConnectionQueue.AddConnectionContext(connInfo, "Scripting"), - bindingTimeout: ScriptingOperationTimeout, - bindOperation: (bindingContext, cancelToken) => + { + Task.Run(() => + { + try { - string script = string.Empty; - ScriptingObject scriptingObject = parameters.ScriptingObjects[0]; - try - { - Server server = new Server(bindingContext.ServerConnection); - server.DefaultTextMode = true; - - // build object URN - SqlConnectionStringBuilder connectionStringBuilder = new SqlConnectionStringBuilder(parameters.ConnectionString); - Urn objectUrn = BuildScriptingObjectUrn(server, connectionStringBuilder, scriptingObject); - string typeName = objectUrn.GetNameForType(scriptingObject.Type); - - // select from service broker - if (string.Compare(typeName, "ServiceBroker", StringComparison.CurrentCultureIgnoreCase) == 0) + QueueItem queueItem = ConnectionServiceInstance.ConnectionQueue.QueueBindingOperation( + key: ConnectionServiceInstance.ConnectionQueue.AddConnectionContext(connInfo, "Scripting"), + bindingTimeout: ScriptingOperationTimeout, + bindOperation: (bindingContext, cancelToken) => { - script = Scripter.SelectAllValuesFromTransmissionQueue(objectUrn); - } - - // select from queues - else if (string.Compare(typeName, "Queues", StringComparison.CurrentCultureIgnoreCase) == 0 || - string.Compare(typeName, "SystemQueues", StringComparison.CurrentCultureIgnoreCase) == 0) - { - script = Scripter.SelectAllValues(objectUrn); - } - - // select from table or view - else - { - Database db = server.Databases[connectionStringBuilder.InitialCatalog]; - bool isDw = db.IsSqlDw; - script = new Scripter().SelectFromTableOrView(server, objectUrn, isDw); - } - - // send script result to client - requestContext.SendResult(new ScriptingResult { Script = script }); - } - catch (Exception e) + return DoScriptSelect(connInfo, parameters, bindingContext); + }); + + queueItem.ItemProcessed.WaitOne(); + var result = queueItem.GetResultAsT(); + if (result == null) { - requestContext.SendError(e); + result = new ScriptingResultWithException() + { + Exception = new ScriptingException(SR.ScriptingUnexpectedError) + }; } + if (result.Exception == null) + { + requestContext.SendResult(result).Wait(); + } + else + { + requestContext.SendError(result.Exception); + } + } + catch (Exception e) + { + requestContext.SendError(e); + } + }).ContinueWithOnFaulted(null); + } - return null; - }); + private ScriptingResult DoScriptSelect(ConnectionInfo connInfo, ScriptingParams parameters, IBindingContext bindingContext) + { + try + { + string script = string.Empty; + ScriptingObject scriptingObject = parameters.ScriptingObjects[0]; + Server server = new Server(bindingContext.ServerConnection); + server.DefaultTextMode = true; + + // build object URN + SqlConnectionStringBuilder connectionStringBuilder = new SqlConnectionStringBuilder(parameters.ConnectionString); + Urn objectUrn = BuildScriptingObjectUrn(server, connectionStringBuilder, scriptingObject); + string typeName = objectUrn.GetNameForType(scriptingObject.Type); + + // select from service broker + if (string.Compare(typeName, "ServiceBroker", StringComparison.CurrentCultureIgnoreCase) == 0) + { + script = Scripter.SelectAllValuesFromTransmissionQueue(objectUrn); + } + + // select from queues + else if (string.Compare(typeName, "Queues", StringComparison.CurrentCultureIgnoreCase) == 0 || + string.Compare(typeName, "SystemQueues", StringComparison.CurrentCultureIgnoreCase) == 0) + { + script = Scripter.SelectAllValues(objectUrn); + } + + // select from table or view + else + { + Database db = server.Databases[connectionStringBuilder.InitialCatalog]; + bool isDw = db.IsSqlDw; + script = new Scripter().SelectFromTableOrView(server, objectUrn, isDw); + } + + // send script result to client + return new ScriptingResult { Script = script }; + } + catch (Exception e) + { + return new ScriptingResultWithException() { Exception = e}; + } } /// @@ -317,5 +349,10 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting } } } + + internal class ScriptingResultWithException : ScriptingResult + { + public Exception Exception { get; set; } + } } } \ No newline at end of file