mirror of
https://github.com/Microsoft/sql-server-samples.git
synced 2025-12-08 14:58:54 +00:00
code review feedback
This commit is contained in:
@@ -1,62 +1,62 @@
|
||||
# SmoSamples
|
||||
|
||||
This unit test project is meant to demonstrate features of the Sql Management Objects framework and to help developers optimize performance of their SMO-based applications.
|
||||
|
||||
|
||||
### Contents
|
||||
|
||||
[About this sample](#about-this-sample)<br/>
|
||||
[Before you begin](#before-you-begin)<br/>
|
||||
[Run this sample](#run-this-sample)<br/>
|
||||
[Sample details](#sample-details)<br/>
|
||||
[Disclaimers](#disclaimers)<br/>
|
||||
[Related links](#related-links)<br/>
|
||||
|
||||
|
||||
<a name=about-this-sample></a>
|
||||
|
||||
## About this sample
|
||||
|
||||
<!-- Delete the ones that don't apply -->
|
||||
- **Applies to:** SQL Server 2016 (or higher), Azure SQL Database, Azure SQL Data Warehouse
|
||||
- **Key features:**
|
||||
- Unit tests and a docker file that demonstrate proper use of SMO features against a working SQL Server instance.
|
||||
- **Programming Language:**
|
||||
- C#
|
||||
|
||||
<a name=before-you-begin></a>
|
||||
|
||||
## Before you begin
|
||||
|
||||
To run this sample, you need the following prerequisites.
|
||||
|
||||
**Software prerequisites:**
|
||||
|
||||
1. SQL Server 2016 (or higher) or an Azure SQL Database with the full WideWorldImporters sample database, or
|
||||
2. Docker
|
||||
3. At minimum the dotnet 2.2 SDK, or Visual Studio 2017
|
||||
|
||||
<a name=run-this-sample></a>
|
||||
|
||||
## Run this sample
|
||||
|
||||
|
||||
<a name=sample-details></a>
|
||||
|
||||
## Sample details
|
||||
|
||||
Each unit test demonstrates a specific aspect of SMO-based application development, either in isolation or in conjunction with other SMO components. <br/>
|
||||
Feature areas tested include:
|
||||
1. Efficient use of collections
|
||||
2. Sql query capture
|
||||
3. Events
|
||||
4. URNs
|
||||
5. Script generation
|
||||
|
||||
|
||||
<a name=related-links></a>
|
||||
|
||||
## Related Links
|
||||
The SMO NuGet package is at https://www.nuget.org/packages/Microsoft.SqlServer.SqlManagementObjects/ <br/>
|
||||
Documentation for the APIs is at https://docs.microsoft.com/en-us/sql/relational-databases/server-management-objects-smo/overview-smo<br/>
|
||||
# SmoSamples
|
||||
|
||||
This unit test project is meant to demonstrate features of the Sql Management Objects framework and to help developers optimize performance of their SMO-based applications.
|
||||
|
||||
|
||||
### Contents
|
||||
|
||||
[About this sample](#about-this-sample)<br/>
|
||||
[Before you begin](#before-you-begin)<br/>
|
||||
[Run this sample](#run-this-sample)<br/>
|
||||
[Sample details](#sample-details)<br/>
|
||||
[Disclaimers](#disclaimers)<br/>
|
||||
[Related links](#related-links)<br/>
|
||||
|
||||
|
||||
<a name=about-this-sample></a>
|
||||
|
||||
## About this sample
|
||||
|
||||
<!-- Delete the ones that don't apply -->
|
||||
- **Applies to:** SQL Server 2016 (or higher), Azure SQL Database, Azure SQL Data Warehouse
|
||||
- **Key features:**
|
||||
- Unit tests and a docker file that demonstrate proper use of SMO features against a working SQL Server instance.
|
||||
- **Programming Language:**
|
||||
- C#
|
||||
|
||||
<a name=before-you-begin></a>
|
||||
|
||||
## Before you begin
|
||||
|
||||
To run this sample, you need the following prerequisites.
|
||||
|
||||
**Software prerequisites:**
|
||||
|
||||
1. SQL Server 2016 (or higher) or an Azure SQL Database with the full WideWorldImporters sample database, or
|
||||
2. Docker
|
||||
3. At minimum the dotnet 2.2 SDK, or Visual Studio 2017
|
||||
|
||||
<a name=run-this-sample></a>
|
||||
|
||||
## Run this sample
|
||||
If using Docker, use runtests.sh or runtests.cmd as appropriate. If using a separate instance of SQL Server or Azure SQL Database, create a .runsettings file and run the unit tests using Visual Studio or "dotnet vstest".
|
||||
|
||||
<a name=sample-details></a>
|
||||
|
||||
## Sample details
|
||||
|
||||
Each unit test demonstrates a specific aspect of SMO-based application development, either in isolation or in conjunction with other SMO components. <br/>
|
||||
Feature areas tested include:
|
||||
1. Efficient use of collections
|
||||
2. Sql query capture
|
||||
3. Events
|
||||
4. URNs
|
||||
5. Script generation
|
||||
|
||||
|
||||
<a name=related-links></a>
|
||||
|
||||
## Related Links
|
||||
The SMO NuGet package is at https://www.nuget.org/packages/Microsoft.SqlServer.SqlManagementObjects/ <br/>
|
||||
Documentation for the APIs is at https://docs.microsoft.com/sql/relational-databases/server-management-objects-smo/overview-smo<br/>
|
||||
The WideWorldImporters sample database can be found at https://github.com/Microsoft/sql-server-samples/releases/download/wide-world-importers-v1.0/WideWorldImporters-Full.bak <br/>
|
||||
@@ -1,8 +1,8 @@
|
||||
FROM mcr.microsoft.com/mssql/server:2017-latest
|
||||
WORKDIR /tmp/backup
|
||||
RUN wget -q https://github.com/Microsoft/sql-server-samples/releases/download/wide-world-importers-v1.0/WideWorldImporters-Full.bak
|
||||
COPY restore.sql .
|
||||
COPY restore.sh .
|
||||
COPY entrypoint.sh .
|
||||
CMD ["/bin/bash", "/tmp/backup/entrypoint.sh"]
|
||||
|
||||
FROM mcr.microsoft.com/mssql/server:2017-latest
|
||||
WORKDIR /tmp/backup
|
||||
RUN wget -q https://github.com/Microsoft/sql-server-samples/releases/download/wide-world-importers-v1.0/WideWorldImporters-Full.bak
|
||||
COPY restore.sql .
|
||||
COPY restore.sh .
|
||||
COPY entrypoint.sh .
|
||||
CMD ["/bin/bash", "/tmp/backup/entrypoint.sh"]
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# Wait for SQL Server to start and be ready to accept connections
|
||||
sleep 35s
|
||||
echo sa_password is $SA_PASSWORD
|
||||
/opt/mssql-tools/bin/sqlcmd -S . -U sa -P $SA_PASSWORD -i /tmp/backup/restore.sql
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
RESTORE DATABASE WideWorldImporters FROM DISK = "/tmp/backup/WideWorldImporters-Full.bak"
|
||||
WITH MOVE "WWI_Primary" TO "/var/opt/mssql/data/WideWorldImporters.mdf",
|
||||
MOVE "WWI_Userdata" TO "/var/opt/mssql/data/WideWorldImporters_UserData.ndf",
|
||||
MOVE "WWI_Log" TO "/var/opt/mssql/data/WideWorldImporters.ldf", MOVE "WWI_InMemory_Data_1"
|
||||
RESTORE DATABASE WideWorldImporters FROM DISK = "/tmp/backup/WideWorldImporters-Full.bak"
|
||||
WITH MOVE "WWI_Primary" TO "/var/opt/mssql/data/WideWorldImporters.mdf",
|
||||
MOVE "WWI_Userdata" TO "/var/opt/mssql/data/WideWorldImporters_UserData.ndf",
|
||||
MOVE "WWI_Log" TO "/var/opt/mssql/data/WideWorldImporters.ldf", MOVE "WWI_InMemory_Data_1"
|
||||
TO "/var/opt/mssql/data/WideWorldImporters_InMemory_Data_1"
|
||||
@@ -1,12 +1,10 @@
|
||||
|
||||
using System.Diagnostics;
|
||||
using Microsoft.SqlServer.Management.Smo;
|
||||
|
||||
namespace Microsoft.SqlServer.SmoSamples
|
||||
namespace Microsoft.SqlServer.SmoSamples
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using Microsoft.SqlServer.Management.Smo;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using NUnit.Framework;
|
||||
using Assert = NUnit.Framework.Assert;
|
||||
@@ -16,6 +14,12 @@ namespace Microsoft.SqlServer.SmoSamples
|
||||
{
|
||||
public VisualStudio.TestTools.UnitTesting.TestContext TestContext { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// SetDefaultInitFields tells the Server object which properties to include in the initial query
|
||||
/// to populate of a given object type when initialized a collection of that type.
|
||||
/// The test demonstrates the effect of using this call to enumerate Tables when accessing the FileGroup
|
||||
/// property of each Table object
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void Collection_iteration_is_faster_with_SetDefaultInitFields()
|
||||
{
|
||||
@@ -26,11 +30,14 @@ namespace Microsoft.SqlServer.SmoSamples
|
||||
connectionMetrics.Reset();
|
||||
foreach (Table table in database.Tables)
|
||||
{
|
||||
// Accessing FileGroup triggers a query to fetch it
|
||||
Trace.TraceInformation(
|
||||
$"Unoptimized table Name: {table.Name}\tSchema:{table.Schema}\tFileGroup:{table.FileGroup}");
|
||||
}
|
||||
|
||||
var unoptimizedMetrics = (connectionMetrics.QueryCount, connectionMetrics.BytesSent, connectionMetrics.BytesRead, connectionMetrics.ConnectionCount);
|
||||
var unoptimizedMetrics = (QueryCount: connectionMetrics.QueryCount,
|
||||
BytesSent: connectionMetrics.BytesSent, BytesRead: connectionMetrics.BytesRead,
|
||||
ConnectionCount: connectionMetrics.ConnectionCount);
|
||||
Trace.TraceInformation(string.Join($"{Environment.NewLine}\t", new[]
|
||||
{
|
||||
"Unoptimized metrics:",
|
||||
@@ -43,11 +50,14 @@ namespace Microsoft.SqlServer.SmoSamples
|
||||
database.Tables.Refresh();
|
||||
foreach (Table table in database.Tables)
|
||||
{
|
||||
// The FileGroup property is already populated, so no extra query is needed
|
||||
Trace.TraceInformation(
|
||||
$"Optimized table Name: {table.Name}\tSchema:{table.Schema}\tFileGroup:{table.FileGroup}");
|
||||
}
|
||||
|
||||
var optimizedMetrics = (connectionMetrics.QueryCount, connectionMetrics.BytesSent, connectionMetrics.BytesRead, connectionMetrics.ConnectionCount);
|
||||
var optimizedMetrics = (QueryCount: connectionMetrics.QueryCount,
|
||||
BytesSent: connectionMetrics.BytesSent, BytesRead: connectionMetrics.BytesRead,
|
||||
ConnectionCount: connectionMetrics.ConnectionCount);
|
||||
Trace.TraceInformation(string.Join($"{Environment.NewLine}\t", new[]
|
||||
{
|
||||
"Optimized Metrics:",
|
||||
|
||||
@@ -1,135 +1,150 @@
|
||||
using Microsoft.SqlServer.Management.Common;
|
||||
using Microsoft.SqlServer.Management.Smo;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Assert = NUnit.Framework.Assert;
|
||||
namespace Microsoft.SqlServer.SmoSamples
|
||||
{
|
||||
// Used by test classes to initialize and retrieve a ServerConnection for use in the tests themselves
|
||||
static class ConnectionHelpers
|
||||
{
|
||||
|
||||
public static ServerConnection GetTestConnection(this VisualStudio.TestTools.UnitTesting.TestContext context, ConnectionType connectionType = ConnectionType.Default)
|
||||
{
|
||||
var connectionString = context.GetConnectionString();
|
||||
var connectionStrBuilder = new SqlConnectionStringBuilder(connectionString);
|
||||
var instanceName = connectionStrBuilder.DataSource;
|
||||
var sqlServerLogin = connectionStrBuilder.UserID;
|
||||
var password = connectionStrBuilder.Password;
|
||||
if (connectionType == ConnectionType.SqlConnection)
|
||||
{
|
||||
return new ServerConnection(new SqlConnection(connectionString));
|
||||
}
|
||||
if (connectionType == ConnectionType.Integrated)
|
||||
{
|
||||
return new ServerConnection(instanceName);
|
||||
}
|
||||
if (connectionType == ConnectionType.SqlAuth )
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(sqlServerLogin) || string.IsNullOrWhiteSpace(password))
|
||||
{
|
||||
throw new ArgumentException("username and password values are missing from test connection string");
|
||||
}
|
||||
return new ServerConnection(instanceName, sqlServerLogin, password);
|
||||
}
|
||||
if (string.IsNullOrEmpty(sqlServerLogin))
|
||||
{
|
||||
return new ServerConnection(instanceName);
|
||||
}
|
||||
return new ServerConnection(instanceName, sqlServerLogin, password);
|
||||
}
|
||||
|
||||
public static string GetConnectionString(this VisualStudio.TestTools.UnitTesting.TestContext context)
|
||||
{
|
||||
var connectionString = context.Properties["connectionString"].ToString();
|
||||
Assert.That(connectionString, Is.Not.Empty, "connectionString must be set");
|
||||
connectionString = connectionString.Replace("[hostname]", Environment.GetEnvironmentVariable("TEST_HOSTNAME")).
|
||||
Replace("[username]", Environment.GetEnvironmentVariable("TEST_USERNAME")).
|
||||
Replace("[password]", Environment.GetEnvironmentVariable("TEST_PASSWORD")).
|
||||
Replace("[database]", Environment.GetEnvironmentVariable("TEST_DATABASE"));
|
||||
Console.WriteLine("Connection string: {0}", connectionString);
|
||||
return connectionString;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the name of the database to use for the tests
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string GetTestDatabaseName(this VisualStudio.TestTools.UnitTesting.TestContext context)
|
||||
{
|
||||
var databaseName = Environment.GetEnvironmentVariable("TEST_DATABASE");
|
||||
if (string.IsNullOrEmpty(databaseName))
|
||||
{
|
||||
databaseName = context.Properties["testDatabase"].ToString();
|
||||
}
|
||||
Assert.That(databaseName, Is.Not.Empty, "testDatabase must be set");
|
||||
Console.WriteLine("Test database: {0}", databaseName);
|
||||
return databaseName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the folder where result files should be written
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetResultsFolder(this VisualStudio.TestTools.UnitTesting.TestContext context)
|
||||
{
|
||||
var path = Environment.GetEnvironmentVariable("RESULTS_FOLDER");
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
path = context.Properties.ContainsKey("resultsFolder") ? context.Properties["resultsFolder"].ToString() : null;
|
||||
}
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
path = PathWrapper.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
path = PathWrapper.Combine(path, "results");
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// creates a new database with a random name, runs the action, and drops the database
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="action"></param>
|
||||
/// <param name="preCreateAction"></param>
|
||||
public static void ExecuteWithDbDrop(this VisualStudio.TestTools.UnitTesting.TestContext context, Action<Database> action, Action<Database> preCreateAction = null)
|
||||
{
|
||||
var dbName = string.Format("{0}{1}", context.TestName, new Random().Next());
|
||||
var serverConnection = context.GetTestConnection();
|
||||
var server = new Management.Smo.Server(serverConnection);
|
||||
var database = new Database(server, dbName);
|
||||
preCreateAction?.Invoke(database);
|
||||
database.Create();
|
||||
try
|
||||
{
|
||||
action(database);
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
database.Drop();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Trace.TraceError("Unable to drop database {0}: {1}", dbName, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ConnectionType
|
||||
{
|
||||
Default, // whatever is specified in the config
|
||||
Integrated, // integrated auth
|
||||
SqlAuth, // SQL auth
|
||||
SqlConnection // Create a SqlConnection first from the connection string
|
||||
}
|
||||
}
|
||||
using Microsoft.SqlServer.Management.Common;
|
||||
using Microsoft.SqlServer.Management.Smo;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Assert = NUnit.Framework.Assert;
|
||||
namespace Microsoft.SqlServer.SmoSamples
|
||||
{
|
||||
/// <summary>
|
||||
/// Used by test classes to initialize and retrieve a ServerConnection for use in the tests themselves
|
||||
/// </summary>
|
||||
static class ConnectionHelpers
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Returns a ServerConnection based on the connectionString parameter defined in the runsettings file
|
||||
/// </summary>
|
||||
public static ServerConnection GetTestConnection(this VisualStudio.TestTools.UnitTesting.TestContext context, ConnectionType connectionType = ConnectionType.Default)
|
||||
{
|
||||
var connectionString = context.GetConnectionString();
|
||||
var connectionStrBuilder = new SqlConnectionStringBuilder(connectionString);
|
||||
var instanceName = connectionStrBuilder.DataSource;
|
||||
var sqlServerLogin = connectionStrBuilder.UserID;
|
||||
var password = connectionStrBuilder.Password;
|
||||
if (connectionType == ConnectionType.SqlConnection)
|
||||
{
|
||||
return new ServerConnection(new SqlConnection(connectionString));
|
||||
}
|
||||
if (connectionType == ConnectionType.Integrated)
|
||||
{
|
||||
return new ServerConnection(instanceName);
|
||||
}
|
||||
if (connectionType == ConnectionType.SqlAuth )
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(sqlServerLogin) || string.IsNullOrWhiteSpace(password))
|
||||
{
|
||||
throw new ArgumentException("username and password values are missing from test connection string");
|
||||
}
|
||||
return new ServerConnection(instanceName, sqlServerLogin, password);
|
||||
}
|
||||
if (string.IsNullOrEmpty(sqlServerLogin))
|
||||
{
|
||||
return new ServerConnection(instanceName);
|
||||
}
|
||||
return new ServerConnection(instanceName, sqlServerLogin, password);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a connection string based on the connectionString parameter defined in the runsettings file
|
||||
/// Placeholders may be included in the connection string if the caller has set corresponding environment variables.
|
||||
/// [hostname] -> TEST_HOSTNAME environment variable
|
||||
/// [username] -> TEST_USERNAME
|
||||
/// [password] -> TEST_PASSWORD
|
||||
/// [database] -> TEST_DATABASE
|
||||
/// </summary>
|
||||
public static string GetConnectionString(this VisualStudio.TestTools.UnitTesting.TestContext context)
|
||||
{
|
||||
var connectionString = context.Properties["connectionString"].ToString();
|
||||
Assert.That(connectionString, Is.Not.Empty, "connectionString must be set");
|
||||
connectionString = connectionString.Replace("[hostname]", Environment.GetEnvironmentVariable("TEST_HOSTNAME")).
|
||||
Replace("[username]", Environment.GetEnvironmentVariable("TEST_USERNAME")).
|
||||
Replace("[password]", Environment.GetEnvironmentVariable("TEST_PASSWORD")).
|
||||
Replace("[database]", Environment.GetEnvironmentVariable("TEST_DATABASE"));
|
||||
Console.WriteLine("Connection string: {0}", connectionString);
|
||||
return connectionString;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the name of the database to use for the tests
|
||||
/// If TEST_DATABASE environment variable is set, that value is used, otherwise the
|
||||
/// testDatabase parameter from runsettings is used.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string GetTestDatabaseName(this VisualStudio.TestTools.UnitTesting.TestContext context)
|
||||
{
|
||||
var databaseName = Environment.GetEnvironmentVariable("TEST_DATABASE");
|
||||
if (string.IsNullOrEmpty(databaseName))
|
||||
{
|
||||
databaseName = context.Properties["testDatabase"].ToString();
|
||||
}
|
||||
Assert.That(databaseName, Is.Not.Empty, "testDatabase must be set");
|
||||
Console.WriteLine("Test database: {0}", databaseName);
|
||||
return databaseName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the folder where result files should be written
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetResultsFolder(this VisualStudio.TestTools.UnitTesting.TestContext context)
|
||||
{
|
||||
var path = Environment.GetEnvironmentVariable("RESULTS_FOLDER");
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
path = context.Properties.ContainsKey("resultsFolder") ? context.Properties["resultsFolder"].ToString() : null;
|
||||
}
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
path = PathWrapper.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
path = PathWrapper.Combine(path, "results");
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// creates a new database with a random name, runs the action, and drops the database
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="action"></param>
|
||||
/// <param name="preCreateAction"></param>
|
||||
public static void ExecuteWithDbDrop(this VisualStudio.TestTools.UnitTesting.TestContext context, Action<Database> action, Action<Database> preCreateAction = null)
|
||||
{
|
||||
var dbName = string.Format("{0}{1}", context.TestName, new Random().Next());
|
||||
var serverConnection = context.GetTestConnection();
|
||||
var server = new Management.Smo.Server(serverConnection);
|
||||
var database = new Database(server, dbName);
|
||||
preCreateAction?.Invoke(database);
|
||||
database.Create();
|
||||
try
|
||||
{
|
||||
action(database);
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
database.Drop();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Trace.TraceError("Unable to drop database {0}: {1}", dbName, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ConnectionType
|
||||
{
|
||||
Default, // whatever is specified in the config
|
||||
Integrated, // integrated auth
|
||||
SqlAuth, // SQL auth
|
||||
SqlConnection // Create a SqlConnection first from the connection string
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,93 +1,93 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Microsoft.SqlServer.Management.Common;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace Microsoft.SqlServer.SmoSamples
|
||||
{
|
||||
class ConnectionMetrics : IDisposable
|
||||
{
|
||||
public int ConnectionCount;
|
||||
public long BytesRead;
|
||||
public long BytesSent;
|
||||
public int QueryCount;
|
||||
public readonly ServerConnection ServerConnection;
|
||||
private readonly GenericSqlProxy proxy;
|
||||
|
||||
public ConnectionMetrics(ServerConnection serverConnection, GenericSqlProxy proxy)
|
||||
{
|
||||
this.proxy = proxy;
|
||||
ServerConnection = serverConnection;
|
||||
proxy.OnConnect += Proxy_OnConnect;
|
||||
proxy.OnWriteHost += Proxy_OnWriteHost;
|
||||
proxy.OnWriteClient += Proxy_OnWriteClient;
|
||||
serverConnection.StatementExecuted += ServerConnection_StatementExecuted;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
ConnectionCount = 0;
|
||||
BytesRead = BytesSent = 0;
|
||||
QueryCount = 0;
|
||||
}
|
||||
|
||||
private void ServerConnection_StatementExecuted(object sender, StatementEventArgs e)
|
||||
{
|
||||
QueryCount++;
|
||||
}
|
||||
|
||||
private void Proxy_OnWriteClient(object sender, StreamWriteEventArgs e)
|
||||
{
|
||||
BytesRead += e.BytesWritten;
|
||||
}
|
||||
|
||||
private void Proxy_OnWriteHost(object sender, StreamWriteEventArgs e)
|
||||
{
|
||||
BytesSent += e.BytesWritten;
|
||||
}
|
||||
|
||||
private void Proxy_OnConnect(object sender, ProxyConnectionEventArgs e)
|
||||
{
|
||||
ConnectionCount++;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
proxy.OnConnect -= Proxy_OnConnect;
|
||||
proxy.OnWriteHost -= Proxy_OnWriteHost;
|
||||
proxy.OnWriteClient -= Proxy_OnWriteClient;
|
||||
ServerConnection.StatementExecuted -= ServerConnection_StatementExecuted;
|
||||
ServerConnection.SqlConnectionObject.Dispose();
|
||||
proxy.Dispose();
|
||||
}
|
||||
|
||||
public static ConnectionMetrics SetupMeasuredConnection(TestContext testContext, int latencyPaddingMs = 0)
|
||||
{
|
||||
var connectionString = testContext.GetConnectionString();
|
||||
var proxy = new GenericSqlProxy(connectionString);
|
||||
if (latencyPaddingMs > 0)
|
||||
{
|
||||
proxy.OnWriteClient += (o,e) => DelayWrite(latencyPaddingMs, e);
|
||||
}
|
||||
// If running these tests in a container you may need to set a specific port
|
||||
// and expose that port in the dockerfile
|
||||
var port = testContext.Properties.ContainsKey("proxyPort")
|
||||
? Convert.ToInt32(testContext.Properties["proxyPort"])
|
||||
: 0;
|
||||
var sqlConnection = new SqlConnection(proxy.Initialize(port));
|
||||
var serverConnection = new ServerConnection(sqlConnection);
|
||||
return new ConnectionMetrics(serverConnection, proxy);
|
||||
}
|
||||
|
||||
static void DelayWrite(long delay, StreamWriteEventArgs args)
|
||||
{
|
||||
Thread.Sleep(Convert.ToInt32(delay));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Microsoft.SqlServer.Management.Common;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace Microsoft.SqlServer.SmoSamples
|
||||
{
|
||||
class ConnectionMetrics : IDisposable
|
||||
{
|
||||
public int ConnectionCount;
|
||||
public long BytesRead;
|
||||
public long BytesSent;
|
||||
public int QueryCount;
|
||||
public readonly ServerConnection ServerConnection;
|
||||
private readonly GenericSqlProxy proxy;
|
||||
|
||||
public ConnectionMetrics(ServerConnection serverConnection, GenericSqlProxy proxy)
|
||||
{
|
||||
this.proxy = proxy;
|
||||
ServerConnection = serverConnection;
|
||||
proxy.OnConnect += Proxy_OnConnect;
|
||||
proxy.OnWriteHost += Proxy_OnWriteHost;
|
||||
proxy.OnWriteClient += Proxy_OnWriteClient;
|
||||
serverConnection.StatementExecuted += ServerConnection_StatementExecuted;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
ConnectionCount = 0;
|
||||
BytesRead = BytesSent = 0;
|
||||
QueryCount = 0;
|
||||
}
|
||||
|
||||
private void ServerConnection_StatementExecuted(object sender, StatementEventArgs e)
|
||||
{
|
||||
QueryCount++;
|
||||
}
|
||||
|
||||
private void Proxy_OnWriteClient(object sender, StreamWriteEventArgs e)
|
||||
{
|
||||
BytesRead += e.BytesWritten;
|
||||
}
|
||||
|
||||
private void Proxy_OnWriteHost(object sender, StreamWriteEventArgs e)
|
||||
{
|
||||
BytesSent += e.BytesWritten;
|
||||
}
|
||||
|
||||
private void Proxy_OnConnect(object sender, ProxyConnectionEventArgs e)
|
||||
{
|
||||
ConnectionCount++;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
proxy.OnConnect -= Proxy_OnConnect;
|
||||
proxy.OnWriteHost -= Proxy_OnWriteHost;
|
||||
proxy.OnWriteClient -= Proxy_OnWriteClient;
|
||||
ServerConnection.StatementExecuted -= ServerConnection_StatementExecuted;
|
||||
ServerConnection.SqlConnectionObject.Dispose();
|
||||
proxy.Dispose();
|
||||
}
|
||||
|
||||
public static ConnectionMetrics SetupMeasuredConnection(TestContext testContext, int latencyPaddingMs = 0)
|
||||
{
|
||||
var connectionString = testContext.GetConnectionString();
|
||||
var proxy = new GenericSqlProxy(connectionString);
|
||||
if (latencyPaddingMs > 0)
|
||||
{
|
||||
proxy.OnWriteClient += (o,e) => DelayWrite(latencyPaddingMs, e);
|
||||
}
|
||||
// If running these tests in a container you may need to set a specific port
|
||||
// and expose that port in the dockerfile
|
||||
var port = testContext.Properties.ContainsKey("proxyPort")
|
||||
? Convert.ToInt32(testContext.Properties["proxyPort"])
|
||||
: 0;
|
||||
var sqlConnection = new SqlConnection(proxy.Initialize(port));
|
||||
var serverConnection = new ServerConnection(sqlConnection);
|
||||
return new ConnectionMetrics(serverConnection, proxy);
|
||||
}
|
||||
|
||||
static void DelayWrite(long delay, StreamWriteEventArgs args)
|
||||
{
|
||||
Thread.Sleep(Convert.ToInt32(delay));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,220 +1,220 @@
|
||||
using System;
|
||||
using System.Data.SqlClient;
|
||||
using System.Net.Sockets;
|
||||
using System.Net;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.SqlServer.SmoSamples
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides an in-memory proxy with callbacks that allow tests to run code before transmission and after receipt of
|
||||
/// data on the wire
|
||||
/// </summary>
|
||||
[DebuggerDisplay("{connectionString}:[{Port}]")]
|
||||
class GenericSqlProxy : IDisposable
|
||||
{
|
||||
// We pick a buffer size that's large enough to hold most single replies so we don't over-inject latency
|
||||
private const int BufferSizeBytes = 128 * 1024;
|
||||
readonly string connectionString;
|
||||
volatile bool disposed;
|
||||
private TcpListener listener = null;
|
||||
private readonly CancellationTokenSource tokenSource = new CancellationTokenSource();
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a GenericSqlProxy for the local default sql instance
|
||||
/// </summary>
|
||||
public GenericSqlProxy() : this(".")
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new GenericSqlProxy for the given connection string
|
||||
/// </summary>
|
||||
/// <param name="connectionString"></param>
|
||||
public GenericSqlProxy(string connectionString)
|
||||
{
|
||||
this.connectionString = connectionString;
|
||||
}
|
||||
|
||||
public int Port { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the proxy by opening the TCP listener and copying data between client and server
|
||||
/// </summary>
|
||||
/// <param name="localPort">local port number to use. 0 will use a random port</param>
|
||||
/// <returns>The connection string to use for the SqlConnection</returns>
|
||||
public string Initialize(int localPort = 0)
|
||||
{
|
||||
var builder = new SqlConnectionStringBuilder(connectionString);
|
||||
GetTcpInfoFromDataSource(builder.DataSource, out string hostName, out int port);
|
||||
listener = new TcpListener(IPAddress.Loopback, localPort);
|
||||
listener.Server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true);
|
||||
listener.Server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
|
||||
listener.Start();
|
||||
Port = ((IPEndPoint) listener.LocalEndpoint).Port;
|
||||
Trace.TraceInformation($"Starting TcpListener on port {Port}");
|
||||
Task.Factory.StartNew(() => { AsyncInit(listener, hostName, port); });
|
||||
return new SqlConnectionStringBuilder(builder.ConnectionString)
|
||||
{
|
||||
DataSource = $"tcp:127.0.0.1,{Port}"
|
||||
}.ConnectionString;
|
||||
}
|
||||
|
||||
private void AsyncInit(TcpListener tcpListener, string hostName, int port)
|
||||
{
|
||||
|
||||
while (!disposed)
|
||||
{
|
||||
var accept = tcpListener.AcceptTcpClientAsync();
|
||||
if (accept.Wait(1000, tokenSource.Token) && !tokenSource.IsCancellationRequested)
|
||||
{
|
||||
var localClient = accept.GetAwaiter().GetResult();
|
||||
OnConnect?.Invoke(this, new ProxyConnectionEventArgs(localClient));
|
||||
var remoteClient = new TcpClient() {NoDelay = true};
|
||||
tokenSource.Token.Register(() =>
|
||||
{
|
||||
localClient.Dispose();
|
||||
remoteClient.Dispose();
|
||||
});
|
||||
remoteClient.ConnectAsync(hostName, port).Wait(tokenSource.Token);
|
||||
if (!tokenSource.IsCancellationRequested)
|
||||
{
|
||||
|
||||
|
||||
Task.Factory.StartNew(() => { ForwardToSql(localClient, remoteClient); });
|
||||
Task.Factory.StartNew(() => { ForwardToClient(localClient, remoteClient); });
|
||||
}
|
||||
else
|
||||
{
|
||||
Trace.TraceInformation("AsyncInit aborted due to cancellation token set");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fires before the proxy writes a buffer to the host
|
||||
/// </summary>
|
||||
public event EventHandler<StreamWriteEventArgs> OnWriteHost;
|
||||
|
||||
/// <summary>
|
||||
/// Fires before the proxy writes a buffer to the client
|
||||
/// </summary>
|
||||
public event EventHandler<StreamWriteEventArgs> OnWriteClient;
|
||||
|
||||
/// <summary>
|
||||
/// Fires when a new connection to the proxy's port is accepted
|
||||
/// </summary>
|
||||
public event EventHandler<ProxyConnectionEventArgs> OnConnect;
|
||||
|
||||
private void ForwardToSql(TcpClient ourClient, TcpClient sqlClient)
|
||||
{
|
||||
long index = 0;
|
||||
try
|
||||
{
|
||||
while (!disposed)
|
||||
{
|
||||
byte[] buffer = new byte[BufferSizeBytes];
|
||||
int bytesRead = ourClient.GetStream().ReadAsync(buffer, 0, buffer.Length, tokenSource.Token).Result;
|
||||
if (!tokenSource.Token.IsCancellationRequested)
|
||||
{
|
||||
OnWriteHost?.Invoke(this, new StreamWriteEventArgs(index++, buffer, bytesRead));
|
||||
sqlClient.GetStream().Write(buffer, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Trace.TraceInformation("ForwardToSql exiting");
|
||||
}
|
||||
}
|
||||
|
||||
private void ForwardToClient(TcpClient ourClient, TcpClient sqlClient)
|
||||
{
|
||||
long index = 0;
|
||||
try
|
||||
{
|
||||
while (!disposed)
|
||||
{
|
||||
byte[] buffer = new byte[BufferSizeBytes];
|
||||
int bytesRead = sqlClient.GetStream().ReadAsync(buffer, 0, buffer.Length, tokenSource.Token).Result;
|
||||
if (!tokenSource.Token.IsCancellationRequested)
|
||||
{
|
||||
OnWriteClient?.Invoke(this, new StreamWriteEventArgs(index++, buffer, bytesRead));
|
||||
ourClient.GetStream().Write(buffer, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Trace.TraceInformation("ForwardToClient exiting");
|
||||
}
|
||||
}
|
||||
|
||||
private static void GetTcpInfoFromDataSource(string dataSource, out string hostName, out int port)
|
||||
{
|
||||
string[] dataSourceParts = dataSource.Split(',');
|
||||
if (dataSourceParts.Length == 1)
|
||||
{
|
||||
hostName = dataSourceParts[0].Replace("tcp:", "");
|
||||
port = 1433;
|
||||
}
|
||||
else if (dataSourceParts.Length == 2)
|
||||
{
|
||||
hostName = dataSourceParts[0].Replace("tcp:", "");
|
||||
port = int.Parse(dataSourceParts[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("TCP Connection String not in correct format!");
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
disposed = true;
|
||||
tokenSource.Cancel();
|
||||
Trace.TraceInformation("Disposing TcpListener on port {0}", Port);
|
||||
listener?.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
public class StreamWriteEventArgs : EventArgs
|
||||
{
|
||||
public StreamWriteEventArgs(long index, byte[]buffer, int bytesWritten)
|
||||
{
|
||||
Index = index;
|
||||
Buffer = buffer;
|
||||
BytesWritten = bytesWritten;
|
||||
}
|
||||
public long Index;
|
||||
public byte[] Buffer;
|
||||
public int BytesWritten;
|
||||
}
|
||||
|
||||
public class ProxyConnectionEventArgs : EventArgs
|
||||
{
|
||||
public ProxyConnectionEventArgs(TcpClient client)
|
||||
{
|
||||
Client = client;
|
||||
}
|
||||
public TcpClient Client;
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Data.SqlClient;
|
||||
using System.Net.Sockets;
|
||||
using System.Net;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.SqlServer.SmoSamples
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides an in-memory proxy with callbacks that allow tests to run code before transmission and after receipt of
|
||||
/// data on the wire
|
||||
/// </summary>
|
||||
[DebuggerDisplay("{connectionString}:[{Port}]")]
|
||||
class GenericSqlProxy : IDisposable
|
||||
{
|
||||
// We pick a buffer size that's large enough to hold most single replies so we don't over-inject latency
|
||||
private const int BufferSizeBytes = 128 * 1024;
|
||||
readonly string connectionString;
|
||||
volatile bool disposed;
|
||||
private TcpListener listener = null;
|
||||
private readonly CancellationTokenSource tokenSource = new CancellationTokenSource();
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a GenericSqlProxy for the local default sql instance
|
||||
/// </summary>
|
||||
public GenericSqlProxy() : this(".")
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new GenericSqlProxy for the given connection string
|
||||
/// </summary>
|
||||
/// <param name="connectionString"></param>
|
||||
public GenericSqlProxy(string connectionString)
|
||||
{
|
||||
this.connectionString = connectionString;
|
||||
}
|
||||
|
||||
public int Port { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the proxy by opening the TCP listener and copying data between client and server
|
||||
/// </summary>
|
||||
/// <param name="localPort">local port number to use. 0 will use a random port</param>
|
||||
/// <returns>The connection string to use for the SqlConnection</returns>
|
||||
public string Initialize(int localPort = 0)
|
||||
{
|
||||
var builder = new SqlConnectionStringBuilder(connectionString);
|
||||
GetTcpInfoFromDataSource(builder.DataSource, out string hostName, out int port);
|
||||
listener = new TcpListener(IPAddress.Loopback, localPort);
|
||||
listener.Server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true);
|
||||
listener.Server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
|
||||
listener.Start();
|
||||
Port = ((IPEndPoint) listener.LocalEndpoint).Port;
|
||||
Trace.TraceInformation($"Starting TcpListener on port {Port}");
|
||||
Task.Factory.StartNew(() => { AsyncInit(listener, hostName, port); });
|
||||
return new SqlConnectionStringBuilder(builder.ConnectionString)
|
||||
{
|
||||
DataSource = $"tcp:127.0.0.1,{Port}"
|
||||
}.ConnectionString;
|
||||
}
|
||||
|
||||
private void AsyncInit(TcpListener tcpListener, string hostName, int port)
|
||||
{
|
||||
|
||||
while (!disposed)
|
||||
{
|
||||
var accept = tcpListener.AcceptTcpClientAsync();
|
||||
if (accept.Wait(1000, tokenSource.Token) && !tokenSource.IsCancellationRequested)
|
||||
{
|
||||
var localClient = accept.GetAwaiter().GetResult();
|
||||
OnConnect?.Invoke(this, new ProxyConnectionEventArgs(localClient));
|
||||
var remoteClient = new TcpClient() {NoDelay = true};
|
||||
tokenSource.Token.Register(() =>
|
||||
{
|
||||
localClient.Dispose();
|
||||
remoteClient.Dispose();
|
||||
});
|
||||
remoteClient.ConnectAsync(hostName, port).Wait(tokenSource.Token);
|
||||
if (!tokenSource.IsCancellationRequested)
|
||||
{
|
||||
|
||||
|
||||
Task.Factory.StartNew(() => { ForwardToSql(localClient, remoteClient); });
|
||||
Task.Factory.StartNew(() => { ForwardToClient(localClient, remoteClient); });
|
||||
}
|
||||
else
|
||||
{
|
||||
Trace.TraceInformation("AsyncInit aborted due to cancellation token set");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fires before the proxy writes a buffer to the host
|
||||
/// </summary>
|
||||
public event EventHandler<StreamWriteEventArgs> OnWriteHost;
|
||||
|
||||
/// <summary>
|
||||
/// Fires before the proxy writes a buffer to the client
|
||||
/// </summary>
|
||||
public event EventHandler<StreamWriteEventArgs> OnWriteClient;
|
||||
|
||||
/// <summary>
|
||||
/// Fires when a new connection to the proxy's port is accepted
|
||||
/// </summary>
|
||||
public event EventHandler<ProxyConnectionEventArgs> OnConnect;
|
||||
|
||||
private void ForwardToSql(TcpClient ourClient, TcpClient sqlClient)
|
||||
{
|
||||
long index = 0;
|
||||
try
|
||||
{
|
||||
while (!disposed)
|
||||
{
|
||||
byte[] buffer = new byte[BufferSizeBytes];
|
||||
int bytesRead = ourClient.GetStream().ReadAsync(buffer, 0, buffer.Length, tokenSource.Token).Result;
|
||||
if (!tokenSource.Token.IsCancellationRequested)
|
||||
{
|
||||
OnWriteHost?.Invoke(this, new StreamWriteEventArgs(index++, buffer, bytesRead));
|
||||
sqlClient.GetStream().Write(buffer, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Trace.TraceInformation("ForwardToSql exiting");
|
||||
}
|
||||
}
|
||||
|
||||
private void ForwardToClient(TcpClient ourClient, TcpClient sqlClient)
|
||||
{
|
||||
long index = 0;
|
||||
try
|
||||
{
|
||||
while (!disposed)
|
||||
{
|
||||
byte[] buffer = new byte[BufferSizeBytes];
|
||||
int bytesRead = sqlClient.GetStream().ReadAsync(buffer, 0, buffer.Length, tokenSource.Token).Result;
|
||||
if (!tokenSource.Token.IsCancellationRequested)
|
||||
{
|
||||
OnWriteClient?.Invoke(this, new StreamWriteEventArgs(index++, buffer, bytesRead));
|
||||
ourClient.GetStream().Write(buffer, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Trace.TraceInformation("ForwardToClient exiting");
|
||||
}
|
||||
}
|
||||
|
||||
private static void GetTcpInfoFromDataSource(string dataSource, out string hostName, out int port)
|
||||
{
|
||||
string[] dataSourceParts = dataSource.Split(',');
|
||||
if (dataSourceParts.Length == 1)
|
||||
{
|
||||
hostName = dataSourceParts[0].Replace("tcp:", "");
|
||||
port = 1433;
|
||||
}
|
||||
else if (dataSourceParts.Length == 2)
|
||||
{
|
||||
hostName = dataSourceParts[0].Replace("tcp:", "");
|
||||
port = int.Parse(dataSourceParts[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("TCP Connection String not in correct format!");
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
disposed = true;
|
||||
tokenSource.Cancel();
|
||||
Trace.TraceInformation("Disposing TcpListener on port {0}", Port);
|
||||
listener?.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
public class StreamWriteEventArgs : EventArgs
|
||||
{
|
||||
public StreamWriteEventArgs(long index, byte[]buffer, int bytesWritten)
|
||||
{
|
||||
Index = index;
|
||||
Buffer = buffer;
|
||||
BytesWritten = bytesWritten;
|
||||
}
|
||||
public long Index;
|
||||
public byte[] Buffer;
|
||||
public int BytesWritten;
|
||||
}
|
||||
|
||||
public class ProxyConnectionEventArgs : EventArgs
|
||||
{
|
||||
public ProxyConnectionEventArgs(TcpClient client)
|
||||
{
|
||||
Client = client;
|
||||
}
|
||||
public TcpClient Client;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +1,29 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup Label="Configuration">
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<IsPackable>false</IsPackable>
|
||||
<ApplicationIcon />
|
||||
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<DefaultNamespace>Microsoft.SqlSerer.SmoSamples</DefaultNamespace>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RootNamespace>Microsoft.SqlServer.SmoSamples</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Remove="out\**" />
|
||||
<EmbeddedResource Remove="out\**" />
|
||||
<None Remove="out\**" />
|
||||
</ItemGroup>
|
||||
<!-- <ItemGroup>
|
||||
<None Remove="ScriptOutput_Standalone_Linux.baseline.txt" />
|
||||
<None Remove="ScriptOutput_Standalone_Win.baseline.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="ScriptOutput_Standalone_Linux.baseline.txt" />
|
||||
<EmbeddedResource Include="ScriptOutput_Standalone_Win.baseline.txt" />
|
||||
</ItemGroup> -->
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Data.SqlClient" Version="4.6.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
|
||||
<PackageReference Include="Microsoft.SqlServer.SqlManagementObjects" Version="150.18118.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="1.4.0" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="1.4.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0" />
|
||||
<PackageReference Include="NUnit" Version="3.11.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup Label="Configuration">
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<IsPackable>false</IsPackable>
|
||||
<ApplicationIcon />
|
||||
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<DefaultNamespace>Microsoft.SqlSerer.SmoSamples</DefaultNamespace>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RootNamespace>Microsoft.SqlServer.SmoSamples</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Remove="out\**" />
|
||||
<EmbeddedResource Remove="out\**" />
|
||||
<None Remove="out\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Data.SqlClient" Version="4.6.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
|
||||
<PackageReference Include="Microsoft.SqlServer.SqlManagementObjects" Version="150.18118.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="1.4.0" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="1.4.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0" />
|
||||
<PackageReference Include="NUnit" Version="3.11.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.28307.572
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SmoSamples", "SmoSamples.csproj", "{7923416F-F384-458E-991C-65AD376F54D0}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{7923416F-F384-458E-991C-65AD376F54D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7923416F-F384-458E-991C-65AD376F54D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7923416F-F384-458E-991C-65AD376F54D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7923416F-F384-458E-991C-65AD376F54D0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {D78231A7-6CE4-407D-B13E-DC6A7F972E3C}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RunSettings>
|
||||
<TestRunParameters>
|
||||
<Parameter name="connectionString" value="server=localhost;User Id=sa;Password=[password];Timeout=60" />
|
||||
<Parameter name="testDatabase" value="WideWorldImporters" />
|
||||
<Parameter name="proxyPort" value="0" />
|
||||
</TestRunParameters>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RunSettings>
|
||||
<TestRunParameters>
|
||||
<Parameter name="connectionString" value="server=localhost;User Id=sa;Password=[password];Timeout=60" />
|
||||
<Parameter name="testDatabase" value="WideWorldImporters" />
|
||||
<Parameter name="proxyPort" value="0" />
|
||||
</TestRunParameters>
|
||||
</RunSettings>
|
||||
Reference in New Issue
Block a user