Skip to content

Commit

Permalink
Fix | SqlBuffer.SqlGuid (#2310)
Browse files Browse the repository at this point in the history
  • Loading branch information
wilbit authored Apr 9, 2024
1 parent 8c9b699 commit f5df519
Show file tree
Hide file tree
Showing 4 changed files with 258 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,9 @@ healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/

# JetBrains Rider (cross platform .NET IDE) working folder
.idea/

# Ionide (cross platform F# VS Code tools) working folder
.ionide/

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -811,7 +811,7 @@ internal SqlGuid SqlGuid
{
if (StorageType.Guid == _type)
{
return new SqlGuid(_value._guid);
return IsNull ? SqlGuid.Null : new SqlGuid(_value._guid);
}
else if (StorageType.SqlGuid == _type)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
<Compile Include="LocalizationTest.cs" />
<Compile Include="MultipartIdentifierTests.cs" />
<Compile Include="SqlAuthenticationProviderTest.cs" />
<Compile Include="SqlBufferTests.cs" />
<Compile Include="SqlClientLoggerTest.cs" />
<Compile Include="SqlCommandSetTest.cs" />
<Compile Include="SqlConfigurableRetryLogicTest.cs" />
Expand Down
253 changes: 253 additions & 0 deletions src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBufferTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
// 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.Collections.Generic;
using System.Data.SqlTypes;
using System.Linq;
using System.Reflection;
using Xunit;

namespace Microsoft.Data.SqlClient.Tests
{
public sealed class SqlBufferTests
{
static SqlBufferTests()
{
const string sqlBufferTypeFullName = "Microsoft.Data.SqlClient.SqlBuffer";
const string storageTypeName = nameof(SqlBufferProxy.StorageType);

var assembly = typeof(SqlClientFactory).Assembly;
_sqlBufferType = assembly.GetType(sqlBufferTypeFullName)
?? throw new Exception($"Type not found [{sqlBufferTypeFullName}]");
_storageTypeType = _sqlBufferType.GetNestedTypes(BindingFlags.NonPublic)
.FirstOrDefault(x => x.Name == storageTypeName)
?? throw new Exception($"Type not found [{sqlBufferTypeFullName}+{storageTypeName}]");
}

private static readonly Type _sqlBufferType;
private static readonly Type _storageTypeType;
private readonly SqlBufferProxy _target = new();

public static IEnumerable<object[]> GetStorageTypeValues()
{
#if NET6_0_OR_GREATER
return Enum.GetValues<SqlBufferProxy.StorageType>()
.Select(x => new object[] { x });
#else
return Enum.GetValues(typeof(SqlBufferProxy.StorageType))
.OfType<SqlBufferProxy.StorageType>()
.Select(x => new object[] { x });
#endif
}

[Theory]
[MemberData(nameof(GetStorageTypeValues))]
public void StorageTypeInProxyShouldHaveTheSameValueAsOriginal(SqlBufferProxy.StorageType expected)
{
var originalEnumName = Enum.GetName(_storageTypeType, (int)expected);

Assert.Equal(expected.ToString(), originalEnumName);
}

[Fact]
public void GuidShouldThrowWhenSqlGuidNullIsSet()
{
_target.SqlGuid = SqlGuid.Null;

Assert.Throws<SqlNullValueException>(() => _target.Guid);
}

[Theory]
[InlineData(SqlBufferProxy.StorageType.Guid)]
[InlineData(SqlBufferProxy.StorageType.SqlGuid)]
public void GuidShouldThrowWhenSetToNullOfTypeIsCalled(SqlBufferProxy.StorageType storageType)
{
_target.SetToNullOfType(storageType);

Assert.Throws<SqlNullValueException>(() => _target.Guid);
}

[Fact]
public void GuidShouldReturnWhenGuidIsSet()
{
var expected = Guid.NewGuid();
_target.Guid = expected;

Assert.Equal(expected, _target.Guid);
}

[Fact]
public void GuidShouldReturnExpectedWhenSqlGuidIsSet()
{
var expected = Guid.NewGuid();
_target.SqlGuid = expected;

Assert.Equal(expected, _target.Guid);
}

[Theory]
[InlineData(SqlBufferProxy.StorageType.Guid)]
[InlineData(SqlBufferProxy.StorageType.SqlGuid)]
public void SqlGuidShouldReturnSqlNullWhenSetToNullOfTypeIsCalled(SqlBufferProxy.StorageType storageType)
{
_target.SetToNullOfType(storageType);

Assert.Equal(SqlGuid.Null, _target.SqlGuid);
}

[Fact]
public void SqlGuidShouldReturnSqlGuidNullWhenSqlGuidNullIsSet()
{
_target.SqlGuid = SqlGuid.Null;

Assert.Equal(SqlGuid.Null, _target.SqlGuid);
}

[Fact]
public void SqlGuidShouldReturnExpectedWhenGuidIsSet()
{
var guid = Guid.NewGuid();
SqlGuid expected = guid;
_target.Guid = guid;

Assert.Equal(expected, _target.SqlGuid);
}

[Fact]
public void SqlGuidShouldReturnExpectedWhenSqlGuidIsSet()
{
SqlGuid expected = Guid.NewGuid();
_target.SqlGuid = expected;

Assert.Equal(expected, _target.SqlGuid);
}

[Fact]
public void SqlValueShouldReturnExpectedWhenGuidIsSet()
{
var guid = Guid.NewGuid();
SqlGuid expected = guid;
_target.Guid = guid;

Assert.Equal(expected, _target.SqlValue);
}

[Fact]
public void SqlValueShouldReturnExpectedWhenSqlGuidIsSet()
{
SqlGuid expected = Guid.NewGuid();
_target.SqlGuid = expected;

Assert.Equal(expected, _target.SqlValue);
}

public sealed class SqlBufferProxy
{
public enum StorageType
{
Empty = 0,
Boolean,
Byte,
DateTime,
Decimal,
Double,
Int16,
Int32,
Int64,
Guid,
Money,
Single,
String,
SqlBinary,
SqlCachedBuffer,
SqlGuid,
SqlXml,
Date,
DateTime2,
DateTimeOffset,
Time,
}

private static readonly PropertyInfo _guidProperty;
private static readonly PropertyInfo _sqlGuidProperty;
private static readonly PropertyInfo _sqlValueProperty;
private static readonly MethodInfo _setToNullOfTypeMethod;
private readonly object _instance;

static SqlBufferProxy()
{
var flags = BindingFlags.NonPublic | BindingFlags.Instance;
_guidProperty = _sqlBufferType.GetProperty(nameof(Guid), flags);
_sqlGuidProperty = _sqlBufferType.GetProperty(nameof(SqlGuid), flags);
_sqlValueProperty = _sqlBufferType.GetProperty(nameof(SqlValue), flags);
_setToNullOfTypeMethod = _sqlBufferType.GetMethod(nameof(SetToNullOfType), flags);
}

public SqlBufferProxy()
{
_instance = Activator.CreateInstance(_sqlBufferType, true);
}

public Guid Guid
{
get => GetPropertyValue<Guid>(_guidProperty);
set => SetPropertyValue(_guidProperty, value);
}

public SqlGuid SqlGuid
{
get => GetPropertyValue<SqlGuid>(_sqlGuidProperty);
set => SetPropertyValue(_sqlGuidProperty, value);
}

public object SqlValue
{
get => GetPropertyValue<object>(_sqlValueProperty);
}

public void SetToNullOfType(StorageType storageType)
{
#if NET6_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
_setToNullOfTypeMethod
.Invoke(_instance, BindingFlags.DoNotWrapExceptions, null, new object[] { (int)storageType }, null);
#else
_setToNullOfTypeMethod.Invoke(_instance, new object[] { (int)storageType });
#endif
}

private T GetPropertyValue<T>(PropertyInfo property)
{
#if NET6_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
return (T)property.GetValue(_instance, BindingFlags.DoNotWrapExceptions, null, null, null);
#else
try
{
return (T)property.GetValue(_instance);
}
catch (TargetInvocationException e)
{
throw e.InnerException!;
}
#endif
}

private void SetPropertyValue(PropertyInfo property, object value)
{
#if NET6_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
property.SetValue(_instance, value, BindingFlags.DoNotWrapExceptions, null, null, null);
#else
try
{
property.SetValue(_instance, value);
}
catch (TargetInvocationException e)
{
throw e.InnerException!;
}
#endif
}
}
}
}

0 comments on commit f5df519

Please sign in to comment.