File Browser: Adding async task exception handling (#504)

* Replacing Dictionary with ConcurrentDictionary since values are accessed in async contexts

* Adding new method to allow async tasks to be executed in the exception continuation

* Adding unit tests for the aforementioned

* Adding exception handling to async tasks in file browser service

* Updating query execution async handling to use the async version

* Removing unnecesary send result from continuewithonfaulted
This commit is contained in:
Benjamin Russell
2017-10-19 11:25:29 -07:00
committed by GitHub
parent 4b66203dfc
commit 9600125186
5 changed files with 220 additions and 107 deletions

View File

@@ -4,8 +4,7 @@
//
using System;
using System.Collections.Generic;
using System.Data;
using System.Collections.Concurrent;
using System.Data.Common;
using System.Data.SqlClient;
using System.Threading.Tasks;
@@ -14,6 +13,7 @@ using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
using Microsoft.SqlTools.ServiceLayer.FileBrowser.Contracts;
using Microsoft.SqlTools.ServiceLayer.Hosting;
using Microsoft.SqlTools.ServiceLayer.Utility;
namespace Microsoft.SqlTools.ServiceLayer.FileBrowser
{
@@ -26,9 +26,9 @@ namespace Microsoft.SqlTools.ServiceLayer.FileBrowser
public static FileBrowserService Instance => LazyInstance.Value;
// Cache file browser operations for expanding node request
private Dictionary<string, FileBrowserOperation> ownerToFileBrowserMap = new Dictionary<string, FileBrowserOperation>();
private Dictionary<string, ValidatePathsCallback> validatePathsCallbackMap = new Dictionary<string, ValidatePathsCallback>();
private ConnectionService connectionService = null;
private readonly ConcurrentDictionary<string, FileBrowserOperation> ownerToFileBrowserMap = new ConcurrentDictionary<string, FileBrowserOperation>();
private readonly ConcurrentDictionary<string, ValidatePathsCallback> validatePathsCallbackMap = new ConcurrentDictionary<string, ValidatePathsCallback>();
private ConnectionService connectionService;
/// <summary>
/// Signature for callback method that validates the selected file paths
@@ -52,23 +52,6 @@ namespace Microsoft.SqlTools.ServiceLayer.FileBrowser
}
}
/// <summary>
/// Service host object for sending/receiving requests/events.
/// Internal for testing purposes.
/// </summary>
internal IProtocolEndpoint ServiceHost
{
get;
set;
}
/// <summary>
/// Constructor
/// </summary>
public FileBrowserService()
{
}
/// <summary>
/// Register validate path callback
/// </summary>
@@ -76,23 +59,15 @@ namespace Microsoft.SqlTools.ServiceLayer.FileBrowser
/// <param name="callback"></param>
public void RegisterValidatePathsCallback(string service, ValidatePathsCallback callback)
{
if (this.validatePathsCallbackMap.ContainsKey(service))
{
this.validatePathsCallbackMap.Remove(service);
}
this.validatePathsCallbackMap.Add(service, callback);
validatePathsCallbackMap.AddOrUpdate(service, callback, (key, oldValue) => callback);
}
/// <summary>
/// Initializes the service instance
/// </summary>
/// <param name="serviceHost"></param>
/// <param name="context"></param>
/// <param name="serviceHost">Service host to register handlers with</param>
public void InitializeService(ServiceHost serviceHost)
{
this.ServiceHost = serviceHost;
// Open a file browser
serviceHost.SetRequestHandler(FileBrowserOpenRequest.Type, HandleFileBrowserOpenRequest);
@@ -108,13 +83,12 @@ namespace Microsoft.SqlTools.ServiceLayer.FileBrowser
#region request handlers
internal async Task HandleFileBrowserOpenRequest(
FileBrowserOpenParams fileBrowserParams,
RequestContext<bool> requestContext)
internal async Task HandleFileBrowserOpenRequest(FileBrowserOpenParams fileBrowserParams, RequestContext<bool> requestContext)
{
try
{
var task = Task.Run(() => RunFileBrowserOpenTask(fileBrowserParams));
var task = Task.Run(() => RunFileBrowserOpenTask(fileBrowserParams, requestContext))
.ContinueWithOnFaulted(null);
await requestContext.SendResult(true);
}
catch
@@ -123,13 +97,12 @@ namespace Microsoft.SqlTools.ServiceLayer.FileBrowser
}
}
internal async Task HandleFileBrowserExpandRequest(
FileBrowserExpandParams fileBrowserParams,
RequestContext<bool> requestContext)
internal async Task HandleFileBrowserExpandRequest(FileBrowserExpandParams fileBrowserParams, RequestContext<bool> requestContext)
{
try
{
var task = Task.Run(() => RunFileBrowserExpandTask(fileBrowserParams));
var task = Task.Run(() => RunFileBrowserExpandTask(fileBrowserParams, requestContext))
.ContinueWithOnFaulted(null);
await requestContext.SendResult(true);
}
catch
@@ -138,13 +111,12 @@ namespace Microsoft.SqlTools.ServiceLayer.FileBrowser
}
}
internal async Task HandleFileBrowserValidateRequest(
FileBrowserValidateParams fileBrowserParams,
RequestContext<bool> requestContext)
internal async Task HandleFileBrowserValidateRequest(FileBrowserValidateParams fileBrowserParams, RequestContext<bool> requestContext)
{
try
{
var task = Task.Run(() => RunFileBrowserValidateTask(fileBrowserParams));
var task = Task.Run(() => RunFileBrowserValidateTask(fileBrowserParams, requestContext))
.ContinueWithOnFaulted(null);
await requestContext.SendResult(true);
}
catch
@@ -158,22 +130,15 @@ namespace Microsoft.SqlTools.ServiceLayer.FileBrowser
RequestContext<FileBrowserCloseResponse> requestContext)
{
FileBrowserCloseResponse response = new FileBrowserCloseResponse();
if (this.ownerToFileBrowserMap.ContainsKey(fileBrowserParams.OwnerUri))
{
this.ownerToFileBrowserMap.Remove(fileBrowserParams.OwnerUri);
response.Succeeded = true;
}
else
{
response.Succeeded = false;
}
FileBrowserOperation removedOperation;
response.Succeeded = ownerToFileBrowserMap.TryRemove(fileBrowserParams.OwnerUri, out removedOperation);
await requestContext.SendResult(response);
}
#endregion
internal async Task RunFileBrowserOpenTask(FileBrowserOpenParams fileBrowserParams)
internal async Task RunFileBrowserOpenTask(FileBrowserOpenParams fileBrowserParams, RequestContext<bool> requestContext)
{
FileBrowserOpenedParams result = new FileBrowserOpenedParams();
@@ -189,7 +154,7 @@ namespace Microsoft.SqlTools.ServiceLayer.FileBrowser
connInfo.TryGetConnection(ConnectionType.Default, out dbConn);
if (dbConn != null)
{
conn = ReliableConnectionHelper.GetAsSqlConnection((IDbConnection)dbConn);
conn = ReliableConnectionHelper.GetAsSqlConnection(dbConn);
}
}
@@ -198,11 +163,7 @@ namespace Microsoft.SqlTools.ServiceLayer.FileBrowser
FileBrowserOperation browser = new FileBrowserOperation(conn, fileBrowserParams.ExpandPath, fileBrowserParams.FileFilters);
browser.PopulateFileTree();
if (this.ownerToFileBrowserMap.ContainsKey(fileBrowserParams.OwnerUri))
{
this.ownerToFileBrowserMap.Remove(fileBrowserParams.OwnerUri);
}
this.ownerToFileBrowserMap.Add(fileBrowserParams.OwnerUri, browser);
ownerToFileBrowserMap.AddOrUpdate(fileBrowserParams.OwnerUri, browser, (key, value) => browser);
result.OwnerUri = fileBrowserParams.OwnerUri;
result.FileTree = browser.FileTree;
@@ -219,25 +180,21 @@ namespace Microsoft.SqlTools.ServiceLayer.FileBrowser
result.Message = ex.Message;
}
await ServiceHost.SendEvent(FileBrowserOpenedNotification.Type, result);
await requestContext.SendEvent(FileBrowserOpenedNotification.Type, result);
}
internal async Task RunFileBrowserExpandTask(FileBrowserExpandParams fileBrowserParams)
internal async Task RunFileBrowserExpandTask(FileBrowserExpandParams fileBrowserParams, RequestContext<bool> requestContext)
{
FileBrowserExpandedParams result = new FileBrowserExpandedParams();
try
{
if (this.ownerToFileBrowserMap.ContainsKey(fileBrowserParams.OwnerUri))
FileBrowserOperation browser;
result.Succeeded = ownerToFileBrowserMap.TryGetValue(fileBrowserParams.OwnerUri, out browser);
if (result.Succeeded && browser != null)
{
FileBrowserOperation browser = this.ownerToFileBrowserMap[fileBrowserParams.OwnerUri];
result.Children = browser.GetChildren(fileBrowserParams.ExpandPath).ToArray();
result.ExpandPath = fileBrowserParams.ExpandPath;
result.OwnerUri = fileBrowserParams.OwnerUri;
result.Succeeded = true;
}
else
{
result.Succeeded = false;
}
}
catch (Exception ex)
@@ -246,22 +203,23 @@ namespace Microsoft.SqlTools.ServiceLayer.FileBrowser
result.Message = ex.Message;
}
await ServiceHost.SendEvent(FileBrowserExpandedNotification.Type, result);
await requestContext.SendEvent(FileBrowserExpandedNotification.Type, result);
}
internal async Task RunFileBrowserValidateTask(FileBrowserValidateParams fileBrowserParams)
internal async Task RunFileBrowserValidateTask(FileBrowserValidateParams fileBrowserParams, RequestContext<bool> requestContext)
{
FileBrowserValidatedParams result = new FileBrowserValidatedParams();
try
{
if (this.validatePathsCallbackMap.ContainsKey(fileBrowserParams.ServiceType)
&& this.validatePathsCallbackMap[fileBrowserParams.ServiceType] != null
ValidatePathsCallback callback;
if (validatePathsCallbackMap.TryGetValue(fileBrowserParams.ServiceType, out callback)
&& callback != null
&& fileBrowserParams.SelectedFiles != null
&& fileBrowserParams.SelectedFiles.Length > 0)
{
string errorMessage;
result.Succeeded = this.validatePathsCallbackMap[fileBrowserParams.ServiceType](new FileBrowserValidateEventArgs
result.Succeeded = callback(new FileBrowserValidateEventArgs
{
ServiceType = fileBrowserParams.ServiceType,
OwnerUri = fileBrowserParams.OwnerUri,
@@ -284,7 +242,7 @@ namespace Microsoft.SqlTools.ServiceLayer.FileBrowser
result.Message = ex.Message;
}
await ServiceHost.SendEvent(FileBrowserValidatedNotification.Type, result);
await requestContext.SendEvent(FileBrowserValidatedNotification.Type, result);
}
}
}