Smosamples (#2)

* initial SMO sample skeleton project

* add linux test runner

* change namepsace

* add Urn tests

* Complete the collection sample

* fix a comment
This commit is contained in:
David Shiflet
2019-04-30 23:33:10 -04:00
committed by GitHub
parent 9740f4d1ba
commit b765d311c1
15 changed files with 726 additions and 0 deletions
@@ -0,0 +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/>
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/>
@@ -0,0 +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"]
@@ -0,0 +1,2 @@
/opt/mssql/bin/sqlservr & /tmp/backup/restore.sh
tail -f /dev/null
@@ -0,0 +1,4 @@
sleep 35s
echo sa_password is $SA_PASSWORD
/opt/mssql-tools/bin/sqlcmd -S . -U sa -P $SA_PASSWORD -i /tmp/backup/restore.sql
@@ -0,0 +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"
TO "/var/opt/mssql/data/WideWorldImporters_InMemory_Data_1"
@@ -0,0 +1,17 @@
@echo off
set pwd=Passwd__%random%
echo Building the SQL Linux Docker container
docker pull mcr.microsoft.com/mssql/server:2017-latest
docker build -t sqllinux prep
echo Running the SQL linux docker image
start cmd /k docker run -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=%pwd%" -e "MSSQL_SA_PASSWORD=%pwd%" -h sqlserver --name sqlserver -p:1433:1433 --rm sqllinux
echo Waiting 90 seconds for SQL server to restore WideWorldImporters
timeout /t 90
setlocal
echo running tests against SQL 2017 database WideWorldImporters
set TEST_PASSWORD=%pwd%
dotnet publish src -o out
dotnet vstest src\out\SmoSamples.dll /logger:console /settings:src\localhost.runsettings
endlocal
echo Terminating docker container
docker kill sqlserver
@@ -0,0 +1,14 @@
pwd=Pwd$RANDOM
echo Building the SQL Linux Docker container
docker pull mcr.microsoft.com/mssql/server:2017-latest
docker build -t sqllinux prep
echo Running the SQL linux docker image
docker run -e ACCEPT_EULA=Y -e SA_PASSWORD=$pwd -e MSSQL_SA_PASSWORD=$pwd -h sqlserver --name sqlserver -p:1433:1433 -d --rm sqllinux
echo Waiting 2 minutes for SQL server to restore WideWorldImporters
sleep 120
echo running tests against SQL 2017 database WideWorldImporters
export TEST_PASSWORD=$pwd
dotnet publish src
dotnet vstest src/bin/Debug/netcoreapp2.1/SmoSamples.dll --logger:console --Settings:src/localhost.runsettings
echo Terminating docker container
docker kill sqlserver
@@ -0,0 +1,64 @@
using System.Diagnostics;
using Microsoft.SqlServer.Management.Smo;
namespace Microsoft.SqlServer.SmoSamples
{
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NUnit.Framework;
using Assert = NUnit.Framework.Assert;
[TestClass]
public class CollectionSamples
{
public VisualStudio.TestTools.UnitTesting.TestContext TestContext { get; set; }
[TestMethod]
public void Collection_iteration_is_faster_with_SetDefaultInitFields()
{
using (var connectionMetrics = ConnectionMetrics.SetupMeasuredConnection(TestContext, 50))
{
var server = new Management.Smo.Server(connectionMetrics.ServerConnection);
var database = server.Databases[TestContext.GetTestDatabaseName()];
connectionMetrics.Reset();
foreach (Table table in database.Tables)
{
Trace.TraceInformation(
$"Unoptimized table Name: {table.Name}\tSchema:{table.Schema}\tFileGroup:{table.FileGroup}");
}
var unoptimizedMetrics = (connectionMetrics.QueryCount, connectionMetrics.BytesSent, connectionMetrics.BytesRead, connectionMetrics.ConnectionCount);
Trace.TraceInformation(string.Join($"{Environment.NewLine}\t", new[]
{
"Unoptimized metrics:",
$"QueryCount:{unoptimizedMetrics.QueryCount}", $"ConnectionCount:{unoptimizedMetrics.ConnectionCount}",
$"BytesSent:{unoptimizedMetrics.BytesSent}", $"BytesRead:{unoptimizedMetrics.BytesRead}"
}));
connectionMetrics.Reset();
server.SetDefaultInitFields(typeof(Table), "Name", "Schema", "FileGroup");
database.Tables.Refresh();
foreach (Table table in database.Tables)
{
Trace.TraceInformation(
$"Optimized table Name: {table.Name}\tSchema:{table.Schema}\tFileGroup:{table.FileGroup}");
}
var optimizedMetrics = (connectionMetrics.QueryCount, connectionMetrics.BytesSent, connectionMetrics.BytesRead, connectionMetrics.ConnectionCount);
Trace.TraceInformation(string.Join($"{Environment.NewLine}\t", new[]
{
"Optimized Metrics:",
$"QueryCount:{optimizedMetrics.QueryCount}", $"ConnectionCount:{optimizedMetrics.ConnectionCount}",
$"BytesSent:{optimizedMetrics.BytesSent}", $"BytesRead:{optimizedMetrics.BytesRead}"
}));
Assert.That(optimizedMetrics.BytesRead, Is.LessThan(unoptimizedMetrics.BytesRead), "BytesRead");
Assert.That(optimizedMetrics.BytesSent, Is.LessThan(unoptimizedMetrics.BytesSent), "BytesSent");
Assert.That(optimizedMetrics.ConnectionCount, Is.AtMost(unoptimizedMetrics.ConnectionCount), "ConnectionCount");
Assert.That(optimizedMetrics.QueryCount, Is.LessThan(unoptimizedMetrics.QueryCount), "QueryCount");
}
}
}
}
@@ -0,0 +1,135 @@
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
}
}
@@ -0,0 +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));
}
}
}
@@ -0,0 +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;
}
}
@@ -0,0 +1,36 @@
<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>
@@ -0,0 +1,51 @@
using Microsoft.SqlServer.Management.Smo;
namespace Microsoft.SqlServer.SmoSamples
{
using VisualStudio.TestTools.UnitTesting;
using Management.Sdk.Sfc;
using NUnit.Framework;
using Assert=NUnit.Framework.Assert;
[TestClass]
public class UrnSamples
{
public VisualStudio.TestTools.UnitTesting.TestContext TestContext {get;set;}
[TestMethod]
public virtual void Urn_attribute_values_require_escaping()
{
var connection = TestContext.GetTestConnection();
var server = new Management.Smo.Server(connection);
TestContext.ExecuteWithDbDrop((database) =>
{
var table = new Table(database, "Name'With'Quotes");
table.Columns.Add(new Column(table, "col1", DataType.Int));
table.Create();
Assert.That(table.Urn.GetNameForType(Table.UrnSuffix), Is.EqualTo("Name'With'Quotes"), "Urn Value");
Assert.Throws<FailedOperationException>(() =>
table = (Table) server.GetSmoObject(
$"Server/Database[@Name='{database.Name}']/Table[@Name='Name'With'Quotes']"));
table = (Table)server.GetSmoObject(
$"Server/Database[@Name='{database.Name}']/Table[@Name='{Urn.EscapeString("Name'With'Quotes")}']");
Assert.That(table.Name, Is.EqualTo("Name'With'Quotes"), "Table with escaped name");
});
}
[TestMethod]
public virtual void Server_Urn_has_Name_matching_InstanceName()
{
var connection = TestContext.GetTestConnection();
var server = new Management.Smo.Server(connection);
Assert.That(server.Urn.Value, Is.EqualTo($"Server[@Name='{Urn.EscapeString(connection.TrueName)}']"), "Server URN");
}
[TestMethod]
public virtual void Urn_Type_is_the_last_item()
{
var urn = new Urn("Server[@Name='server']/Database[@Name='database']/Table[@Name='table']");
Assert.That(urn.Type, Is.EqualTo(Table.UrnSuffix), "Urn Type");
}
}
}
@@ -0,0 +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>
</RunSettings>