Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue# 1579 th-TH culture info causes network connection error. #2065

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?xml version="1.0"?>
<docs>
<members name="SqlConnectionEncryptOptionConverter">
<SqlConnectionEncryptOption>
<summary>Converts a string Sql Connection Encrypt option into SqlConnectionEncryptOption object</summary>
<remarks>
<para>
## Remarks
Implicit conversions have been added to maintain backwards compatibility with boolean behahavior for the <see cref="Microsoft.Data.SqlClient.SqlConnectionStringBuilder.Encrypt" /> property. When converting from a boolean, a value of `true` converts to <see cref="Microsoft.Data.SqlClient.SqlConnectionEncryptOption.Mandatory" /> and a value of `false` converts to <see cref="Microsoft.Data.SqlClient.SqlConnectionEncryptOption.Optional"/>. When converting to a boolean, <see cref="Microsoft.Data.SqlClient.SqlConnectionEncryptOption.Mandatory"/>, <see cref="Microsoft.Data.SqlClient.SqlConnectionEncryptOption.Strict"/> , and `null` convert to `true` and <see cref="Microsoft.Data.SqlClient.SqlConnectionEncryptOption.Optional"/> converts `false`.
</para>
</remarks>
</SqlConnectionEncryptOption>
<CanConvertFrom>
<summary>
If the source type is a string then conversion is allowed <see cref="T:Microsoft.Data.SqlClient.SqlConnectionEncryptOption"/>.
</summary>
<param name="value">A string containing the value to convert.</param>
<returns>
<see langword="true" /> if the <paramref name="value"/> parameter can be converted successfully; otherwise, <see langword="false" />.
</returns>
<remarks>This method does not throw an exception.</remarks>
</CanConvertFrom>
<ConvertFrom>
<summary>
Converts the specified string representation of a logical value to its <see cref="T:Microsoft.Data.SqlClient.SqlConnectionEncryptOption"/> equivalent.
</summary>
<param name="value">A string containing the value to convert.</param>
<param name="result">
An object that is equivalent to <see cref="T:Microsoft.Data.SqlClient.SqlConnectionEncryptOption"/>.
</param>
<returns>
An object that is equivalent to <see cref="T:Microsoft.Data.SqlClient.SqlConnectionEncryptOption"/> with value of <paramref name="value"/> if conversion was successful;
otherwise, an exception is thrown.
</returns>
<remarks>This method throws an exception if conversion fails.</remarks>
</ConvertFrom>
<ConvertTo>
<summary>
Converts an object <see cref="T:Microsoft.Data.SqlClient.SqlConnectionEncryptOption"/> value to its string representation.
</summary>
<param name="value">An object <see cref="T:Microsoft.Data.SqlClient.SqlConnectionEncryptOption"/> containing the value to convert.</param>
<param name="result">
A string representation of the value of <see cref="T:Microsoft.Data.SqlClient.SqlConnectionEncryptOption"/>.
</param>
<returns>
A string representation of the value of <see cref="T:Microsoft.Data.SqlClient.SqlConnectionEncryptOption"/>.
</returns>
<remarks>This method does not throw an exception if conversion fails.</remarks>
</ConvertTo>
</members>
</docs>
1 change: 1 addition & 0 deletions src/Microsoft.Data.SqlClient.sln
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.Data.SqlClient",
..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionAttestationProtocol.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionAttestationProtocol.xml
..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionColumnEncryptionSetting.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionColumnEncryptionSetting.xml
..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionEncryptOption.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionEncryptOption.xml
..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionEncryptOptionConverter.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionEncryptOptionConverter.xml
..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionStringBuilder.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionStringBuilder.xml
..\doc\snippets\Microsoft.Data.SqlClient\SqlCredential.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlCredential.xml
..\doc\snippets\Microsoft.Data.SqlClient\SqlDataAdapter.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlDataAdapter.xml
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,9 @@
<Compile Include="..\..\src\Microsoft\Data\SqlClient\SqlConnectionEncryptOption.cs">
<Link>Microsoft\Data\SqlClient\SqlConnectionEncryptOption.cs</Link>
</Compile>
<Compile Include="..\..\src\Microsoft\Data\SqlClient\SqlConnectionEncryptOptionConverter.cs">
<Link>Microsoft\Data\SqlClient\SqlConnectionEncryptOptionConverter.cs</Link>
</Compile>
<Compile Include="..\..\src\Microsoft\Data\SqlClient\SqlConnectionPoolGroupProviderInfo.cs">
<Link>Microsoft\Data\SqlClient\SqlConnectionPoolGroupProviderInfo.cs</Link>
</Compile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -675,7 +675,7 @@ private void ReportSNIError(SNIProviders provider)
private bool InferNamedPipesInformation()
{
// If we have a datasource beginning with a pipe or we have already determined that the protocol is Named Pipe
if (_dataSourceAfterTrimmingProtocol.StartsWith(PipeBeginning) || _connectionProtocol == Protocol.NP)
if (_dataSourceAfterTrimmingProtocol.StartsWith(PipeBeginning, StringComparison.OrdinalIgnoreCase) || _connectionProtocol == Protocol.NP)
{
// If the data source is "np:servername"
if (!_dataSourceAfterTrimmingProtocol.Contains(PipeBeginning))
Expand Down Expand Up @@ -714,7 +714,7 @@ private bool InferNamedPipesInformation()
return false;
}

if (tokensByBackSlash[4].StartsWith(NamedPipeInstanceNameHeader))
if (tokensByBackSlash[4].StartsWith(NamedPipeInstanceNameHeader, StringComparison.OrdinalIgnoreCase))
{
InstanceName = tokensByBackSlash[4].Substring(NamedPipeInstanceNameHeader.Length);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,9 @@
<Compile Include="..\..\src\Microsoft\Data\SqlClient\SqlConnectionEncryptOption.cs">
<Link>Microsoft\Data\SqlClient\SqlConnectionEncryptOption.cs</Link>
</Compile>
<Compile Include="..\..\src\Microsoft\Data\SqlClient\SqlConnectionEncryptOptionConverter.cs">
<Link>Microsoft\Data\SqlClient\SqlConnectionEncryptOptionConverter.cs</Link>
</Compile>
<Compile Include="..\..\src\Microsoft\Data\SqlClient\SqlConnectionPoolGroupProviderInfo.cs">
<Link>Microsoft\Data\SqlClient\SqlConnectionPoolGroupProviderInfo.cs</Link>
</Compile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
// See the LICENSE file in the project root for more information.

using System;
using System.ComponentModel;
using Microsoft.Data.Common;

namespace Microsoft.Data.SqlClient
{
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnectionEncryptOption.xml' path='docs/members[@name="SqlConnectionEncryptOption"]/SqlConnectionEncryptOption/*'/>
[TypeConverter(typeof(SqlConnectionEncryptOptionConverter))]
public sealed class SqlConnectionEncryptOption
{
private const string TRUE = "True";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// 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.


using System;
using System.ComponentModel;
using System.Globalization;
using System.Drawing;

namespace Microsoft.Data.SqlClient
{
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnectionEncryptOptionConverter.xml' path='docs/members[@name="SqlConnectionEncryptOptionConverter"]/SqlConnectionEncryptOptionConverter/*'/>
public class SqlConnectionEncryptOptionConverter : TypeConverter
{
// Overrides the CanConvertFrom method of TypeConverter.
// The ITypeDescriptorContext interface provides the context for the
// conversion. Typically, this interface is used at design time to
// provide information about the design-time container.
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnectionEncryptOptionConverter.xml' path='docs/members[@name="SqlConnectionEncryptOptionConverter"]/SqlConnectionEncryptOptionConverter/CanConvertFrom/*'/>
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
if (sourceType == typeof(string))
{
return true;
}
return base.CanConvertFrom(context, sourceType);
}
// Overrides the ConvertFrom method of TypeConverter.
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnectionEncryptOptionConverter.xml' path='docs/members[@name="SqlConnectionEncryptOptionConverter"]/SqlConnectionEncryptOptionConverter/ConvertFrom/*'/>
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string)
{
return SqlConnectionEncryptOption.Parse(value.ToString());
}
throw new Exception("Value to convert must be of string type!");
}
// Overrides the ConvertTo method of TypeConverter.
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnectionEncryptOptionConverter.xml' path='docs/members[@name="SqlConnectionEncryptOptionConverter"]/SqlConnectionEncryptOptionConverter/ConvertTo/*'/>
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
return base.ConvertTo(context, culture, value, destinationType);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
<Compile Include="..\..\src\Microsoft\Data\Common\MultipartIdentifier.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkVersion)" />
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="$(SystemDiagnosticsDiagnosticSourceVersion)" />
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonVersion)" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
using System;
using System.Data;
using System.Data.Common;
using System.Globalization;
using System.Reflection;
using System.Security;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.SqlServer.TDS.Servers;
using Xunit;
Expand All @@ -23,6 +25,27 @@ public void ConnectionTest()
connection.Open();
}

[Fact]
public void ConnectionTestWithCultureTH()
{
// Save current cultures
CultureInfo currentCulture = Thread.CurrentThread.CurrentCulture;
CultureInfo currentUICulture = Thread.CurrentThread.CurrentUICulture;

Thread.CurrentThread.CurrentCulture = new CultureInfo("th-TH");
Thread.CurrentThread.CurrentUICulture = new CultureInfo("th-TH");

using TestTdsServer server = TestTdsServer.StartTestServer();
using SqlConnection connection = new SqlConnection(server.ConnectionString);
connection.Open();

// Restore saved cultures
Thread.CurrentThread.CurrentCulture = currentCulture;
Thread.CurrentThread.CurrentUICulture = currentUICulture;

Assert.Equal(ConnectionState.Open, connection.State);
}

[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotArmProcess))]
[ActiveIssue(4830, TestPlatforms.AnyUnix)]
[PlatformSpecific(TestPlatforms.Windows)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Text;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Xunit;

namespace Microsoft.Data.SqlClient.Tests
Expand Down Expand Up @@ -468,6 +474,36 @@ public void EncryptTryParseInvalidValuesReturnsFalse(string value)
Assert.Null(result);
}

[Theory]
[InlineData("false","False")]
[InlineData("true", "True")]
[InlineData("strict", "Strict")]
[InlineData("mandatory","True")]
[InlineData("optional", "False")]
[InlineData("yes", "True")]
[InlineData("no", "False")]
[InlineData("absolutely", "True")]
[InlineData("affirmative", "True")]
[InlineData("never", "True")]
[InlineData("always", "True")]
[InlineData("none", "True")]
public void ConnectionStringFromJsonTests(string value, string expectedValue)
{
ExecuteConnectionStringFromJsonTests(value, expectedValue);
}

[Theory]
[InlineData("absolutely")]
[InlineData("affirmative")]
[InlineData("never")]
[InlineData("always")]
[InlineData("none")]
[InlineData(" for sure ")]
public void ConnectionStringFromJsonThrowsException(string value)
{
ExecuteConnectionStringFromJsonThrowsException(value);
}

internal void ExecuteConnectionStringTests(string connectionString)
{
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(connectionString);
Expand Down Expand Up @@ -495,5 +531,62 @@ internal static void CheckEncryptType(SqlConnectionStringBuilder builder, SqlCon
Assert.IsType<SqlConnectionEncryptOption>(builder.Encrypt);
Assert.Equal(expectedValue, builder.Encrypt);
}

internal void ExecuteConnectionStringFromJsonTests(string encryptOption, string result)
{
var settings = LoadSettingsFromJsonStream<UserDbConnectionStringSettings>(encryptOption);
var connectionString = settings!.UserDb!.ToString();
Assert.Contains($"Encrypt={result}", connectionString, StringComparison.InvariantCultureIgnoreCase);
}

internal void ExecuteConnectionStringFromJsonThrowsException(string encryptOption)
{
Assert.Throws<System.InvalidOperationException>(() => LoadSettingsFromJsonStream<UserDbConnectionStringSettings>(encryptOption));
}

TSettings LoadSettingsFromJsonStream<TSettings>(string encryptOption) where TSettings : class
{
TSettings settingsOut = null;

Host.CreateDefaultBuilder()
.ConfigureAppConfiguration((ctx, configBuilder) =>
{
// Note: Inside string interpolation, a { should be {{ and a } should be }}
// First, declare a stringified JSON
var json = $"{{ \"UserDb\": {{ \"UserComponents\": {{ \"NetworkLibrary\": \"DBMSSOCN\", \"UserID\": \"user\", \"Password\": \"password\", \"DataSource\": \"localhost\", \"InitialCatalog\": \"catalog\", \"Encrypt\": \"{encryptOption}\" }}}}}}";

// Load the stringified JSON as a stream into the configuration builder
configBuilder.AddJsonStream(new MemoryStream(Encoding.ASCII.GetBytes(json)));
configBuilder.AddEnvironmentVariables();
})
.ConfigureServices((ctx, services) =>
{
var configuration = ctx.Configuration;
services.AddOptions();
services.Configure<TSettings>(ctx.Configuration);
settingsOut = configuration.Get<TSettings>();
})
.Build();

return settingsOut;
}
}

// These 2 classes will be used by ConnectionStringFromJsonTests only
internal class UserDbConnectionStringSettings
{
[Required]
public UserSqlConnectionString UserDb { get; set; }
}

internal class UserSqlConnectionString
{
public SqlConnectionStringBuilder UserComponents { get; set; } = new();

public override string ToString()
{
return UserComponents!.ConnectionString;
}
}

}