diff --git a/BUILDGUIDE.md b/BUILDGUIDE.md
index 1ebb710197..1943ef0bbc 100644
--- a/BUILDGUIDE.md
+++ b/BUILDGUIDE.md
@@ -119,7 +119,7 @@ Manual Tests require the below setup to run:
|AzureKeyVaultClientSecret | (Optional) "Client Secret" of the Active Directory registered application, granted access to the Azure Key Vault specified in `AZURE_KEY_VAULT_URL` | _{Client Application Secret}_ |
|LocalDbAppName | (Optional) If Local Db Testing is supported, this property configures the name of Local DB App instance available in client environment. Empty string value disables Local Db testing. | Name of Local Db App to connect to.|
|SupportsIntegratedSecurity | (Optional) Whether or not the USER running tests has integrated security access to the target SQL Server.| `true` OR `false`|
- |SupportsFileStream | (Optional) Whether or not FileStream is enabled on SQL Server| `true` OR `false`|
+ |FileStreamDirectory | (Optional) If File Stream is enabled on SQL Server, pass local directory path to be used for setting up File Stream enabled database. | `D:\\escaped\\absolute\\path\\to\\directory\\` |
|UseManagedSNIOnWindows | (Optional) Enables testing with Managed SNI on Windows| `true` OR `false`|
|IsAzureSynpase | (Optional) When set to 'true', test suite runs compatible tests for Azure Synapse/Parallel Data Warehouse. | `true` OR `false`|
diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.NetCoreApp.cs
deleted file mode 100644
index c925335791..0000000000
--- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.NetCoreApp.cs
+++ /dev/null
@@ -1,60 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-// ------------------------------------------------------------------------------
-// Changes to this file must follow the http://aka.ms/api-review process.
-// ------------------------------------------------------------------------------
-
-namespace Microsoft.Data.SqlTypes
-{
- ///
- public sealed partial class SqlFileStream : System.IO.Stream
- {
- ///
- public SqlFileStream(string path, byte[] transactionContext, System.IO.FileAccess access) { }
- ///
- public SqlFileStream(string path, byte[] transactionContext, System.IO.FileAccess access, System.IO.FileOptions options, System.Int64 allocationSize) { }
- ///
- public string Name { get { throw null; } }
- ///
- public byte[] TransactionContext { get { throw null; } }
- ///
- public override bool CanRead { get { throw null; } }
- ///
- public override bool CanSeek { get { throw null; } }
- ///
- public override bool CanTimeout { get { throw null; } }
- ///
- public override bool CanWrite { get { throw null; } }
- ///
- public override long Length { get { throw null; } }
- ///
- public override long Position { get { throw null; } set { throw null; } }
- ///
- public override int ReadTimeout { get { throw null; } }
- ///
- public override int WriteTimeout { get { throw null; } }
- ///
- public override void Flush() { }
- ///
- public override System.IAsyncResult BeginRead(byte[] buffer, int offset, int count, System.AsyncCallback callback, object state) { throw null; }
- ///
- public override int EndRead(System.IAsyncResult asyncResult) { throw null; }
- ///
- public override System.IAsyncResult BeginWrite(byte[] buffer, int offset, int count, System.AsyncCallback callback, System.Object state) { throw null; }
- ///
- public override void EndWrite(System.IAsyncResult asyncResult) { }
- ///
- public override long Seek(long offset, System.IO.SeekOrigin origin) { throw null; }
- ///
- public override void SetLength(long value) { throw null; }
- ///
- public override int Read(byte[] buffer, int offset, int count) { throw null; }
- ///
- public override int ReadByte() { throw null; }
- ///
- public override void Write(byte[] buffer, int offset, int count) { throw null; }
- ///
- public override void WriteByte(byte value) { }
- }
-}
diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs
index 659f490053..fbcfa45e9d 100644
--- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs
+++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs
@@ -28,6 +28,59 @@ public SqlNotificationRequest(string userData, string options, int timeout) { }
public string UserData { get { throw null; } set { } }
}
}
+namespace Microsoft.Data.SqlTypes
+{
+ ///
+ public sealed partial class SqlFileStream : System.IO.Stream
+ {
+ ///
+ public SqlFileStream(string path, byte[] transactionContext, System.IO.FileAccess access) { }
+ ///
+ public SqlFileStream(string path, byte[] transactionContext, System.IO.FileAccess access, System.IO.FileOptions options, System.Int64 allocationSize) { }
+ ///
+ public string Name { get { throw null; } }
+ ///
+ public byte[] TransactionContext { get { throw null; } }
+ ///
+ public override bool CanRead { get { throw null; } }
+ ///
+ public override bool CanSeek { get { throw null; } }
+ ///
+ public override bool CanTimeout { get { throw null; } }
+ ///
+ public override bool CanWrite { get { throw null; } }
+ ///
+ public override long Length { get { throw null; } }
+ ///
+ public override long Position { get { throw null; } set { throw null; } }
+ ///
+ public override int ReadTimeout { get { throw null; } }
+ ///
+ public override int WriteTimeout { get { throw null; } }
+ ///
+ public override void Flush() { }
+ ///
+ public override System.IAsyncResult BeginRead(byte[] buffer, int offset, int count, System.AsyncCallback callback, object state) { throw null; }
+ ///
+ public override int EndRead(System.IAsyncResult asyncResult) { throw null; }
+ ///
+ public override System.IAsyncResult BeginWrite(byte[] buffer, int offset, int count, System.AsyncCallback callback, System.Object state) { throw null; }
+ ///
+ public override void EndWrite(System.IAsyncResult asyncResult) { }
+ ///
+ public override long Seek(long offset, System.IO.SeekOrigin origin) { throw null; }
+ ///
+ public override void SetLength(long value) { throw null; }
+ ///
+ public override int Read(byte[] buffer, int offset, int count) { throw null; }
+ ///
+ public override int ReadByte() { throw null; }
+ ///
+ public override void Write(byte[] buffer, int offset, int count) { throw null; }
+ ///
+ public override void WriteByte(byte value) { }
+ }
+}
namespace Microsoft.Data.SqlClient
{
///
diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj
index 3d70bf7dce..c56cf83540 100644
--- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj
@@ -23,7 +23,6 @@
-
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
index f42f2ecd05..4099202001 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
@@ -575,12 +575,12 @@
-
-
+
+
-
-
+
+
Common\CoreLib\Interop\Windows\kernel32\Interop.FileTypes.cs
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlTypes/SqlFileStream.Windows.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlTypes/SqlFileStream.Windows.cs
index d52bfadf30..017320168b 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlTypes/SqlFileStream.Windows.cs
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlTypes/SqlFileStream.Windows.cs
@@ -689,8 +689,13 @@ static private string InitializeNtPath(string path)
// Ensure we have validated and normalized the path before
AssertPathFormat(path);
string uniqueId = Guid.NewGuid().ToString("N");
- return System.IO.PathInternal.IsDeviceUNC(path) ? string.Format(CultureInfo.InvariantCulture, @"{0}\{1}", path.Replace(@"\\.", @"\??"), uniqueId)
- : string.Format(CultureInfo.InvariantCulture, @"\??\UNC\{0}\{1}", path.Trim('\\'), uniqueId);
+#if NETSTANDARD
+ return System.IO.PathInternal.IsDeviceUNC(path.AsSpan())
+#else
+ return System.IO.PathInternal.IsDeviceUNC(path)
+#endif
+ ? string.Format(CultureInfo.InvariantCulture, @"{0}\{1}", path.Replace(@"\\.", @"\??"), uniqueId)
+ : string.Format(CultureInfo.InvariantCulture, @"\??\UNC\{0}\{1}", path.Trim('\\'), uniqueId);
}
}
}
diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj
index 645c11928e..b74f39f60c 100644
--- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj
+++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj
@@ -8,6 +8,7 @@
$(DefineConstants);NETFX
$(DefineConstants);NETCOREAPP
$(DefineConstants);NET50_OR_LATER
+ NETSTANDARDREFERNCE
$(ObjFolder)$(Configuration).$(Platform).$(AssemblyName)
$(BinFolder)$(Configuration).$(Platform).$(AssemblyName)
diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlCommandTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlCommandTest.cs
index 79b8c08df0..df97e95fcd 100644
--- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlCommandTest.cs
+++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlCommandTest.cs
@@ -125,7 +125,7 @@ public void Constructor3()
Assert.Null(cmd.Container);
Assert.True(cmd.DesignTimeVisible);
Assert.Null(cmd.Notification);
-#if NETFX
+#if NETFX && !NETSTANDARDREFERNCE
// see https://github.com/dotnet/SqlClient/issues/17
Assert.True(cmd.NotificationAutoEnlist);
#endif
@@ -166,7 +166,7 @@ public void Constructor4()
Assert.Null(cmd.Container);
Assert.True(cmd.DesignTimeVisible);
Assert.Null(cmd.Notification);
-#if NETFX
+#if NETFX && !NETSTANDARDREFERNCE
// see https://github.com/dotnet/SqlClient/issues/17
Assert.True(cmd.NotificationAutoEnlist);
#endif
@@ -184,7 +184,7 @@ public void Constructor4()
Assert.Null(cmd.Container);
Assert.True(cmd.DesignTimeVisible);
Assert.Null(cmd.Notification);
-#if NETFX
+#if NETFX && !NETSTANDARDREFERNCE
// see https://github.com/dotnet/SqlClient/issues/17
Assert.True(cmd.NotificationAutoEnlist);
#endif
@@ -202,7 +202,7 @@ public void Constructor4()
Assert.Null(cmd.Container);
Assert.True(cmd.DesignTimeVisible);
Assert.Null(cmd.Notification);
-#if NETFX
+#if NETFX && !NETSTANDARDREFERNCE
// see https://github.com/dotnet/SqlClient/issues/17
Assert.True(cmd.NotificationAutoEnlist);
#endif
@@ -224,7 +224,7 @@ public void Clone()
cmd.CommandType = CommandType.StoredProcedure;
cmd.DesignTimeVisible = false;
cmd.Notification = notificationReq;
-#if NETFX
+#if NETFX && !NETSTANDARDREFERNCE
// see https://github.com/dotnet/SqlClient/issues/17
Assert.True(cmd.NotificationAutoEnlist);
#endif
@@ -240,7 +240,7 @@ public void Clone()
Assert.Null(cmd.Connection);
Assert.False(cmd.DesignTimeVisible);
Assert.Same(notificationReq, cmd.Notification);
-#if NETFX
+#if NETFX && !NETSTANDARDREFERNCE
// see https://github.com/dotnet/SqlClient/issues/17
Assert.True(cmd.NotificationAutoEnlist);
#endif
diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs
index 8ea16c9a83..b60b10e5d8 100644
--- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs
+++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs
@@ -44,10 +44,10 @@ public static class DataTestUtility
public static bool EnclaveEnabled { get; private set; } = false;
public static readonly bool TracingEnabled = false;
public static readonly bool SupportsIntegratedSecurity = false;
- public static readonly bool SupportsFileStream = false;
public static readonly bool UseManagedSNIOnWindows = false;
public static readonly bool IsAzureSynapse = false;
public static Uri AKVBaseUri = null;
+ public static string FileStreamDirectory = null;
public static readonly string DNSCachingConnString = null;
public static readonly string DNSCachingServerCR = null; // this is for the control ring
@@ -85,7 +85,7 @@ static DataTestUtility()
AADServicePrincipalSecret = c.AADServicePrincipalSecret;
LocalDbAppName = c.LocalDbAppName;
SupportsIntegratedSecurity = c.SupportsIntegratedSecurity;
- SupportsFileStream = c.SupportsFileStream;
+ FileStreamDirectory = c.FileStreamDirectory;
EnclaveEnabled = c.EnclaveEnabled;
TracingEnabled = c.TracingEnabled;
UseManagedSNIOnWindows = c.UseManagedSNIOnWindows;
@@ -437,10 +437,8 @@ public static void DropStoredProcedure(SqlConnection sqlConnection, string spNam
/// Database name without brackets.
public static void DropDatabase(SqlConnection sqlConnection, string dbName)
{
- using (SqlCommand cmd = new SqlCommand(string.Format("IF (EXISTS(SELECT 1 FROM sys.databases WHERE name = '{0}')) \nBEGIN \n ALTER DATABASE [{0}] SET SINGLE_USER WITH ROLLBACK IMMEDIATE \n DROP DATABASE [{0}] \nEND", dbName), sqlConnection))
- {
- cmd.ExecuteNonQuery();
- }
+ using SqlCommand cmd = new(string.Format("IF (EXISTS(SELECT 1 FROM sys.databases WHERE name = '{0}')) \nBEGIN \n ALTER DATABASE [{0}] SET SINGLE_USER WITH ROLLBACK IMMEDIATE \n DROP DATABASE [{0}] \nEND", dbName), sqlConnection);
+ cmd.ExecuteNonQuery();
}
public static bool IsLocalDBInstalled() => !string.IsNullOrEmpty(LocalDbAppName?.Trim());
@@ -492,7 +490,7 @@ public static string GetUserIdentityAccessToken()
public static bool IsUserIdentityTokenSetup() => !string.IsNullOrEmpty(GetUserIdentityAccessToken());
- public static bool IsFileStreamSetup() => SupportsFileStream;
+ public static bool IsFileStreamSetup() => !string.IsNullOrEmpty(FileStreamDirectory);
private static bool CheckException(Exception ex, string exceptionMessage, bool innerExceptionMustBeNull) where TException : Exception
{
diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj
index a308ef7520..5f871d93b4 100644
--- a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj
+++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj
@@ -200,6 +200,7 @@
+
@@ -261,9 +262,6 @@
-
-
-
diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlFileStreamTest/SqlFileStreamTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlFileStreamTest/SqlFileStreamTest.cs
index 24dde457e5..1bf5bde2a1 100644
--- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlFileStreamTest/SqlFileStreamTest.cs
+++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlFileStreamTest/SqlFileStreamTest.cs
@@ -16,53 +16,68 @@ public static class SqlFileStreamTest
private static bool AreConnectionStringsSetup() => DataTestUtility.AreConnStringsSetup();
private static bool IsIntegratedSecurityEnvironmentSet() => DataTestUtility.IsIntegratedSecuritySetup();
- private static int[] s_insertedValues = { 11 , 22 };
+ private static int[] s_insertedValues = { 11, 22 };
+ private static string s_fileStreamDBName = null;
[PlatformSpecific(TestPlatforms.Windows)]
[ConditionalFact(nameof(IsFileStreamEnvironmentSet), nameof(IsIntegratedSecurityEnvironmentSet), nameof(AreConnectionStringsSetup))]
public static void ReadFilestream()
{
- using (SqlConnection connection = new SqlConnection(DataTestUtility.TCPConnectionString))
+ try
{
+ string connString = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString)
+ {
+ InitialCatalog = SetupFileStreamDB(ref DataTestUtility.FileStreamDirectory, DataTestUtility.TCPConnectionString)
+ }.ConnectionString;
+
+ using SqlConnection connection = new(connString);
connection.Open();
string tempTable = SetupTable(connection);
int nRow = 0;
byte[] retrievedValue;
- SqlCommand command = new SqlCommand($"SELECT Photo.PathName(), GET_FILESTREAM_TRANSACTION_CONTEXT(),EmployeeId FROM {tempTable} ORDER BY EmployeeId", connection);
-
- SqlTransaction transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted);
- command.Transaction = transaction;
-
- using (SqlDataReader reader = command.ExecuteReader())
+ SqlCommand command = new($"SELECT Photo.PathName(), GET_FILESTREAM_TRANSACTION_CONTEXT(),EmployeeId FROM {tempTable} ORDER BY EmployeeId", connection);
+ try
{
- while (reader.Read())
+ SqlTransaction transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted);
+ command.Transaction = transaction;
+
+ using (SqlDataReader reader = command.ExecuteReader())
{
- // Get the pointer for the file.
- string path = reader.GetString(0);
- byte[] transactionContext = reader.GetSqlBytes(1).Buffer;
-
- // Create the SqlFileStream
- using (Stream fileStream = new SqlFileStream(path, transactionContext, FileAccess.Read, FileOptions.SequentialScan, allocationSize: 0))
+ while (reader.Read())
{
- // Read the contents as bytes.
- retrievedValue = new byte[fileStream.Length];
- fileStream.Read(retrievedValue,0,(int)(fileStream.Length));
-
- // Reverse the byte array, if the system architecture is little-endian.
- if (BitConverter.IsLittleEndian)
- Array.Reverse(retrievedValue);
-
- // Compare inserted and retrieved values.
- Assert.Equal(s_insertedValues[nRow], BitConverter.ToInt32(retrievedValue,0));
+ // Get the pointer for the file.
+ string path = reader.GetString(0);
+ byte[] transactionContext = reader.GetSqlBytes(1).Buffer;
+
+ // Create the SqlFileStream
+ using (Stream fileStream = new SqlFileStream(path, transactionContext, FileAccess.Read, FileOptions.SequentialScan, allocationSize: 0))
+ {
+ // Read the contents as bytes.
+ retrievedValue = new byte[fileStream.Length];
+ fileStream.Read(retrievedValue, 0, (int)(fileStream.Length));
+
+ // Reverse the byte array, if the system architecture is little-endian.
+ if (BitConverter.IsLittleEndian)
+ Array.Reverse(retrievedValue);
+
+ // Compare inserted and retrieved values.
+ Assert.Equal(s_insertedValues[nRow], BitConverter.ToInt32(retrievedValue, 0));
+ }
+ nRow++;
}
- nRow++;
+
}
-
+ transaction.Commit();
+ }
+ finally
+ {
+ // Drop Table
+ ExecuteNonQueryCommand($"DROP TABLE {tempTable}", connection);
}
- transaction.Commit();
-
- // Drop Table
- ExecuteNonQueryCommand($"DROP TABLE {tempTable}", connection);
+ }
+ finally
+ {
+ DropFileStreamDb(ref DataTestUtility.FileStreamDirectory, DataTestUtility.TCPConnectionString);
}
}
@@ -70,8 +85,14 @@ public static void ReadFilestream()
[ConditionalFact(nameof(IsFileStreamEnvironmentSet), nameof(IsIntegratedSecurityEnvironmentSet), nameof(AreConnectionStringsSetup))]
public static void OverwriteFilestream()
{
- using (SqlConnection connection = new SqlConnection(DataTestUtility.TCPConnectionString))
+ try
{
+ string connString = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString)
+ {
+ InitialCatalog = SetupFileStreamDB(ref DataTestUtility.FileStreamDirectory, DataTestUtility.TCPConnectionString)
+ }.ConnectionString;
+
+ using SqlConnection connection = new(connString);
connection.Open();
string tempTable = SetupTable(connection);
byte[] insertedValue = BitConverter.GetBytes(3);
@@ -79,36 +100,42 @@ public static void OverwriteFilestream()
// Reverse the byte array, if the system architecture is little-endian.
if (BitConverter.IsLittleEndian)
Array.Reverse(insertedValue);
+ try
+ {
+ SqlCommand command = new($"SELECT TOP(1) Photo.PathName(), GET_FILESTREAM_TRANSACTION_CONTEXT(),EmployeeId FROM {tempTable} ORDER BY EmployeeId", connection);
- SqlCommand command = new SqlCommand($"SELECT TOP(1) Photo.PathName(), GET_FILESTREAM_TRANSACTION_CONTEXT(),EmployeeId FROM {tempTable} ORDER BY EmployeeId", connection);
-
- SqlTransaction transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted);
- command.Transaction = transaction;
+ SqlTransaction transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted);
+ command.Transaction = transaction;
- using (SqlDataReader reader = command.ExecuteReader())
- {
- while (reader.Read())
+ using (SqlDataReader reader = command.ExecuteReader())
{
- // Get the pointer for file
- string path = reader.GetString(0);
- byte[] transactionContext = reader.GetSqlBytes(1).Buffer;
-
- // Create the SqlFileStream
- using (Stream fileStream = new SqlFileStream(path, transactionContext, FileAccess.Write, FileOptions.SequentialScan, allocationSize: 0))
+ while (reader.Read())
{
+ // Get the pointer for file
+ string path = reader.GetString(0);
+ byte[] transactionContext = reader.GetSqlBytes(1).Buffer;
+
+ // Create the SqlFileStream
+ using Stream fileStream = new SqlFileStream(path, transactionContext, FileAccess.Write, FileOptions.SequentialScan, allocationSize: 0);
// Overwrite the first row in the table
fileStream.Write((insertedValue), 0, 4);
}
}
+ transaction.Commit();
+
+ // Compare inserted and retrieved value
+ byte[] retrievedValue = RetrieveData(tempTable, connection, insertedValue.Length);
+ Assert.Equal(insertedValue, retrievedValue);
+ }
+ finally
+ {
+ // Drop Table
+ ExecuteNonQueryCommand($"DROP TABLE {tempTable}", connection);
}
- transaction.Commit();
-
- // Compare inserted and retrieved value
- byte[] retrievedValue = RetrieveData(tempTable, connection, insertedValue.Length);
- Assert.Equal(insertedValue, retrievedValue);
-
- // Drop Table
- ExecuteNonQueryCommand($"DROP TABLE {tempTable}", connection);
+ }
+ finally
+ {
+ DropFileStreamDb(ref DataTestUtility.FileStreamDirectory, DataTestUtility.TCPConnectionString);
}
}
@@ -116,8 +143,14 @@ public static void OverwriteFilestream()
[ConditionalFact(nameof(IsFileStreamEnvironmentSet), nameof(IsIntegratedSecurityEnvironmentSet), nameof(AreConnectionStringsSetup))]
public static void AppendFilestream()
{
- using (SqlConnection connection = new SqlConnection(DataTestUtility.TCPConnectionString))
+ try
{
+ string connString = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString)
+ {
+ InitialCatalog = SetupFileStreamDB(ref DataTestUtility.FileStreamDirectory, DataTestUtility.TCPConnectionString)
+ }.ConnectionString;
+
+ using SqlConnection connection = new(connString);
connection.Open();
string tempTable = SetupTable(connection);
@@ -129,21 +162,22 @@ public static void AppendFilestream()
if (BitConverter.IsLittleEndian)
Array.Reverse(insertedValue);
- SqlCommand command = new SqlCommand($"SELECT TOP(1) Photo.PathName(), GET_FILESTREAM_TRANSACTION_CONTEXT(),EmployeeId FROM {tempTable} ORDER BY EmployeeId", connection);
+ try
+ {
+ SqlCommand command = new($"SELECT TOP(1) Photo.PathName(), GET_FILESTREAM_TRANSACTION_CONTEXT(),EmployeeId FROM {tempTable} ORDER BY EmployeeId", connection);
- SqlTransaction transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted);
- command.Transaction = transaction;
+ SqlTransaction transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted);
+ command.Transaction = transaction;
- using (SqlDataReader reader = command.ExecuteReader())
- {
- while (reader.Read())
+ using (SqlDataReader reader = command.ExecuteReader())
{
- // Get the pointer for file
- string path = reader.GetString(0);
- byte[] transactionContext = reader.GetSqlBytes(1).Buffer;
-
- using (Stream fileStream = new SqlFileStream(path, transactionContext, FileAccess.ReadWrite, FileOptions.SequentialScan, allocationSize: 0))
+ while (reader.Read())
{
+ // Get the pointer for file
+ string path = reader.GetString(0);
+ byte[] transactionContext = reader.GetSqlBytes(1).Buffer;
+
+ using Stream fileStream = new SqlFileStream(path, transactionContext, FileAccess.ReadWrite, FileOptions.SequentialScan, allocationSize: 0);
// Seek to the end of the file
fileStream.Seek(0, SeekOrigin.End);
@@ -151,18 +185,81 @@ public static void AppendFilestream()
fileStream.WriteByte(appendedByte);
}
}
- }
- transaction.Commit();
+ transaction.Commit();
- // Compare inserted and retrieved value
- byte[] retrievedValue = RetrieveData(tempTable, connection, insertedValue.Length);
- Assert.Equal(insertedValue, retrievedValue);
+ // Compare inserted and retrieved value
+ byte[] retrievedValue = RetrieveData(tempTable, connection, insertedValue.Length);
+ Assert.Equal(insertedValue, retrievedValue);
- // Drop Table
- ExecuteNonQueryCommand($"DROP TABLE {tempTable}", connection);
+ }
+ finally
+ {
+ // Drop Table
+ ExecuteNonQueryCommand($"DROP TABLE {tempTable}", connection);
+ }
+ }
+ finally
+ {
+ DropFileStreamDb(ref DataTestUtility.FileStreamDirectory, DataTestUtility.TCPConnectionString);
}
}
+
#region Private helper methods
+
+ private static string SetupFileStreamDB(ref string fileStreamDir, string connString)
+ {
+ try
+ {
+ if (fileStreamDir != null)
+ {
+ if (!fileStreamDir.EndsWith("\\"))
+ {
+ fileStreamDir += "\\";
+ }
+
+ string dbName = DataTestUtility.GetUniqueName("FS", false);
+ string createDBQuery = @$"CREATE DATABASE [{dbName}]
+ ON PRIMARY
+ (NAME = PhotoLibrary_data,
+ FILENAME = '{fileStreamDir}PhotoLibrary_data.mdf'),
+ FILEGROUP FileStreamGroup CONTAINS FILESTREAM
+ (NAME = PhotoLibrary_blobs,
+ FILENAME = '{fileStreamDir}Photos')
+ LOG ON
+ (NAME = PhotoLibrary_log,
+ FILENAME = '{fileStreamDir}PhotoLibrary_log.ldf')";
+ using SqlConnection con = new(new SqlConnectionStringBuilder(connString) { InitialCatalog = "master" }.ConnectionString);
+ con.Open();
+ using SqlCommand cmd = con.CreateCommand();
+ cmd.CommandText = createDBQuery;
+ cmd.ExecuteNonQuery();
+ s_fileStreamDBName = dbName;
+ }
+ }
+ catch (SqlException e)
+ {
+ Console.WriteLine("File Stream database could not be setup. " + e.Message);
+ fileStreamDir = null;
+ }
+ return s_fileStreamDBName;
+ }
+
+ private static void DropFileStreamDb(ref string fileStreamDir, string connString)
+ {
+ try
+ {
+ using SqlConnection con = new(new SqlConnectionStringBuilder(connString) { InitialCatalog = "master" }.ConnectionString);
+ con.Open();
+ DataTestUtility.DropDatabase(con, s_fileStreamDBName);
+ s_fileStreamDBName = null;
+ }
+ catch (SqlException e)
+ {
+ Console.WriteLine("File Stream database could not be dropped. " + e.Message);
+ fileStreamDir = null;
+ }
+ }
+
private static string SetupTable(SqlConnection conn)
{
// Generate random table name
@@ -184,16 +281,14 @@ private static string SetupTable(SqlConnection conn)
private static void ExecuteNonQueryCommand(string cmdText, SqlConnection conn)
{
- using (SqlCommand cmd = conn.CreateCommand())
- {
- cmd.CommandText = cmdText;
- cmd.ExecuteNonQuery();
- }
+ using SqlCommand cmd = conn.CreateCommand();
+ cmd.CommandText = cmdText;
+ cmd.ExecuteNonQuery();
}
private static byte[] RetrieveData(string tempTable, SqlConnection conn, int len)
{
- SqlCommand command = new SqlCommand($"SELECT TOP(1) Photo FROM {tempTable}", conn);
+ SqlCommand command = new($"SELECT TOP(1) Photo FROM {tempTable}", conn);
byte[] bArray = new byte[len];
using (SqlDataReader reader = command.ExecuteReader())
{
diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Config.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Config.cs
index c5a3da8c3e..ce1aaaca86 100644
--- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Config.cs
+++ b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Config.cs
@@ -27,7 +27,7 @@ public class Config
public bool EnclaveEnabled = false;
public bool TracingEnabled = false;
public bool SupportsIntegratedSecurity = false;
- public bool SupportsFileStream = false;
+ public string FileStreamDirectory = null;
public bool UseManagedSNIOnWindows = false;
public string DNSCachingConnString = null;
public string DNSCachingServerCR = null; // this is for the control ring
diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json
index 8d50730a33..6b4e45ef8f 100644
--- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json
+++ b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json
@@ -16,7 +16,7 @@
"AzureKeyVaultClientSecret": "",
"SupportsIntegratedSecurity": true,
"LocalDbAppName": "",
- "SupportsFileStream": false,
+ "FileStreamDirectory": "",
"UseManagedSNIOnWindows": false,
"DNSCachingConnString": "",
"DNSCachingServerCR": "",