/
dev-npgsql
/
npgsql
Обзор
Документация
Войти
/
dev-npgsql
/
npgsql
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Npgsql/Internal/Converters/RecordConverter.cs
76 строк
4 KB
Nino Floris
Introduce bind operation on PgConverter surface (#6566)
11 май 2026, 20:01
Не верифицирован
11 май 2026, 20:01
0f4cfd8
Код
Авторство
О чём код?
using System; using System.Threading; using System.Threading.Tasks; using Npgsql.Internal.Postgres; namespace Npgsql.Internal.Converters; sealed class RecordConverter<T>(PgSerializerOptions options, Func<object[], T>? factory = null) : PgStreamingConverter<T> { static bool IsObjectArrayRecord => typeof(T) == typeof(object[]); public override T Read(PgReader reader) => Read(async: false, reader, CancellationToken.None).GetAwaiter().GetResult(); public override ValueTask<T> ReadAsync(PgReader reader, CancellationToken cancellationToken = default) => Read(async: true, reader, cancellationToken); async ValueTask<T> Read(bool async, PgReader reader, CancellationToken cancellationToken) { if (reader.ShouldBuffer(sizeof(int))) await reader.Buffer(async, sizeof(int), cancellationToken).ConfigureAwait(false); var fieldCount = reader.ReadInt32(); var result = new object[fieldCount]; for (var i = 0; i < fieldCount; i++) { if (reader.ShouldBuffer(sizeof(uint) + sizeof(int))) await reader.Buffer(async, sizeof(uint) + sizeof(int), cancellationToken).ConfigureAwait(false); var typeOid = reader.ReadUInt32(); var length = reader.ReadInt32(); // Note that we leave .NET nulls in the object array rather than DBNull. if (length == -1) continue; var postgresType = options.DatabaseInfo.GetPostgresType(typeOid).GetRepresentationalType() ?? throw new NotSupportedException($"Reading isn't supported for record field {i} (unknown type OID {typeOid}"); var pgTypeId = options.ToCanonicalTypeId(postgresType); // TODO resolve based on types expected by _factory (pass in a Type[] during construcion) // Only allow object polymorphism for object[] records; valuetuple records always have exact types. var typeInfo = (IsObjectArrayRecord ? options.GetTypeInfo(typeof(object), pgTypeId) : options.GetDefaultTypeInfo(pgTypeId)) ?? throw new NotSupportedException( $"Reading isn't supported for record field {i} (PG type '{postgresType.DisplayName}'"); var concreteTypeInfo = typeInfo.MakeConcreteForField(Field.CreateUnspecified(pgTypeId)); if (!concreteTypeInfo.SupportsReading) AdoSerializerHelpers.ThrowReadingNotSupported(IsObjectArrayRecord ? typeof(object) : null, options, pgTypeId, resolved: true); var binding = concreteTypeInfo.BindField(DataFormat.Binary); var scope = await reader.BeginNestedRead(async, length, binding.BufferRequirement, cancellationToken).ConfigureAwait(false); try { result[i] = (await concreteTypeInfo.Converter.ReadAsObject(async, reader, cancellationToken).ConfigureAwait(false))!; } finally { if (async) await scope.DisposeAsync().ConfigureAwait(false); else scope.Dispose(); } } return factory is null ? (T)(object)result : factory(result); } protected override Size BindValue(in BindContext context, T value, ref object? writeState) => throw new NotSupportedException(); public override void Write(PgWriter writer, T value) => throw new NotSupportedException(); public override ValueTask WriteAsync(PgWriter writer, T value, CancellationToken cancellationToken = default) => throw new NotSupportedException(); }