using System;
using System.Collections;
using System.Data;
using System.Data.Common;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
namespace WeilandYutani.Database.Utilities
{
public class RowNumberDataReader : DbDataReader
{
public RowNumberDataReader(DbDataReader inner)
{
Inner = inner;
RowNumberOrdinal = Inner.FieldCount;
if (GetColumnNames().Any(n => n == RowNumberColumnName))
{
var id = Guid.NewGuid().ToString("N");
RowNumberColumnName = $"RowNumber{id}";
}
IEnumerable<string> GetColumnNames()
{
for (int i = 0; i < Inner.FieldCount; i++)
yield return GetName(i);
}
}
ulong _rowNumber = 0;
public DbDataReader Inner { get; private set; }
public string RowNumberColumnName { get; private set; } = "RowNumber";
public int RowNumberOrdinal { get; private set; }
public override object this[int ordinal] => ordinal == RowNumberOrdinal ? _rowNumber : Inner[ordinal];
public override object this[string name] => name == RowNumberColumnName ? _rowNumber : Inner[name];
public override int Depth => Inner.Depth;
public override int FieldCount => Inner.FieldCount + 1;
public override bool HasRows => Inner.HasRows;
public override bool IsClosed => Inner.IsClosed;
public override int RecordsAffected => Inner.RecordsAffected;
public override bool GetBoolean(int ordinal) => GetInnerOrThrow(ordinal, Inner.GetBoolean);
public override byte GetByte(int ordinal) => GetInnerOrRowNumber(ordinal, Inner.GetByte, (byte)_rowNumber);
public override char GetChar(int ordinal) => GetInnerOrThrow((int)ordinal, Inner.GetChar);
public override DateTime GetDateTime(int ordinal) => GetInnerOrThrow(ordinal, Inner.GetDateTime);
public override decimal GetDecimal(int ordinal) => GetInnerOrRowNumber(ordinal, Inner.GetDecimal, _rowNumber);
public override double GetDouble(int ordinal) => GetInnerOrRowNumber(ordinal, Inner.GetDouble, _rowNumber);
public override float GetFloat(int ordinal) => GetInnerOrRowNumber(ordinal, Inner.GetFloat, _rowNumber);
public override Guid GetGuid(int ordinal) => GetInnerOrThrow(ordinal, Inner.GetGuid);
public override short GetInt16(int ordinal) => GetInnerOrRowNumber(ordinal, Inner.GetInt16, (short)_rowNumber);
public override int GetInt32(int ordinal) => GetInnerOrRowNumber(ordinal, Inner.GetInt32, (int)_rowNumber);
public override long GetInt64(int ordinal) => GetInnerOrRowNumber(ordinal, Inner.GetInt64, (long)_rowNumber);
public override string GetName(int ordinal) => GetInnerOrRowNumber(ordinal, Inner.GetName, RowNumberColumnName);
public override int GetOrdinal(string name) => name == RowNumberColumnName ? RowNumberOrdinal : Inner.GetOrdinal(name);
public override string GetString(int ordinal) => GetInnerOrRowNumber(ordinal, Inner.GetString, _rowNumber.ToString());
public override object GetValue(int ordinal) => GetInnerOrRowNumber(ordinal, Inner.GetValue, _rowNumber);
public override bool IsDBNull(int ordinal) => GetInnerOrRowNumber(ordinal, Inner.IsDBNull, false);
public override IEnumerator GetEnumerator() => new DbEnumerator(this);
public override bool NextResult() => IncrementRowNumberAndDo(Inner.NextResult);
public override bool Read() => IncrementRowNumberAndDo(Inner.Read);
[return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties)]
public override Type GetFieldType(int ordinal) => GetInnerOrRowNumber(ordinal, Inner.GetFieldType, _rowNumber.GetType());
public override string GetDataTypeName(int ordinal) => GetInnerOrRowNumber(ordinal, Inner.GetDataTypeName, _rowNumber.GetType().Name);
public override long GetBytes(int ordinal, long dataOffset, byte[]? buffer, int bufferOffset, int length)
=> GetInnerOrThrow(ordinal, () => Inner.GetBytes(ordinal, dataOffset, buffer, bufferOffset, length));
public override long GetChars(int ordinal, long dataOffset, char[]? buffer, int bufferOffset, int length)
=> GetInnerOrThrow(ordinal, () => Inner.GetChars(ordinal, dataOffset, buffer, bufferOffset, length));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
T GetInnerOrThrow<T>(int ordinal, Func<int, T> func)
=> GetInnerOrThrow<T>(ordinal, () => func(ordinal));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
T GetInnerOrThrow<T>(int ordinal, Func<T> func)
=> ordinal < RowNumberOrdinal ? func() : throw new InvalidOperationException();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
T GetInnerOrRowNumber<T>(int ordinal, Func<int, T> func, T rowNumberValue)
=> ordinal < RowNumberOrdinal ? func(ordinal) : rowNumberValue;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
T IncrementRowNumberAndDo<T>(Func<T> func)
{
_rowNumber++;
return func();
}
public override int GetValues(object[] values)
{
values[RowNumberOrdinal] = _rowNumber;
return Inner.GetValues(values) + 1;
}
}
}