Files
sqltoolsservice/src/Microsoft.SqlTools.ServiceLayer/Connection/ConnectionInfo.cs
Kevin Cunnane f3bf330da6 Connect with different properties should actually change context (#307)
* Connect with different properties should actually change context
- Up to now, calling Connect for a previously-connected URI would disconnect, then reconnect ot the original (not new) target. WIth these changes we handle changes to database name or other key properties by updating the ConnectionInfo and connecting to the new target
- Some interesting scenarios are raised by our API, notably that an empty database name maps to the default DB (which we know nothing about). This limits the new feature such that only if the DB Name is specified, we'll change the connection. Hence 2 calls to an empty DB will not result in a DB change.

Additional changes:
- After discussion with Ben, we're simplifying the cancellation logic. He had made changes to support this, so the main update is that we dispose the token in the final block after its last use (hence avoiding a disposed exception) and clean up the number of Waits required since we already have async cancellation support
- Factored some logic such that the OnConnection callback isn't invoked until after we've updated the database name in the GetConnectionCompleteParams method. Again, this supports reporting the actual DB name instead of leaving it blank for default DB requests.

* PR comment fixes
2017-04-06 11:25:59 -07:00

167 lines
6.0 KiB
C#

//
// 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.Concurrent;
using System.Collections.Generic;
using System.Data.Common;
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.ServiceLayer.Connection
{
/// <summary>
/// Information pertaining to a unique connection instance.
/// </summary>
public class ConnectionInfo
{
/// <summary>
/// Constructor
/// </summary>
public ConnectionInfo(ISqlConnectionFactory factory, string ownerUri, ConnectionDetails details)
{
Factory = factory;
OwnerUri = ownerUri;
ConnectionDetails = details;
ConnectionId = Guid.NewGuid();
IntellisenseMetrics = new InteractionMetrics<double>(new int[] {50, 100, 200, 500, 1000, 2000});
}
/// <summary>
/// Unique Id, helpful to identify a connection info object
/// </summary>
public Guid ConnectionId { get; private set; }
/// <summary>
/// URI identifying the owner/user of the connection. Could be a file, service, resource, etc.
/// </summary>
public string OwnerUri { get; private set; }
/// <summary>
/// Factory used for creating the SQL connection associated with the connection info.
/// </summary>
public ISqlConnectionFactory Factory { get; private set; }
/// <summary>
/// Properties used for creating/opening the SQL connection.
/// </summary>
public ConnectionDetails ConnectionDetails { get; private set; }
/// <summary>
/// A map containing all connections to the database that are associated with
/// this ConnectionInfo's OwnerUri.
/// This is internal for testing access only
/// </summary>
internal readonly ConcurrentDictionary<string, DbConnection> ConnectionTypeToConnectionMap =
new ConcurrentDictionary<string, DbConnection>();
/// <summary>
/// Intellisense Metrics
/// </summary>
public InteractionMetrics<double> IntellisenseMetrics { get; private set; }
/// <summary>
/// Returns true is the db connection is to a SQL db
/// </summary>
public bool IsAzure { get; set; }
/// Returns true if the sql connection is to a DW instance
/// </summary>
public bool IsSqlDW { get; set; }
/// <summary>
/// Returns the major version number of the db we are connected to
/// </summary>
public int MajorVersion { get; set; }
/// <summary>
/// All DbConnection instances held by this ConnectionInfo
/// </summary>
public ICollection<DbConnection> AllConnections
{
get
{
return ConnectionTypeToConnectionMap.Values;
}
}
/// <summary>
/// All connection type strings held by this ConnectionInfo
/// </summary>
/// <returns></returns>
public ICollection<string> AllConnectionTypes
{
get
{
return ConnectionTypeToConnectionMap.Keys;
}
}
public bool HasConnectionType(string connectionType)
{
connectionType = connectionType ?? ConnectionType.Default;
return ConnectionTypeToConnectionMap.ContainsKey(connectionType);
}
/// <summary>
/// The count of DbConnectioninstances held by this ConnectionInfo
/// </summary>
public int CountConnections
{
get
{
return ConnectionTypeToConnectionMap.Count;
}
}
/// <summary>
/// Try to get the DbConnection associated with the given connection type string.
/// </summary>
/// <returns>true if a connection with type connectionType was located and out connection was set,
/// false otherwise </returns>
/// <exception cref="ArgumentException">Thrown when connectionType is null or empty</exception>
public bool TryGetConnection(string connectionType, out DbConnection connection)
{
Validate.IsNotNullOrEmptyString("Connection Type", connectionType);
return ConnectionTypeToConnectionMap.TryGetValue(connectionType, out connection);
}
/// <summary>
/// Adds a DbConnection to this object and associates it with the given
/// connection type string. If a connection already exists with an identical
/// connection type string, it is not overwritten. Ignores calls where connectionType = null
/// </summary>
/// <exception cref="ArgumentException">Thrown when connectionType is null or empty</exception>
public void AddConnection(string connectionType, DbConnection connection)
{
Validate.IsNotNullOrEmptyString("Connection Type", connectionType);
ConnectionTypeToConnectionMap.TryAdd(connectionType, connection);
}
/// <summary>
/// Removes the single DbConnection instance associated with string connectionType
/// </summary>
/// <exception cref="ArgumentException">Thrown when connectionType is null or empty</exception>
public void RemoveConnection(string connectionType)
{
Validate.IsNotNullOrEmptyString("Connection Type", connectionType);
DbConnection connection;
ConnectionTypeToConnectionMap.TryRemove(connectionType, out connection);
}
/// <summary>
/// Removes all DbConnection instances held by this object
/// </summary>
public void RemoveAllConnections()
{
foreach (var type in AllConnectionTypes)
{
DbConnection connection;
ConnectionTypeToConnectionMap.TryRemove(type, out connection);
}
}
}
}