Skip to content

Commit 1aa9af6

Browse files
authored
Fix | Adding type convertor support for SqlConnectionEncryptOption (#2057)
1 parent ae9dada commit 1aa9af6

File tree

7 files changed

+227
-0
lines changed

7 files changed

+227
-0
lines changed

src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,9 @@
330330
<Compile Include="..\..\src\Microsoft\Data\SqlClient\SqlConnectionEncryptOption.cs">
331331
<Link>Microsoft\Data\SqlClient\SqlConnectionEncryptOption.cs</Link>
332332
</Compile>
333+
<Compile Include="..\..\src\Microsoft\Data\SqlClient\SqlConnectionEncryptOptionConverter.cs">
334+
<Link>Microsoft\Data\SqlClient\SqlConnectionEncryptOptionConverter.cs</Link>
335+
</Compile>
333336
<Compile Include="..\..\src\Microsoft\Data\SqlClient\SqlConnectionPoolGroupProviderInfo.cs">
334337
<Link>Microsoft\Data\SqlClient\SqlConnectionPoolGroupProviderInfo.cs</Link>
335338
</Compile>

src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,9 @@
422422
<Compile Include="..\..\src\Microsoft\Data\SqlClient\SqlConnectionEncryptOption.cs">
423423
<Link>Microsoft\Data\SqlClient\SqlConnectionEncryptOption.cs</Link>
424424
</Compile>
425+
<Compile Include="..\..\src\Microsoft\Data\SqlClient\SqlConnectionEncryptOptionConverter.cs">
426+
<Link>Microsoft\Data\SqlClient\SqlConnectionEncryptOptionConverter.cs</Link>
427+
</Compile>
425428
<Compile Include="..\..\src\Microsoft\Data\SqlClient\SqlConnectionPoolGroupProviderInfo.cs">
426429
<Link>Microsoft\Data\SqlClient\SqlConnectionPoolGroupProviderInfo.cs</Link>
427430
</Compile>

src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionEncryptOption.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System;
6+
using System.ComponentModel;
67
using Microsoft.Data.Common;
78

89
namespace Microsoft.Data.SqlClient
910
{
1011
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnectionEncryptOption.xml' path='docs/members[@name="SqlConnectionEncryptOption"]/SqlConnectionEncryptOption/*'/>
12+
[TypeConverter(typeof(SqlConnectionEncryptOptionConverter))]
1113
public sealed class SqlConnectionEncryptOption
1214
{
1315
private const string TRUE = "True";
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
using System;
5+
using System.ComponentModel;
6+
using System.Globalization;
7+
using Microsoft.Data.Common;
8+
9+
namespace Microsoft.Data.SqlClient
10+
{
11+
internal class SqlConnectionEncryptOptionConverter : TypeConverter
12+
{
13+
// Overrides the CanConvertFrom method of TypeConverter.
14+
// The ITypeDescriptorContext interface provides the context for the
15+
// conversion. Typically, this interface is used at design time to
16+
// provide information about the design-time container.
17+
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
18+
{
19+
if (sourceType == typeof(string))
20+
{
21+
return true;
22+
}
23+
return base.CanConvertFrom(context, sourceType);
24+
}
25+
26+
// Overrides the CanConvertTo method of TypeConverter.
27+
public override bool CanConvertTo(ITypeDescriptorContext context, Type sourceType)
28+
{
29+
if (sourceType == typeof(string))
30+
{
31+
return true;
32+
}
33+
return base.CanConvertTo(context, sourceType);
34+
}
35+
36+
// Overrides the ConvertFrom method of TypeConverter.
37+
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
38+
{
39+
if (value is string)
40+
{
41+
return SqlConnectionEncryptOption.Parse(value.ToString());
42+
}
43+
throw ADP.ConvertFailed(value.GetType(), typeof(SqlConnectionEncryptOption), null);
44+
}
45+
46+
// Overrides the ConvertTo method of TypeConverter.
47+
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
48+
{
49+
if (destinationType == typeof(string))
50+
{
51+
return base.ConvertTo(context, culture, value, destinationType);
52+
}
53+
throw ADP.ConvertFailed(value.GetType(), typeof(SqlConnectionEncryptOption), null);
54+
}
55+
}
56+
}

src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
<Compile Include="..\..\src\Microsoft\Data\Common\MultipartIdentifier.cs" />
6767
</ItemGroup>
6868
<ItemGroup>
69+
<PackageReference Include="Microsoft.Extensions.Hosting" Version="$(MicrosoftExtensionsHosting)" />
6970
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkVersion)" />
7071
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="$(SystemDiagnosticsDiagnosticSourceVersion)" />
7172
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonVersion)" />

src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionStringBuilderTest.cs

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@
44

55
using System;
66
using System.Collections.Generic;
7+
using System.ComponentModel;
8+
using System.ComponentModel.DataAnnotations;
9+
using System.IO;
10+
using System.Text;
11+
using Microsoft.Extensions.Configuration;
12+
using Microsoft.Extensions.DependencyInjection;
13+
using Microsoft.Extensions.Hosting;
714
using Xunit;
815

916
namespace Microsoft.Data.SqlClient.Tests
@@ -468,6 +475,159 @@ public void EncryptTryParseInvalidValuesReturnsFalse(string value)
468475
Assert.Null(result);
469476
}
470477

478+
#region SqlConnectionEncryptOptionCoverterTests
479+
[Fact]
480+
public void ConnectionStringFromJsonTests()
481+
{
482+
UserDbConnectionStringSettings settings = LoadSettingsFromJsonStream<UserDbConnectionStringSettings>("false");
483+
Assert.Equal(SqlConnectionEncryptOption.Optional, settings.UserDb.UserComponents.Encrypt);
484+
485+
settings = LoadSettingsFromJsonStream<UserDbConnectionStringSettings>("true");
486+
Assert.Equal(SqlConnectionEncryptOption.Mandatory, settings.UserDb.UserComponents.Encrypt);
487+
488+
settings = LoadSettingsFromJsonStream<UserDbConnectionStringSettings>("strict");
489+
Assert.Equal(SqlConnectionEncryptOption.Strict, settings.UserDb.UserComponents.Encrypt);
490+
491+
settings = LoadSettingsFromJsonStream<UserDbConnectionStringSettings>("mandatory");
492+
Assert.Equal(SqlConnectionEncryptOption.Mandatory, settings.UserDb.UserComponents.Encrypt);
493+
494+
settings = LoadSettingsFromJsonStream<UserDbConnectionStringSettings>("optional");
495+
Assert.Equal(SqlConnectionEncryptOption.Optional, settings.UserDb.UserComponents.Encrypt);
496+
497+
settings = LoadSettingsFromJsonStream<UserDbConnectionStringSettings>("yes");
498+
Assert.Equal(SqlConnectionEncryptOption.Mandatory, settings.UserDb.UserComponents.Encrypt);
499+
500+
settings = LoadSettingsFromJsonStream<UserDbConnectionStringSettings>("no");
501+
Assert.Equal(SqlConnectionEncryptOption.Optional, settings.UserDb.UserComponents.Encrypt);
502+
}
503+
504+
[Theory]
505+
[InlineData("absolutely")]
506+
[InlineData("affirmative")]
507+
[InlineData("never")]
508+
[InlineData("always")]
509+
[InlineData("none")]
510+
[InlineData(" for sure ")]
511+
public void ConnectionStringFromJsonThrowsException(string value)
512+
{
513+
ExecuteConnectionStringFromJsonThrowsException(value);
514+
}
515+
516+
[Fact]
517+
public void SqlConnectionEncryptOptionConverterCanConvertFromTest()
518+
{
519+
// Get a converter
520+
SqlConnectionEncryptOption option = SqlConnectionEncryptOption.Parse("false");
521+
TypeConverter converter = TypeDescriptor.GetConverter(option.GetType());
522+
// Use the converter to determine if can convert from string data type
523+
Assert.True(converter.CanConvertFrom(null, typeof(string)), "Expecting to convert from a string type.");
524+
// Use the same converter to determine if can convert from int or bool data types
525+
Assert.False(converter.CanConvertFrom(null, typeof(int)), "Not expecting to convert from integer type.");
526+
Assert.False(converter.CanConvertFrom(null, typeof(bool)), "Not expecting to convert from boolean type.");
527+
}
528+
529+
[Fact]
530+
public void SqlConnectionEncryptOptionConverterCanConvertToTest()
531+
{
532+
// Get a converter
533+
SqlConnectionEncryptOption option = SqlConnectionEncryptOption.Parse("false");
534+
TypeConverter converter = TypeDescriptor.GetConverter(option.GetType());
535+
// Use the converter to check if can convert from stirng
536+
Assert.True(converter.CanConvertTo(null, typeof(string)), "Expecting to convert to a string type.");
537+
// Use the same convert to check if can convert to int or bool
538+
Assert.False(converter.CanConvertTo(null, typeof(int)), "Not expecting to convert from integer type.");
539+
Assert.False(converter.CanConvertTo(null, typeof(bool)), "Not expecting to convert from boolean type.");
540+
}
541+
542+
[Fact]
543+
public void SqlConnectionEncryptOptionConverterConvertFromTest()
544+
{
545+
// Create a converter
546+
SqlConnectionEncryptOption option = SqlConnectionEncryptOption.Parse("false");
547+
TypeConverter converter = TypeDescriptor.GetConverter(option.GetType());
548+
// Use the converter to convert all possible valid values
549+
Assert.Equal(SqlConnectionEncryptOption.Parse("false"), converter.ConvertFrom("false"));
550+
Assert.Equal(SqlConnectionEncryptOption.Parse("true"), converter.ConvertFrom("true"));
551+
Assert.Equal(SqlConnectionEncryptOption.Parse("strict"), converter.ConvertFrom("strict"));
552+
Assert.Equal(SqlConnectionEncryptOption.Parse("mandatory"), converter.ConvertFrom("mandatory"));
553+
Assert.Equal(SqlConnectionEncryptOption.Parse("optional"), converter.ConvertFrom("optional"));
554+
Assert.Equal(SqlConnectionEncryptOption.Parse("yes"), converter.ConvertFrom("yes"));
555+
Assert.Equal(SqlConnectionEncryptOption.Parse("no"), converter.ConvertFrom("no"));
556+
// Use the converter to covert invalid value
557+
Assert.Throws<ArgumentException>(() => converter.ConvertFrom("affirmative"));
558+
// Use the same converter to convert from bad data types
559+
Assert.Throws<ArgumentException>(() => converter.ConvertFrom(1));
560+
Assert.Throws<ArgumentException>(() => converter.ConvertFrom(true));
561+
}
562+
563+
[Fact]
564+
public void SqlConnectionEncryptOptionConverterConvertToTest()
565+
{
566+
// Get a converter
567+
SqlConnectionEncryptOption option = SqlConnectionEncryptOption.Parse("false");
568+
TypeConverter converter = TypeDescriptor.GetConverter(option.GetType());
569+
// Use the converter to convert all possible valid values to string
570+
Assert.Equal("False", converter.ConvertTo(SqlConnectionEncryptOption.Parse("false"), typeof(string)));
571+
Assert.Equal("True", converter.ConvertTo(SqlConnectionEncryptOption.Parse("true"), typeof(string)));
572+
Assert.Equal("Strict", converter.ConvertTo(SqlConnectionEncryptOption.Parse("strict"), typeof(string)));
573+
Assert.Equal("True", converter.ConvertTo(SqlConnectionEncryptOption.Parse("mandatory"), typeof(string)));
574+
Assert.Equal("False", converter.ConvertTo(SqlConnectionEncryptOption.Parse("optional"), typeof(string)));
575+
Assert.Equal("True", converter.ConvertTo(SqlConnectionEncryptOption.Parse("yes"), typeof(string)));
576+
Assert.Equal("False", converter.ConvertTo(SqlConnectionEncryptOption.Parse("no"), typeof(string)));
577+
// Use the same converter to try convert to bad data types
578+
Assert.Throws<ArgumentException>(() => converter.ConvertTo(SqlConnectionEncryptOption.Parse("false"), typeof(int)));
579+
Assert.Throws<ArgumentException>(() => converter.ConvertTo(SqlConnectionEncryptOption.Parse("false"), typeof(bool)));
580+
}
581+
582+
internal class UserDbConnectionStringSettings
583+
{
584+
[Required]
585+
public UserSqlConnectionString UserDb { get; set; }
586+
}
587+
588+
internal class UserSqlConnectionString
589+
{
590+
public SqlConnectionStringBuilder UserComponents { get; set; } = new();
591+
592+
public override string ToString()
593+
{
594+
return UserComponents!.ConnectionString;
595+
}
596+
}
597+
598+
internal static void ExecuteConnectionStringFromJsonThrowsException(string encryptOption)
599+
{
600+
var exception = Assert.Throws<InvalidOperationException>(() => LoadSettingsFromJsonStream<UserDbConnectionStringSettings>(encryptOption));
601+
Assert.Contains("Failed to convert configuration", exception.Message, StringComparison.Ordinal);
602+
}
603+
604+
private static TSettings LoadSettingsFromJsonStream<TSettings>(string encryptOption) where TSettings : class
605+
{
606+
TSettings settingsOut = null;
607+
608+
Host.CreateDefaultBuilder()
609+
.ConfigureAppConfiguration((ctx, configBuilder) =>
610+
{
611+
// Note: Inside string interpolation, a { should be {{ and a } should be }}
612+
// First, declare a stringified JSON
613+
var json = $"{{ \"UserDb\": {{ \"UserComponents\": {{ \"NetworkLibrary\": \"DBMSSOCN\", \"UserID\": \"user\", \"Password\": \"password\", \"DataSource\": \"localhost\", \"InitialCatalog\": \"catalog\", \"Encrypt\": \"{encryptOption}\" }}}}}}";
614+
// Load the stringified JSON as a stream into the configuration builder
615+
configBuilder.AddJsonStream(new MemoryStream(Encoding.ASCII.GetBytes(json)));
616+
configBuilder.AddEnvironmentVariables();
617+
})
618+
.ConfigureServices((ctx, services) =>
619+
{
620+
var configuration = ctx.Configuration;
621+
services.AddOptions();
622+
services.Configure<TSettings>(ctx.Configuration);
623+
settingsOut = configuration.Get<TSettings>();
624+
})
625+
.Build();
626+
627+
return settingsOut;
628+
}
629+
#endregion
630+
471631
internal void ExecuteConnectionStringTests(string connectionString)
472632
{
473633
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(connectionString);
@@ -495,5 +655,6 @@ internal static void CheckEncryptType(SqlConnectionStringBuilder builder, SqlCon
495655
Assert.IsType<SqlConnectionEncryptOption>(builder.Encrypt);
496656
Assert.Equal(expectedValue, builder.Encrypt);
497657
}
658+
498659
}
499660
}

tools/props/Versions.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
<MicrosoftSqlServerTypesVersionNet>160.1000.6</MicrosoftSqlServerTypesVersionNet>
7575
<BenchmarkDotNetVersion>0.13.2</BenchmarkDotNetVersion>
7676
<SystemServiceProcessServiceControllerVersion>6.0.0</SystemServiceProcessServiceControllerVersion>
77+
<MicrosoftExtensionsHosting>6.0.0</MicrosoftExtensionsHosting>
7778
</PropertyGroup>
7879
<PropertyGroup>
7980
<TestAKVProviderVersion>$(NugetPackageVersion)</TestAKVProviderVersion>

0 commit comments

Comments
 (0)