Merge pull request #13 from Microsoft/feature/queryExecutionV1_cr

ConnectionService ISqlConnection -> DbConnection
This commit is contained in:
Benjamin Russell
2016-08-03 14:56:20 -07:00
committed by GitHub
23 changed files with 347 additions and 142 deletions

View File

@@ -5,9 +5,11 @@
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.SqlClient;
using System.Threading.Tasks;
using Microsoft.SqlTools.EditorServices.Utility;
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
using Microsoft.SqlTools.ServiceLayer.Hosting;
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
@@ -63,15 +65,15 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
/// <summary>
/// Active connections lazy dictionary instance
/// </summary>
private Lazy<Dictionary<int, ISqlConnection>> activeConnections
= new Lazy<Dictionary<int, ISqlConnection>>(()
=> new Dictionary<int, ISqlConnection>());
private readonly Lazy<Dictionary<int, DbConnection>> activeConnections
= new Lazy<Dictionary<int, DbConnection>>(()
=> new Dictionary<int, DbConnection>());
/// <summary>
/// Callback for onconnection handler
/// </summary>
/// <param name="sqlConnection"></param>
public delegate Task OnConnectionHandler(ISqlConnection sqlConnection);
public delegate Task OnConnectionHandler(DbConnection sqlConnection);
/// <summary>
/// List of onconnection handlers
@@ -81,7 +83,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
/// <summary>
/// Gets the active connection map
/// </summary>
public Dictionary<int, ISqlConnection> ActiveConnections
public Dictionary<int, DbConnection> ActiveConnections
{
get
{
@@ -127,10 +129,10 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
string connectionString = BuildConnectionString(connectionDetails);
// create a sql connection instance
ISqlConnection connection = this.ConnectionFactory.CreateSqlConnection();
DbConnection connection = this.ConnectionFactory.CreateSqlConnection(connectionString);
// open the database
connection.OpenDatabaseConnection(connectionString);
connection.Open();
// map the connection id to the connection object for future lookups
this.ActiveConnections.Add(++maxConnectionId, connection);

View File

@@ -5,7 +5,7 @@
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
namespace Microsoft.SqlTools.ServiceLayer.Connection
namespace Microsoft.SqlTools.ServiceLayer.Connection.Contracts
{
/// <summary>
/// Message format for the initial connection request

View File

@@ -1,34 +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.Collections.Generic;
namespace Microsoft.SqlTools.ServiceLayer.Connection
{
/// <summary>
/// Interface for the SQL Connection factory
/// </summary>
public interface ISqlConnectionFactory
{
/// <summary>
/// Create a new SQL Connection object
/// </summary>
ISqlConnection CreateSqlConnection();
}
/// <summary>
/// Interface for the SQL Connection wrapper
/// </summary>
public interface ISqlConnection
{
/// <summary>
/// Open a connection to the provided connection string
/// </summary>
/// <param name="connectionString"></param>
void OpenDatabaseConnection(string connectionString);
IEnumerable<string> GetServerObjects();
}
}

View File

@@ -0,0 +1,20 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System.Data.Common;
namespace Microsoft.SqlTools.ServiceLayer.Connection
{
/// <summary>
/// Interface for the SQL Connection factory
/// </summary>
public interface ISqlConnectionFactory
{
/// <summary>
/// Create a new SQL Connection object
/// </summary>
DbConnection CreateSqlConnection(string connectionString);
}
}

View File

@@ -1,72 +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.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
namespace Microsoft.SqlTools.ServiceLayer.Connection
{
/// <summary>
/// Factory class to create SqlClientConnections
/// The purpose of the factory is to make it easier to mock out the database
/// in 'offline' unit test scenarios.
/// </summary>
public class SqlConnectionFactory : ISqlConnectionFactory
{
/// <summary>
/// Creates a new SqlClientConnection object
/// </summary>
public ISqlConnection CreateSqlConnection()
{
return new SqlClientConnection();
}
}
/// <summary>
/// Wrapper class that implements ISqlConnection and hosts a SqlConnection.
/// This wrapper exists primarily for decoupling to support unit testing.
/// </summary>
public class SqlClientConnection : ISqlConnection
{
/// <summary>
/// the underlying SQL connection
/// </summary>
private SqlConnection connection;
/// <summary>
/// Opens a SqlConnection using provided connection string
/// </summary>
/// <param name="connectionString"></param>
public void OpenDatabaseConnection(string connectionString)
{
this.connection = new SqlConnection(connectionString);
this.connection.Open();
}
/// <summary>
/// Gets a list of database server schema objects
/// </summary>
/// <returns></returns>
public IEnumerable<string> GetServerObjects()
{
// Select the values from sys.tables to give a super basic
// autocomplete experience. This will be replaced by SMO.
SqlCommand command = connection.CreateCommand();
command.CommandText = "SELECT name FROM sys.tables";
command.CommandTimeout = 15;
command.CommandType = CommandType.Text;
var reader = command.ExecuteReader();
List<string> results = new List<string>();
while (reader.Read())
{
results.Add(reader[0].ToString());
}
return results;
}
}
}

View File

@@ -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.Data.Common;
using System.Data.SqlClient;
namespace Microsoft.SqlTools.ServiceLayer.Connection
{
/// <summary>
/// Factory class to create SqlClientConnections
/// The purpose of the factory is to make it easier to mock out the database
/// in 'offline' unit test scenarios.
/// </summary>
public class SqlConnectionFactory : ISqlConnectionFactory
{
/// <summary>
/// Creates a new SqlConnection object
/// </summary>
public DbConnection CreateSqlConnection(string connectionString)
{
return new SqlConnection(connectionString);
}
}
}

View File

@@ -5,8 +5,10 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Threading.Tasks;
using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.ConnectionServices;
using Microsoft.SqlTools.ServiceLayer.Hosting;
using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts;
using Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts;
@@ -60,11 +62,24 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
/// <summary>
/// Update the cached autocomplete candidate list when the user connects to a database
/// TODO: Update with refactoring/async
/// </summary>
/// <param name="connection"></param>
public async Task UpdateAutoCompleteCache(ISqlConnection connection)
public async Task UpdateAutoCompleteCache(DbConnection connection)
{
AutoCompleteList = connection.GetServerObjects();
DbCommand command = connection.CreateCommand();
command.CommandText = "SELECT name FROM sys.tables";
command.CommandTimeout = 15;
command.CommandType = CommandType.Text;
var reader = await command.ExecuteReaderAsync();
List<string> results = new List<string>();
while (await reader.ReadAsync())
{
results.Add(reader[0].ToString());
}
AutoCompleteList = results;
await Task.FromResult(0);
}

View File

@@ -5,6 +5,7 @@
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.SqlTools.EditorServices.Utility;
@@ -17,7 +18,8 @@ using Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts;
using System.Linq;
using Microsoft.SqlServer.Management.SqlParser.Parser;
using Location = Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts.Location;
using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.ConnectionServices;
using Microsoft.SqlTools.ServiceLayer.ConnectionServices.Contracts;
namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
{
@@ -308,7 +310,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
/// Callback for when a user connection is done processing
/// </summary>
/// <param name="sqlConnection"></param>
public async Task OnConnection(ISqlConnection sqlConnection)
public async Task OnConnection(DbConnection sqlConnection)
{
await AutoCompleteService.Instance.UpdateAutoCompleteCache(sqlConnection);
await Task.FromResult(true);

View File

@@ -7,7 +7,7 @@ using Microsoft.SqlTools.ServiceLayer.Hosting;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
using Microsoft.SqlTools.ServiceLayer.WorkspaceServices;
using Microsoft.SqlTools.ServiceLayer.LanguageServices;
using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.ConnectionServices;
namespace Microsoft.SqlTools.ServiceLayer
{

View File

@@ -5,7 +5,7 @@
using System.Diagnostics;
namespace Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts
namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
{
/// <summary>
/// Provides details about a position in a file buffer. All

View File

@@ -6,7 +6,7 @@
using System;
using System.Diagnostics;
namespace Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts
namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
{
/// <summary>
/// Provides details about a range between two positions in

View File

@@ -5,7 +5,7 @@
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
namespace Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts
namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
{
public class DidChangeConfigurationNotification<TConfig>
{

View File

@@ -3,7 +3,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
namespace Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts
namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
{
/// <summary>
/// Contains details relating to a content change in an open file.

View File

@@ -3,7 +3,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
namespace Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts
namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
{
/// <summary>
/// Provides details and operations for a buffer position in a

View File

@@ -9,7 +9,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts
namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
{
/// <summary>
/// Contains the details and contents of an open script file.

View File

@@ -3,7 +3,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
namespace Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts
namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
{
/// <summary>
/// Defines the message level of a script file marker.

View File

@@ -5,7 +5,7 @@
//using System.Management.Automation.Language;
namespace Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts
namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
{
/// <summary>
/// Contains details about a specific region of text in script file.

View File

@@ -6,7 +6,7 @@
using System.Diagnostics;
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
namespace Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts
namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
{
/// <summary>
/// Defines a base parameter class for identifying a text document.

View File

@@ -5,7 +5,7 @@
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
namespace Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts
namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
{
public enum SymbolKind
{

View File

@@ -10,9 +10,9 @@ using System.Text;
using System.Text.RegularExpressions;
using System.Linq;
using Microsoft.SqlTools.EditorServices.Utility;
using Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts;
using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
namespace Microsoft.SqlTools.ServiceLayer.WorkspaceServices
namespace Microsoft.SqlTools.ServiceLayer.Workspace
{
/// <summary>
/// Manages a "workspace" of script files that are open for a particular

View File

@@ -11,9 +11,9 @@ using System.Threading.Tasks;
using Microsoft.SqlTools.EditorServices.Utility;
using Microsoft.SqlTools.ServiceLayer.Hosting;
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
using Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts;
using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
namespace Microsoft.SqlTools.ServiceLayer.WorkspaceServices
namespace Microsoft.SqlTools.ServiceLayer.Workspace
{
/// <summary>
/// Class for handling requests/events that deal with the state of the workspace, including the

View File

@@ -115,7 +115,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.LanguageServices
var connectionService = TestObjects.GetTestConnectionService();
var connectionResult = connectionService.Connect(TestObjects.GetTestConnectionDetails());
var sqlConnection = connectionService.ActiveConnections[connectionResult.ConnectionId];
autocompleteService.UpdateAutoCompleteCache(sqlConnection);
autocompleteService.UpdateAutoCompleteCache(sqlConnection).Wait();
}
#endregion

View File

@@ -5,9 +5,17 @@
//#define USE_LIVE_CONNECTION
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.ConnectionServices;
using Microsoft.SqlTools.ServiceLayer.ConnectionServices.Contracts;
using Microsoft.SqlTools.ServiceLayer.LanguageServices;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
using Xunit;
@@ -80,18 +88,253 @@ namespace Microsoft.SqlTools.Test.Utility
}
}
public class TestDataReader : DbDataReader
{
#region Test Specific Implementations
internal string SqlCommandText { get; set; }
private const string tableNameTestCommand = "SELECT name FROM sys.tables";
private List<Dictionary<string, string>> tableNamesTest = new List<Dictionary<string, string>>
{
new Dictionary<string, string> { {"name", "table1"} },
new Dictionary<string, string> { {"name", "table2"} }
};
private IEnumerator<Dictionary<string, string>> tableEnumerator;
#endregion
public override bool GetBoolean(int ordinal)
{
throw new NotImplementedException();
}
public override byte GetByte(int ordinal)
{
throw new NotImplementedException();
}
public override long GetBytes(int ordinal, long dataOffset, byte[] buffer, int bufferOffset, int length)
{
throw new NotImplementedException();
}
public override char GetChar(int ordinal)
{
throw new NotImplementedException();
}
public override long GetChars(int ordinal, long dataOffset, char[] buffer, int bufferOffset, int length)
{
throw new NotImplementedException();
}
public override string GetDataTypeName(int ordinal)
{
throw new NotImplementedException();
}
public override DateTime GetDateTime(int ordinal)
{
throw new NotImplementedException();
}
public override decimal GetDecimal(int ordinal)
{
throw new NotImplementedException();
}
public override double GetDouble(int ordinal)
{
throw new NotImplementedException();
}
public override IEnumerator GetEnumerator()
{
throw new NotImplementedException();
}
public override int GetOrdinal(string name)
{
throw new NotImplementedException();
}
public override string GetName(int ordinal)
{
throw new NotImplementedException();
}
public override long GetInt64(int ordinal)
{
throw new NotImplementedException();
}
public override int GetInt32(int ordinal)
{
throw new NotImplementedException();
}
public override short GetInt16(int ordinal)
{
throw new NotImplementedException();
}
public override Guid GetGuid(int ordinal)
{
throw new NotImplementedException();
}
public override float GetFloat(int ordinal)
{
throw new NotImplementedException();
}
public override Type GetFieldType(int ordinal)
{
throw new NotImplementedException();
}
public override string GetString(int ordinal)
{
throw new NotImplementedException();
}
public override object GetValue(int ordinal)
{
throw new NotImplementedException();
}
public override int GetValues(object[] values)
{
throw new NotImplementedException();
}
public override bool IsDBNull(int ordinal)
{
throw new NotImplementedException();
}
public override bool NextResult()
{
throw new NotImplementedException();
}
public override bool Read()
{
if (tableEnumerator == null)
{
switch (SqlCommandText)
{
case tableNameTestCommand:
tableEnumerator = ((IEnumerable<Dictionary<string, string>>)tableNamesTest).GetEnumerator();
break;
default:
throw new NotImplementedException();
}
}
return tableEnumerator.MoveNext();
}
public override int Depth { get; }
public override bool IsClosed { get; }
public override int RecordsAffected { get; }
public override object this[string name]
{
get { return tableEnumerator.Current[name]; }
}
public override object this[int ordinal]
{
get { return tableEnumerator.Current[tableEnumerator.Current.Keys.ToArray()[ordinal]]; }
}
public override int FieldCount { get; }
public override bool HasRows { get; }
}
/// <summary>
/// Test mock class for IDbCommand
/// </summary>
public class TestSqlCommand : DbCommand
{
public override void Cancel()
{
throw new NotImplementedException();
}
public override int ExecuteNonQuery()
{
throw new NotImplementedException();
}
public override object ExecuteScalar()
{
throw new NotImplementedException();
}
public override void Prepare()
{
throw new NotImplementedException();
}
public override string CommandText { get; set; }
public override int CommandTimeout { get; set; }
public override CommandType CommandType { get; set; }
public override UpdateRowSource UpdatedRowSource { get; set; }
protected override DbConnection DbConnection { get; set; }
protected override DbParameterCollection DbParameterCollection { get; }
protected override DbTransaction DbTransaction { get; set; }
public override bool DesignTimeVisible { get; set; }
protected override DbParameter CreateDbParameter()
{
throw new NotImplementedException();
}
protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
{
return new TestDataReader {SqlCommandText = CommandText};
}
}
/// <summary>
/// Test mock class for SqlConnection wrapper
/// </summary>
public class TestSqlConnection : ISqlConnection
public class TestSqlConnection : DbConnection
{
public void OpenDatabaseConnection(string connectionString)
protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel)
{
throw new NotImplementedException();
}
public IEnumerable<string> GetServerObjects()
public override void Close()
{
return null;
throw new NotImplementedException();
}
public override void Open()
{
// No Op
}
public override string ConnectionString { get; set; }
public override string Database { get; }
public override ConnectionState State { get; }
public override string DataSource { get; }
public override string ServerVersion { get; }
protected override DbCommand CreateDbCommand()
{
return new TestSqlCommand();
}
public override void ChangeDatabase(string databaseName)
{
throw new NotImplementedException();
}
}
@@ -100,9 +343,12 @@ namespace Microsoft.SqlTools.Test.Utility
/// </summary>
public class TestSqlConnectionFactory : ISqlConnectionFactory
{
public ISqlConnection CreateSqlConnection()
public DbConnection CreateSqlConnection(string connectionString)
{
return new TestSqlConnection();
return new TestSqlConnection()
{
ConnectionString = connectionString
};
}
}
}