Switching from ISqlConnection to DbConnection

This is a fairly minor change that will save tons of time as we develop
this service. The DbConnection and associated Db* abstract classes
ask for synchronous versions of the code and allow the addition of async
code. The SqlClient implementation already implements Db* abstract
classes, so we can piggy back off that for our dependency injection layer.

Tests and existing code has been updated to handle the change, as well
This commit is contained in:
Benjamin Russell
2016-08-02 18:19:51 -07:00
parent f40aa31c67
commit b2f44031b7
8 changed files with 104 additions and 351 deletions

View File

@@ -5,6 +5,7 @@
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.SqlClient;
using System.Threading.Tasks;
using Microsoft.SqlTools.EditorServices.Utility;
@@ -64,15 +65,15 @@ namespace Microsoft.SqlTools.ServiceLayer.ConnectionServices
/// <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
@@ -82,7 +83,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ConnectionServices
/// <summary>
/// Gets the active connection map
/// </summary>
public Dictionary<int, ISqlConnection> ActiveConnections
public Dictionary<int, DbConnection> ActiveConnections
{
get
{
@@ -128,7 +129,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ConnectionServices
string connectionString = BuildConnectionString(connectionDetails);
// create a sql connection instance
ISqlConnection connection = this.ConnectionFactory.CreateSqlConnection(connectionString);
DbConnection connection = this.ConnectionFactory.CreateSqlConnection(connectionString);
// open the database
connection.Open();

View File

@@ -1,28 +0,0 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.Data;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.SqlTools.ServiceLayer.ConnectionServices.Contracts
{
/// <summary>
/// Interface for the SQL Connection wrapper
/// </summary>
public interface ISqlConnection : IDbConnection
{
string DataSource { get; }
string ServerVersion { get; }
void ClearPool();
Task OpenAsync();
Task OpenAsync(CancellationToken token);
}
}

View File

@@ -3,6 +3,8 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System.Data.Common;
namespace Microsoft.SqlTools.ServiceLayer.ConnectionServices.Contracts
{
/// <summary>
@@ -13,6 +15,6 @@ namespace Microsoft.SqlTools.ServiceLayer.ConnectionServices.Contracts
/// <summary>
/// Create a new SQL Connection object
/// </summary>
ISqlConnection CreateSqlConnection(string connectionString);
DbConnection CreateSqlConnection(string connectionString);
}
}

View File

@@ -1,160 +0,0 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.Data;
using System.Data.SqlClient;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.SqlTools.ServiceLayer.ConnectionServices.Contracts
{
/// <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>
/// Creates a new instance of the SqlClientConnection with an underlying connection to the
/// database server provided in <paramref name="connectionString"/>.
/// </summary>
/// <param name="connectionString">Connection string for the database to connect to</param>
public SqlClientConnection(string connectionString)
{
connection = new SqlConnection(connectionString);
}
#region ISqlConnection Implementation
#region Properties
public string ConnectionString
{
get { return connection.ConnectionString; }
set { connection.ConnectionString = value; }
}
public int ConnectionTimeout
{
get { return connection.ConnectionTimeout; }
}
public string Database
{
get { return connection.Database; }
}
public string DataSource
{
get { return connection.DataSource; }
}
public string ServerVersion
{
get { return connection.ServerVersion; }
}
public ConnectionState State
{
get { return connection.State; }
}
#endregion
#region Public Methods
public IDbTransaction BeginTransaction()
{
return connection.BeginTransaction();
}
public IDbTransaction BeginTransaction(IsolationLevel il)
{
return connection.BeginTransaction(il);
}
public void ChangeDatabase(string databaseName)
{
connection.ChangeDatabase(databaseName);
}
public void ClearPool()
{
if (connection != null)
{
SqlConnection.ClearPool(connection);
}
}
public void Close()
{
connection.Close();
}
public IDbCommand CreateCommand()
{
return connection.CreateCommand();
}
public void Open()
{
connection.Open();
}
public Task OpenAsync()
{
return connection.OpenAsync();
}
public Task OpenAsync(CancellationToken token)
{
return connection.OpenAsync(token);
}
#endregion
#endregion
#region IDisposable Implementation
private bool disposed;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
if (connection.State == ConnectionState.Open)
{
connection.Close();
}
connection.Dispose();
}
disposed = true;
}
}
~SqlClientConnection()
{
Dispose(false);
}
#endregion
}
}

View File

@@ -3,6 +3,9 @@
// 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.ConnectionServices.Contracts
{
/// <summary>
@@ -15,9 +18,9 @@ namespace Microsoft.SqlTools.ServiceLayer.ConnectionServices.Contracts
/// <summary>
/// Creates a new SqlClientConnection object
/// </summary>
public ISqlConnection CreateSqlConnection(string connectionString)
public DbConnection CreateSqlConnection(string connectionString)
{
return new SqlClientConnection(connectionString);
return new SqlConnection(connectionString);
}
}
}

View File

@@ -6,10 +6,9 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Data.Common;
using System.Threading.Tasks;
using Microsoft.SqlTools.ServiceLayer.ConnectionServices;
using Microsoft.SqlTools.ServiceLayer.ConnectionServices.Contracts;
using Microsoft.SqlTools.ServiceLayer.Hosting;
using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts;
using Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts;
@@ -66,16 +65,16 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
/// TODO: Update with refactoring/async
/// </summary>
/// <param name="connection"></param>
public async Task UpdateAutoCompleteCache(ISqlConnection connection)
public async Task UpdateAutoCompleteCache(DbConnection connection)
{
IDbCommand command = connection.CreateCommand();
DbCommand command = connection.CreateCommand();
command.CommandText = "SELECT name FROM sys.tables";
command.CommandTimeout = 15;
command.CommandType = CommandType.Text;
var reader = command.ExecuteReader();
var reader = await command.ExecuteReaderAsync();
List<string> results = new List<string>();
while (reader.Read())
while (await reader.ReadAsync())
{
results.Add(reader[0].ToString());
}

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;
@@ -309,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);