Skip to content

Commit 0b51b63

Browse files
Merged PR 4030: [5.1.2] Fix | Adding type convertor support for SqlConnectionEncryptOption (#2057)
Ports [#2057](#2057)
1 parent 80d1f47 commit 0b51b63

File tree

7 files changed

+226
-0
lines changed

7 files changed

+226
-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
@@ -328,6 +328,9 @@
328328
<Compile Include="..\..\src\Microsoft\Data\SqlClient\SqlConnectionEncryptOption.cs">
329329
<Link>Microsoft\Data\SqlClient\SqlConnectionEncryptOption.cs</Link>
330330
</Compile>
331+
<Compile Include="..\..\src\Microsoft\Data\SqlClient\SqlConnectionEncryptOptionConverter.cs">
332+
<Link>Microsoft\Data\SqlClient\SqlConnectionEncryptOptionConverter.cs</Link>
333+
</Compile>
331334
<Compile Include="..\..\src\Microsoft\Data\SqlClient\SqlConnectionPoolGroupProviderInfo.cs">
332335
<Link>Microsoft\Data\SqlClient\SqlConnectionPoolGroupProviderInfo.cs</Link>
333336
</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: 160 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
@@ -446,6 +453,159 @@ public void EncryptTryParseInvalidValuesReturnsFalse(string value)
446453
Assert.Null(result);
447454
}
448455

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

tools/props/Versions.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
<MicrosoftSqlServerTypesVersion>10.50.1600.1</MicrosoftSqlServerTypesVersion>
7474
<BenchmarkDotNetVersion>0.13.2</BenchmarkDotNetVersion>
7575
<SystemServiceProcessServiceControllerVersion>6.0.0</SystemServiceProcessServiceControllerVersion>
76+
<MicrosoftExtensionsHosting>6.0.0</MicrosoftExtensionsHosting>
7677
</PropertyGroup>
7778
<PropertyGroup>
7879
<TestAKVProviderVersion>$(NugetPackageVersion)</TestAKVProviderVersion>

0 commit comments

Comments
 (0)