From 35d1742f00a35712c2be38f0151db343cf1405d3 Mon Sep 17 00:00:00 2001 From: Kyle Ratti Date: Fri, 3 May 2024 18:08:35 -0400 Subject: [PATCH] feat: add async extensions support for DbDataReader --- .../DataReaderAsyncMaybeExtensionTests.cs | 555 ++++++++++++++++++ .../DataReaderMaybeExtensionTests.cs | 35 ++ Base/Structures/DataReaderMaybeExtensions.cs | 54 ++ 3 files changed, 644 insertions(+) create mode 100644 Base.Tests/Structures/DataReaderAsyncMaybeExtensionTests.cs diff --git a/Base.Tests/Structures/DataReaderAsyncMaybeExtensionTests.cs b/Base.Tests/Structures/DataReaderAsyncMaybeExtensionTests.cs new file mode 100644 index 0000000..23f5386 --- /dev/null +++ b/Base.Tests/Structures/DataReaderAsyncMaybeExtensionTests.cs @@ -0,0 +1,555 @@ +using System; +using System.Collections; +using System.Data.Common; +using System.Threading; +using System.Threading.Tasks; +using FruityFoundation.Base.Structures; +using NUnit.Framework; + +namespace Base.Tests.Structures; + +public class DataReaderAsyncMaybeExtensionTests +{ + [Test] + public async Task DbDataReader_TryGetBooleanAsync_WithDbNull_ReturnsEmptyMaybe() + { + // Arrange + var fakeDataReader = MockDbDataReader.Empty; + + // Act + var result = await fakeDataReader.TryGetBooleanAsync(0); + + // Assert + Assert.That(result, Is.InstanceOf>()); + Assert.That(result.HasValue, Is.False); + } + + [Test] + public async Task DbDataReader_TryGetBooleanAsync_WithValue_ReturnsMaybeWithValue() + { + // Arrange + var fakeDataReader = MockDbDataReader.WithBoolean(true); + + // Act + var result = await fakeDataReader.TryGetBooleanAsync(0); + + // Assert + Assert.That(result, Is.InstanceOf>()); + Assert.That(result.HasValue, Is.True); + Assert.That(result.Value, Is.EqualTo(true)); + } + + [Test] + public async Task DbDataReader_TryGetByteAsync_WithDbNull_ReturnsEmptyMaybe() + { + // Arrange + var fakeDataReader = MockDbDataReader.Empty; + + // Act + var result = await fakeDataReader.TryGetByteAsync(0); + + // Assert + Assert.That(result, Is.InstanceOf>()); + Assert.That(result.HasValue, Is.False); + } + + [Test] + public async Task DbDataReader_TryGetByteAsync_WithValue_ReturnsMaybeWithValue() + { + // Arrange + var fakeDataReader = MockDbDataReader.WithByte(25); + + // Act + var result = await fakeDataReader.TryGetByteAsync(0); + + // Assert + Assert.That(result, Is.InstanceOf>()); + Assert.That(result.HasValue, Is.True); + Assert.That(result.Value, Is.EqualTo((byte)25)); + } + + [Test] + public async Task DbDataReader_TryGetBytesAsync_WithDbNull_ReturnsEmptyMaybe() + { + // Arrange + var fakeDataReader = MockDbDataReader.Empty; + + // Act + var result = await fakeDataReader.TryGetBytesAsync(0, 0, null, 0, 0); + + // Assert + Assert.That(result, Is.InstanceOf>()); + Assert.That(result.HasValue, Is.False); + } + + [Test] + public async Task DbDataReader_TryGetBytesAsync_WithValue_ReturnsMaybeWithValue() + { + // Arrange + var fakeDataReader = MockDbDataReader.WithBytes(25); + + // Act + var result = await fakeDataReader.TryGetBytesAsync(0, 0, [], 0, 0); + + // Assert + Assert.That(result, Is.InstanceOf>()); + Assert.That(result.HasValue, Is.True); + Assert.That(result.Value, Is.EqualTo(25)); + } + + [Test] + public async Task DbDataReader_TryGetCharAsync_WithDbNull_ReturnsEmptyMaybe() + { + // Arrange + var fakeDataReader = MockDbDataReader.Empty; + + // Act + var result = await fakeDataReader.TryGetCharAsync(0); + + // Assert + Assert.That(result, Is.InstanceOf>()); + Assert.That(result.HasValue, Is.False); + } + + [Test] + public async Task DbDataReader_TryGetCharAsync_WithValue_ReturnsMaybeWithValue() + { + // Arrange + var fakeDataReader = MockDbDataReader.WithChar('b'); + + // Act + var result = await fakeDataReader.TryGetCharAsync(0); + + // Assert + Assert.That(result, Is.InstanceOf>()); + Assert.That(result.HasValue, Is.True); + Assert.That(result.Value, Is.EqualTo('b')); + } + + [Test] + public async Task DbDataReader_TryGetCharsAsync_WithDbNull_ReturnsEmptyMaybe() + { + // Arrange + var fakeDataReader = MockDbDataReader.Empty; + + // Act + var result = await fakeDataReader.TryGetCharsAsync(0, 0, null, 0, 0); + + // Assert + Assert.That(result, Is.InstanceOf>()); + Assert.That(result.HasValue, Is.False); + } + + [Test] + public async Task DbDataReader_TryGetCharsAsync_WithValue_ReturnsMaybeWithValue() + { + // Arrange + var fakeDataReader = MockDbDataReader.WithChars(25); + + // Act + var result = await fakeDataReader.TryGetCharsAsync(0, 0, [], 0, 0); + + // Assert + Assert.That(result, Is.InstanceOf>()); + Assert.That(result.HasValue, Is.True); + Assert.That(result.Value, Is.EqualTo(25)); + } + + [Test] + public async Task DbDataReader_TryGetDateTimeAsync_WithDbNull_ReturnsEmptyMaybe() + { + // Arrange + var fakeDataReader = MockDbDataReader.Empty; + + // Act + var result = await fakeDataReader.TryGetDateTimeAsync(0); + + // Assert + Assert.That(result, Is.InstanceOf>()); + Assert.That(result.HasValue, Is.False); + } + + [Test] + public async Task DbDataReader_TryGetDateTimeAsync_WithValue_ReturnsMaybeWithValue() + { + // Arrange + var fakeDataReader = MockDbDataReader.WithDateTime(new DateTime(2024, 05, 03, 17, 21, 0, DateTimeKind.Utc)); + + // Act + var result = await fakeDataReader.TryGetDateTimeAsync(0); + + // Assert + Assert.That(result, Is.InstanceOf>()); + Assert.That(result.HasValue, Is.True); + Assert.That(result.Value, Is.EqualTo(new DateTime(2024, 05, 03, 17, 21, 0, DateTimeKind.Utc))); + } + + [Test] + public async Task DbDataReader_TryGetDecimalAsync_WithDbNull_ReturnsEmptyMaybe() + { + // Arrange + var fakeDataReader = MockDbDataReader.Empty; + + // Act + var result = await fakeDataReader.TryGetDecimalAsync(0); + + // Assert + Assert.That(result, Is.InstanceOf>()); + Assert.That(result.HasValue, Is.False); + } + + [Test] + public async Task DbDataReader_TryGetDecimalAsync_WithValue_ReturnsMaybeWithValue() + { + // Arrange + var fakeDataReader = MockDbDataReader.WithDecimal(25.0m); + + // Act + var result = await fakeDataReader.TryGetDecimalAsync(0); + + // Assert + Assert.That(result, Is.InstanceOf>()); + Assert.That(result.HasValue, Is.True); + Assert.That(result.Value, Is.EqualTo(25.0m)); + } + + [Test] + public async Task DbDataReader_TryGetFloatAsync_WithDbNull_ReturnsEmptyMaybe() + { + // Arrange + var fakeDataReader = MockDbDataReader.Empty; + + // Act + var result = await fakeDataReader.TryGetFloatAsync(0); + + // Assert + Assert.That(result, Is.InstanceOf>()); + Assert.That(result.HasValue, Is.False); + } + + [Test] + public async Task DbDataReader_TryGetFloatAsync_WithValue_ReturnsMaybeWithValue() + { + // Arrange + var fakeDataReader = MockDbDataReader.WithFloat(25.0f); + + // Act + var result = await fakeDataReader.TryGetFloatAsync(0); + + // Assert + Assert.That(result, Is.InstanceOf>()); + Assert.That(result.HasValue, Is.True); + Assert.That(result.Value, Is.EqualTo(25.0f)); + } + + [Test] + public async Task DbDataReader_TryGetGuidAsync_WithDbNull_ReturnsEmptyMaybe() + { + // Arrange + var fakeDataReader = MockDbDataReader.Empty; + + // Act + var result = await fakeDataReader.TryGetGuidAsync(0); + + // Assert + Assert.That(result, Is.InstanceOf>()); + Assert.That(result.HasValue, Is.False); + } + + [Test] + public async Task DbDataReader_TryGetGuidAsync_WithValue_ReturnsMaybeWithValue() + { + // Arrange + var fakeDataReader = MockDbDataReader.WithGuid(new Guid("b3e7f4d3-3b7b-4b1b-8e0e-3b1b7b4b3e7f")); + + // Act + var result = await fakeDataReader.TryGetGuidAsync(0); + + // Assert + Assert.That(result, Is.InstanceOf>()); + Assert.That(result.HasValue, Is.True); + Assert.That(result.Value, Is.EqualTo(new Guid("b3e7f4d3-3b7b-4b1b-8e0e-3b1b7b4b3e7f"))); + } + + [Test] + public async Task DbDataReader_TryGetInt16Async_WithDbNull_ReturnsEmptyMaybe() + { + // Arrange + var fakeDataReader = MockDbDataReader.Empty; + + // Act + var result = await fakeDataReader.TryGetInt16Async(0); + + // Assert + Assert.That(result, Is.InstanceOf>()); + Assert.That(result.HasValue, Is.False); + } + + [Test] + public async Task DbDataReader_TryGetInt16Async_WithValue_ReturnsMaybeWithValue() + { + // Arrange + var fakeDataReader = MockDbDataReader.WithInt16(25); + + // Act + var result = await fakeDataReader.TryGetInt16Async(0); + + // Assert + Assert.That(result, Is.InstanceOf>()); + Assert.That(result.HasValue, Is.True); + Assert.That(result.Value, Is.EqualTo((short)25)); + } + + [Test] + public async Task DbDataReader_TryGetInt32Async_WithDbNull_ReturnsEmptyMaybe() + { + // Arrange + var fakeDataReader = MockDbDataReader.Empty; + + // Act + var result = await fakeDataReader.TryGetInt32Async(0); + + // Assert + Assert.That(result, Is.InstanceOf>()); + Assert.That(result.HasValue, Is.False); + } + + [Test] + public async Task DbDataReader_TryGetInt32Async_WithValue_ReturnsMaybeWithValue() + { + // Arrange + var fakeDataReader = MockDbDataReader.WithInt32(25); + + // Act + var result = await fakeDataReader.TryGetInt32Async(0); + + // Assert + Assert.That(result, Is.InstanceOf>()); + Assert.That(result.HasValue, Is.True); + Assert.That(result.Value, Is.EqualTo(25)); + } + + [Test] + public async Task DbDataReader_TryGetInt64Async_WithDbNull_ReturnsEmptyMaybe() + { + // Arrange + var fakeDataReader = MockDbDataReader.Empty; + + // Act + var result = await fakeDataReader.TryGetInt64Async(0); + + // Assert + Assert.That(result, Is.InstanceOf>()); + Assert.That(result.HasValue, Is.False); + } + + [Test] + public async Task DbDataReader_TryGetInt64Async_WithValue_ReturnsMaybeWithValue() + { + // Arrange + var fakeDataReader = MockDbDataReader.WithInt64(25L); + + // Act + var result = await fakeDataReader.TryGetInt64Async(0); + + // Assert + Assert.That(result, Is.InstanceOf>()); + Assert.That(result.HasValue, Is.True); + Assert.That(result.Value, Is.EqualTo(25)); + } + + [Test] + public async Task DbDataReader_TryGetStringAsync_WithDbNull_ReturnsEmptyMaybe() + { + // Arrange + var fakeDataReader = MockDbDataReader.Empty; + + // Act + var result = await fakeDataReader.TryGetStringAsync(0); + + // Assert + Assert.That(result, Is.InstanceOf>()); + Assert.That(result.HasValue, Is.False); + } + + [Test] + public async Task DbDataReader_TryGetStringAsync_WithValue_ReturnsMaybeWithValue() + { + // Arrange + var fakeDataReader = MockDbDataReader.WithString("banana"); + + // Act + var result = await fakeDataReader.TryGetStringAsync(0); + + // Assert + Assert.That(result, Is.InstanceOf>()); + Assert.That(result.HasValue, Is.True); + Assert.That(result.Value, Is.EqualTo("banana")); + } + + private class MockDbDataReader : DbDataReader + { + private readonly bool _isNull; + private readonly Maybe _boolValue; + private readonly Maybe _byteValue; + private readonly Maybe _bytesValue; + private readonly Maybe _charValue; + private readonly Maybe _charsValue; + private readonly Maybe _dateTimeValue; + private readonly Maybe _decimalValue; + private readonly Maybe _floatValue; + private readonly Maybe _guidValue; + private readonly Maybe _int16Value; + private readonly Maybe _int32Value; + private readonly Maybe _int64Value; + private readonly Maybe _stringValue; + + private MockDbDataReader( + bool isNull, + Maybe boolValue = default, + Maybe byteValue = default, + Maybe bytesValue = default, + Maybe charValue = default, + Maybe charsValue = default, + Maybe dateTimeValue = default, + Maybe decimalValue = default, + Maybe floatValue = default, + Maybe guidValue = default, + Maybe int16Value = default, + Maybe int32Value = default, + Maybe int64Value = default, + Maybe stringValue = default + ) + { + _isNull = isNull; + _boolValue = boolValue; + _byteValue = byteValue; + _bytesValue = bytesValue; + _charValue = charValue; + _charsValue = charsValue; + _dateTimeValue = dateTimeValue; + _decimalValue = decimalValue; + _floatValue = floatValue; + _guidValue = guidValue; + _int16Value = int16Value; + _int32Value = int32Value; + _int64Value = int64Value; + _stringValue = stringValue; + // + } + + public static MockDbDataReader Empty => new(isNull: true); + public static MockDbDataReader WithBoolean(bool value) => new(isNull: false, boolValue: Maybe.Create(value)); + public static MockDbDataReader WithByte(byte value) => new(isNull: false, byteValue: Maybe.Create(value)); + public static MockDbDataReader WithBytes(long value) => new(isNull: false, bytesValue: Maybe.Create(value)); + public static MockDbDataReader WithChar(char value) => new(isNull: false, charValue: Maybe.Create(value)); + public static MockDbDataReader WithChars(long value) => new(isNull: false, charsValue: Maybe.Create(value)); + public static MockDbDataReader WithDateTime(DateTime value) => new(isNull: false, dateTimeValue: Maybe.Create(value)); + public static MockDbDataReader WithDecimal(decimal value) => new(isNull: false, decimalValue: Maybe.Create(value)); + public static MockDbDataReader WithFloat(float value) => new(isNull: false, floatValue: Maybe.Create(value)); + public static MockDbDataReader WithGuid(Guid value) => new(isNull: false, guidValue: Maybe.Create(value)); + public static MockDbDataReader WithInt16(short value) => new(isNull: false, int16Value: Maybe.Create(value)); + public static MockDbDataReader WithInt32(int value) => new(isNull: false, int32Value: Maybe.Create(value)); + public static MockDbDataReader WithInt64(long value) => new(isNull: false, int64Value: Maybe.Create(value)); + public static MockDbDataReader WithString(string value) => new(isNull: false, stringValue: Maybe.Create(value)); + + /// + public override bool GetBoolean(int ordinal) => _boolValue.Value; + + /// + public override byte GetByte(int ordinal) => _byteValue.Value; + + /// + public override long GetBytes(int ordinal, long dataOffset, byte[]? buffer, int bufferOffset, int length) => + _bytesValue.Value; + + /// + public override char GetChar(int ordinal) => _charValue.Value; + + /// + public override long GetChars(int ordinal, long dataOffset, char[]? buffer, int bufferOffset, int length) => + _charsValue.Value; + + /// + public override string GetDataTypeName(int ordinal) => throw new NotImplementedException(); + + /// + public override DateTime GetDateTime(int ordinal) => _dateTimeValue.Value; + + /// + public override decimal GetDecimal(int ordinal) => _decimalValue.Value; + + /// + public override double GetDouble(int ordinal) => throw new NotImplementedException(); + + /// + public override Type GetFieldType(int ordinal) => throw new NotImplementedException(); + + /// + public override float GetFloat(int ordinal) => _floatValue.Value; + + /// + public override Guid GetGuid(int ordinal) => _guidValue.Value; + + /// + public override short GetInt16(int ordinal) => _int16Value.Value; + + /// + public override int GetInt32(int ordinal) => _int32Value.Value; + + /// + public override long GetInt64(int ordinal) => _int64Value.Value; + + /// + public override string GetName(int ordinal) => throw new NotImplementedException(); + + /// + public override int GetOrdinal(string name) => throw new NotImplementedException(); + + /// + public override string GetString(int ordinal) => _stringValue.Value; + + /// + public override object GetValue(int ordinal) => throw new NotImplementedException(); + + /// + public override int GetValues(object[] values) => throw new NotImplementedException(); + + /// + public override bool IsDBNull(int ordinal) => throw new NotImplementedException(); + + /// + public override Task IsDBNullAsync(int ordinal, CancellationToken cancellationToken) => + Task.FromResult(_isNull); + + /// + public override int FieldCount => throw new NotImplementedException(); + + /// + public override object this[int ordinal] => throw new NotImplementedException(); + + /// + public override object this[string name] => throw new NotImplementedException(); + + /// + public override int RecordsAffected => throw new NotImplementedException(); + + /// + public override bool HasRows => throw new NotImplementedException(); + + /// + public override bool IsClosed => throw new NotImplementedException(); + + /// + public override bool NextResult() => throw new NotImplementedException(); + + /// + public override bool Read() => throw new NotImplementedException(); + + /// + public override int Depth => throw new NotImplementedException(); + + /// + public override IEnumerator GetEnumerator() => throw new NotImplementedException(); + } +} diff --git a/Base.Tests/Structures/DataReaderMaybeExtensionTests.cs b/Base.Tests/Structures/DataReaderMaybeExtensionTests.cs index 1886ca3..18e1c21 100644 --- a/Base.Tests/Structures/DataReaderMaybeExtensionTests.cs +++ b/Base.Tests/Structures/DataReaderMaybeExtensionTests.cs @@ -113,6 +113,41 @@ public class DataReaderMaybeExtensionTests Assert.That(result.Value, Is.EqualTo(25)); } + [Test] + public void DataReader_TryGetChar_WithDbNull_ReturnsEmptyMaybe() + { + // Arrange + var fakeDataReader = A.Fake(); + A.CallTo(() => fakeDataReader.IsDBNull(0)) + .Returns(true); + + // Act + var result = fakeDataReader.TryGetChar(0); + + // Assert + Assert.That(result, Is.InstanceOf>()); + Assert.That(result.HasValue, Is.False); + } + + [Test] + public void DataReader_TryGetChar_WithValue_ReturnsMaybeWithValue() + { + // Arrange + var fakeDataReader = A.Fake(); + A.CallTo(() => fakeDataReader.IsDBNull(0)) + .Returns(false); + A.CallTo(() => fakeDataReader.GetChar(0)) + .Returns('b'); + + // Act + var result = fakeDataReader.TryGetChar(0); + + // Assert + Assert.That(result, Is.InstanceOf>()); + Assert.That(result.HasValue, Is.True); + Assert.That(result.Value, Is.EqualTo('b')); + } + [Test] public void DataReader_TryGetChars_WithDbNull_ReturnsEmptyMaybe() { diff --git a/Base/Structures/DataReaderMaybeExtensions.cs b/Base/Structures/DataReaderMaybeExtensions.cs index 90cc81d..252b2ba 100644 --- a/Base/Structures/DataReaderMaybeExtensions.cs +++ b/Base/Structures/DataReaderMaybeExtensions.cs @@ -1,5 +1,7 @@ using System; using System.Data; +using System.Data.Common; +using System.Threading.Tasks; namespace FruityFoundation.Base.Structures; @@ -8,39 +10,91 @@ public static class DataReaderExtensions public static Maybe TryGetBoolean(this IDataReader reader, int ord) => TryGet(reader, ord, reader.GetBoolean); + public static async Task> TryGetBooleanAsync(this DbDataReader reader, int ord) => + await TryGetAsync(reader, ord, reader.GetBoolean); + public static Maybe TryGetByte(this IDataReader reader, int ord) => TryGet(reader, ord, reader.GetByte); + public static async Task> TryGetByteAsync(this DbDataReader reader, int ord) => + await TryGetAsync(reader, ord, reader.GetByte); + public static Maybe TryGetBytes(this IDataReader reader, int ord, long fieldOffset, byte[]? buffer, int bufferOffset, int length) => TryGet(reader, ord, _ => reader.GetBytes(ord, fieldOffset, buffer, bufferOffset, length)); + public static async Task> TryGetBytesAsync(this DbDataReader reader, int ord, long fieldOffset, byte[]? buffer, int bufferOffset, int length) => + await TryGetAsync(reader, ord, _ => reader.GetBytes(ord, fieldOffset, buffer, bufferOffset, length)); + + public static Maybe TryGetChar(this IDataReader reader, int ord) => + TryGet(reader, ord, reader.GetChar); + + public static async Task> TryGetCharAsync(this DbDataReader reader, int ord) => + await TryGetAsync(reader, ord, reader.GetChar); + public static Maybe TryGetChars(this IDataReader reader, int ord, long fieldOffset, char[]? buffer, int bufferOffset, int length) => TryGet(reader, ord, _ => reader.GetChars(ord, fieldOffset, buffer, bufferOffset, length)); + public static async Task> TryGetCharsAsync(this DbDataReader reader, int ord, long fieldOffset, char[]? buffer, int bufferOffset, int length) => + await TryGetAsync(reader, ord, _ => reader.GetChars(ord, fieldOffset, buffer, bufferOffset, length)); + public static Maybe TryGetDateTime(this IDataReader reader, int ord) => TryGet(reader, ord, reader.GetDateTime); + public static async Task> TryGetDateTimeAsync(this DbDataReader reader, int ord) => + await TryGetAsync(reader, ord, reader.GetDateTime); + public static Maybe TryGetDecimal(this IDataReader reader, int ord) => TryGet(reader, ord, reader.GetDecimal); + public static async Task> TryGetDecimalAsync(this DbDataReader reader, int ord) => + await TryGetAsync(reader, ord, reader.GetDecimal); + public static Maybe TryGetFloat(this IDataReader reader, int ord) => TryGet(reader, ord, reader.GetFloat); + public static async Task> TryGetFloatAsync(this DbDataReader reader, int ord) => + await TryGetAsync(reader, ord, reader.GetFloat); + public static Maybe TryGetGuid(this IDataReader reader, int ord) => TryGet(reader, ord, reader.GetGuid); + public static async Task> TryGetGuidAsync(this DbDataReader reader, int ord) => + await TryGetAsync(reader, ord, reader.GetGuid); + public static Maybe TryGetInt16(this IDataReader reader, int ord) => TryGet(reader, ord, reader.GetInt16); + public static async Task> TryGetInt16Async(this DbDataReader reader, int ord) => + await TryGetAsync(reader, ord, reader.GetInt16); + public static Maybe TryGetInt32(this IDataReader reader, int ord) => TryGet(reader, ord, reader.GetInt32); + public static async Task> TryGetInt32Async(this DbDataReader reader, int ord) => + await TryGetAsync(reader, ord, reader.GetInt32); + public static Maybe TryGetInt64(this IDataReader reader, int ord) => TryGet(reader, ord, reader.GetInt64); + public static async Task> TryGetInt64Async(this DbDataReader reader, int ord) => + await TryGetAsync(reader, ord, reader.GetInt64); + public static Maybe TryGetString(this IDataReader reader, int ord) => TryGet(reader, ord, reader.GetString); + public static async Task> TryGetStringAsync(this DbDataReader reader, int ord) => + await TryGetAsync(reader, ord, reader.GetString); + private static Maybe TryGet(IDataRecord reader, int ord, Func valueGetter) => reader.IsDBNull(ord) ? Maybe.Empty() : valueGetter(ord); + + private static async Task> TryGetAsync(DbDataReader reader, int ord, Func valueGetter) + { + if (await reader.IsDBNullAsync(ord)) + return Maybe.Empty(); + + var value = valueGetter(ord); + + return Maybe.Create(value); + } }