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 66791366f3..90367cf55a 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
@@ -330,6 +330,9 @@
Microsoft\Data\SqlClient\SqlConnectionEncryptOption.cs
+
+ Microsoft\Data\SqlClient\SqlConnectionEncryptOptionConverter.cs
+
Microsoft\Data\SqlClient\SqlConnectionPoolGroupProviderInfo.cs
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
index 4492274116..675dd57fe6 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
@@ -422,6 +422,9 @@
Microsoft\Data\SqlClient\SqlConnectionEncryptOption.cs
+
+ Microsoft\Data\SqlClient\SqlConnectionEncryptOptionConverter.cs
+
Microsoft\Data\SqlClient\SqlConnectionPoolGroupProviderInfo.cs
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionEncryptOption.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionEncryptOption.cs
index ecabdb9f04..997833437f 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionEncryptOption.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionEncryptOption.cs
@@ -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
{
///
+ [TypeConverter(typeof(SqlConnectionEncryptOptionConverter))]
public sealed class SqlConnectionEncryptOption
{
private const string TRUE = "True";
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionEncryptOptionConverter.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionEncryptOptionConverter.cs
new file mode 100644
index 0000000000..0e22e6ce2e
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionEncryptOptionConverter.cs
@@ -0,0 +1,56 @@
+// 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 Microsoft.Data.Common;
+
+namespace Microsoft.Data.SqlClient
+{
+ internal 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.
+ public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
+ {
+ if (sourceType == typeof(string))
+ {
+ return true;
+ }
+ return base.CanConvertFrom(context, sourceType);
+ }
+
+ // Overrides the CanConvertTo method of TypeConverter.
+ public override bool CanConvertTo(ITypeDescriptorContext context, Type sourceType)
+ {
+ if (sourceType == typeof(string))
+ {
+ return true;
+ }
+ return base.CanConvertTo(context, sourceType);
+ }
+
+ // Overrides the ConvertFrom method of TypeConverter.
+ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
+ {
+ if (value is string)
+ {
+ return SqlConnectionEncryptOption.Parse(value.ToString());
+ }
+ throw ADP.ConvertFailed(value.GetType(), typeof(SqlConnectionEncryptOption), null);
+ }
+
+ // Overrides the ConvertTo method of TypeConverter.
+ public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
+ {
+ if (destinationType == typeof(string))
+ {
+ return base.ConvertTo(context, culture, value, destinationType);
+ }
+ throw ADP.ConvertFailed(value.GetType(), typeof(SqlConnectionEncryptOption), null);
+ }
+ }
+}
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 9d1e8d5087..a90960bdc5 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
@@ -66,6 +66,7 @@
+
diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionStringBuilderTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionStringBuilderTest.cs
index 2bc842566a..b3a090f58f 100644
--- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionStringBuilderTest.cs
+++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionStringBuilderTest.cs
@@ -4,6 +4,13 @@
using System;
using System.Collections.Generic;
+using System.ComponentModel;
+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
@@ -468,6 +475,159 @@ public void EncryptTryParseInvalidValuesReturnsFalse(string value)
Assert.Null(result);
}
+ #region SqlConnectionEncryptOptionCoverterTests
+ [Fact]
+ public void ConnectionStringFromJsonTests()
+ {
+ UserDbConnectionStringSettings settings = LoadSettingsFromJsonStream("false");
+ Assert.Equal(SqlConnectionEncryptOption.Optional, settings.UserDb.UserComponents.Encrypt);
+
+ settings = LoadSettingsFromJsonStream("true");
+ Assert.Equal(SqlConnectionEncryptOption.Mandatory, settings.UserDb.UserComponents.Encrypt);
+
+ settings = LoadSettingsFromJsonStream("strict");
+ Assert.Equal(SqlConnectionEncryptOption.Strict, settings.UserDb.UserComponents.Encrypt);
+
+ settings = LoadSettingsFromJsonStream("mandatory");
+ Assert.Equal(SqlConnectionEncryptOption.Mandatory, settings.UserDb.UserComponents.Encrypt);
+
+ settings = LoadSettingsFromJsonStream("optional");
+ Assert.Equal(SqlConnectionEncryptOption.Optional, settings.UserDb.UserComponents.Encrypt);
+
+ settings = LoadSettingsFromJsonStream("yes");
+ Assert.Equal(SqlConnectionEncryptOption.Mandatory, settings.UserDb.UserComponents.Encrypt);
+
+ settings = LoadSettingsFromJsonStream("no");
+ Assert.Equal(SqlConnectionEncryptOption.Optional, settings.UserDb.UserComponents.Encrypt);
+ }
+
+ [Theory]
+ [InlineData("absolutely")]
+ [InlineData("affirmative")]
+ [InlineData("never")]
+ [InlineData("always")]
+ [InlineData("none")]
+ [InlineData(" for sure ")]
+ public void ConnectionStringFromJsonThrowsException(string value)
+ {
+ ExecuteConnectionStringFromJsonThrowsException(value);
+ }
+
+ [Fact]
+ public void SqlConnectionEncryptOptionConverterCanConvertFromTest()
+ {
+ // Get a converter
+ SqlConnectionEncryptOption option = SqlConnectionEncryptOption.Parse("false");
+ TypeConverter converter = TypeDescriptor.GetConverter(option.GetType());
+ // Use the converter to determine if can convert from string data type
+ Assert.True(converter.CanConvertFrom(null, typeof(string)), "Expecting to convert from a string type.");
+ // Use the same converter to determine if can convert from int or bool data types
+ Assert.False(converter.CanConvertFrom(null, typeof(int)), "Not expecting to convert from integer type.");
+ Assert.False(converter.CanConvertFrom(null, typeof(bool)), "Not expecting to convert from boolean type.");
+ }
+
+ [Fact]
+ public void SqlConnectionEncryptOptionConverterCanConvertToTest()
+ {
+ // Get a converter
+ SqlConnectionEncryptOption option = SqlConnectionEncryptOption.Parse("false");
+ TypeConverter converter = TypeDescriptor.GetConverter(option.GetType());
+ // Use the converter to check if can convert from stirng
+ Assert.True(converter.CanConvertTo(null, typeof(string)), "Expecting to convert to a string type.");
+ // Use the same convert to check if can convert to int or bool
+ Assert.False(converter.CanConvertTo(null, typeof(int)), "Not expecting to convert from integer type.");
+ Assert.False(converter.CanConvertTo(null, typeof(bool)), "Not expecting to convert from boolean type.");
+ }
+
+ [Fact]
+ public void SqlConnectionEncryptOptionConverterConvertFromTest()
+ {
+ // Create a converter
+ SqlConnectionEncryptOption option = SqlConnectionEncryptOption.Parse("false");
+ TypeConverter converter = TypeDescriptor.GetConverter(option.GetType());
+ // Use the converter to convert all possible valid values
+ Assert.Equal(SqlConnectionEncryptOption.Parse("false"), converter.ConvertFrom("false"));
+ Assert.Equal(SqlConnectionEncryptOption.Parse("true"), converter.ConvertFrom("true"));
+ Assert.Equal(SqlConnectionEncryptOption.Parse("strict"), converter.ConvertFrom("strict"));
+ Assert.Equal(SqlConnectionEncryptOption.Parse("mandatory"), converter.ConvertFrom("mandatory"));
+ Assert.Equal(SqlConnectionEncryptOption.Parse("optional"), converter.ConvertFrom("optional"));
+ Assert.Equal(SqlConnectionEncryptOption.Parse("yes"), converter.ConvertFrom("yes"));
+ Assert.Equal(SqlConnectionEncryptOption.Parse("no"), converter.ConvertFrom("no"));
+ // Use the converter to covert invalid value
+ Assert.Throws(() => converter.ConvertFrom("affirmative"));
+ // Use the same converter to convert from bad data types
+ Assert.Throws(() => converter.ConvertFrom(1));
+ Assert.Throws(() => converter.ConvertFrom(true));
+ }
+
+ [Fact]
+ public void SqlConnectionEncryptOptionConverterConvertToTest()
+ {
+ // Get a converter
+ SqlConnectionEncryptOption option = SqlConnectionEncryptOption.Parse("false");
+ TypeConverter converter = TypeDescriptor.GetConverter(option.GetType());
+ // Use the converter to convert all possible valid values to string
+ Assert.Equal("False", converter.ConvertTo(SqlConnectionEncryptOption.Parse("false"), typeof(string)));
+ Assert.Equal("True", converter.ConvertTo(SqlConnectionEncryptOption.Parse("true"), typeof(string)));
+ Assert.Equal("Strict", converter.ConvertTo(SqlConnectionEncryptOption.Parse("strict"), typeof(string)));
+ Assert.Equal("True", converter.ConvertTo(SqlConnectionEncryptOption.Parse("mandatory"), typeof(string)));
+ Assert.Equal("False", converter.ConvertTo(SqlConnectionEncryptOption.Parse("optional"), typeof(string)));
+ Assert.Equal("True", converter.ConvertTo(SqlConnectionEncryptOption.Parse("yes"), typeof(string)));
+ Assert.Equal("False", converter.ConvertTo(SqlConnectionEncryptOption.Parse("no"), typeof(string)));
+ // Use the same converter to try convert to bad data types
+ Assert.Throws(() => converter.ConvertTo(SqlConnectionEncryptOption.Parse("false"), typeof(int)));
+ Assert.Throws(() => converter.ConvertTo(SqlConnectionEncryptOption.Parse("false"), typeof(bool)));
+ }
+
+ 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;
+ }
+ }
+
+ internal static void ExecuteConnectionStringFromJsonThrowsException(string encryptOption)
+ {
+ var exception = Assert.Throws(() => LoadSettingsFromJsonStream(encryptOption));
+ Assert.Contains("Failed to convert configuration", exception.Message, StringComparison.Ordinal);
+ }
+
+ private static TSettings LoadSettingsFromJsonStream(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(ctx.Configuration);
+ settingsOut = configuration.Get();
+ })
+ .Build();
+
+ return settingsOut;
+ }
+ #endregion
+
internal void ExecuteConnectionStringTests(string connectionString)
{
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(connectionString);
@@ -495,5 +655,6 @@ internal static void CheckEncryptType(SqlConnectionStringBuilder builder, SqlCon
Assert.IsType(builder.Encrypt);
Assert.Equal(expectedValue, builder.Encrypt);
}
+
}
}
diff --git a/tools/props/Versions.props b/tools/props/Versions.props
index 2aab85d890..6588fb2651 100644
--- a/tools/props/Versions.props
+++ b/tools/props/Versions.props
@@ -74,6 +74,7 @@
160.1000.6
0.13.2
6.0.0
+ 6.0.0
$(NugetPackageVersion)