-
Notifications
You must be signed in to change notification settings - Fork 877
Expand file tree
/
Copy pathPgConcreteTypeInfoProvider.cs
More file actions
105 lines (90 loc) · 4.68 KB
/
PgConcreteTypeInfoProvider.cs
File metadata and controls
105 lines (90 loc) · 4.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
using System;
using System.Diagnostics.CodeAnalysis;
using Npgsql.Internal.Postgres;
namespace Npgsql.Internal;
[Experimental(NpgsqlDiagnostics.ConvertersExperimental)]
public abstract class PgConcreteTypeInfoProvider
{
private protected PgConcreteTypeInfoProvider() { }
/// <summary>
/// Gets the appropriate type info solely based on PgTypeId.
/// </summary>
public PgConcreteTypeInfo GetDefault(PgTypeId? pgTypeId)
{
var result = GetDefaultCore(pgTypeId);
if (pgTypeId is { } id && result.PgTypeId != id)
ThrowPgTypeIdMismatch(nameof(GetDefaultCore));
return result;
}
/// <summary>
/// Gets the appropriate type info based on the given field info.
/// </summary>
public PgConcreteTypeInfo? GetForField(Field field)
{
var result = GetForFieldCore(field);
if (result is not null && result.PgTypeId != field.PgTypeId)
ThrowPgTypeIdMismatch(nameof(GetForFieldCore));
return result;
}
/// <summary>
/// Gets the appropriate type info based on the given value and expected type id.
/// </summary>
public PgConcreteTypeInfo? GetForValueAsObject(ProviderValueContext context, object? value, ref object? writeState)
{
var result = GetForValueAsObjectCore(context, value, ref writeState);
if (context.ExpectedPgTypeId is { } id && result is not null && result.PgTypeId != id)
ThrowPgTypeIdMismatch(nameof(GetForValueAsObjectCore));
return result;
}
/// <summary>
/// Gets the default concrete type info for a given PgTypeId.
/// </summary>
/// <remarks>
/// Implementations should not return new instances of the possible infos that can be returned, instead its expected these are cached once returned.
/// Composing providers depend on this to cache their own infos - wrapping the element info - with the cache key being the element info reference.
/// </remarks>
protected abstract PgConcreteTypeInfo GetDefaultCore(PgTypeId? pgTypeId);
/// <summary>
/// Gets the concrete type info for a given field.
/// </summary>
/// <remarks>
/// Implementations should not return new instances of the possible infos that can be returned, instead its expected these are cached once returned.
/// Composing providers depend on this to cache their own infos - wrapping the element info - with the cache key being the element info reference.
/// </remarks>
protected virtual PgConcreteTypeInfo? GetForFieldCore(Field field) => null;
internal abstract Type TypeToConvert { get; }
private protected abstract PgConcreteTypeInfo? GetForValueAsObjectCore(ProviderValueContext context, object? value, ref object? writeState);
private protected static void ThrowPgTypeIdMismatch(string methodName)
=> throw new InvalidOperationException(
$"'{methodName}' incorrectly returned a different {nameof(PgTypeId)} in its concrete type info than the caller passed in.");
}
public abstract class PgConcreteTypeInfoProvider<T> : PgConcreteTypeInfoProvider
{
/// <summary>
/// Gets the appropriate type info based on the given value and expected type id.
/// </summary>
public PgConcreteTypeInfo? GetForValue(ProviderValueContext context, T? value, ref object? writeState)
{
var result = GetForValueCore(context, value, ref writeState);
if (context.ExpectedPgTypeId is { } id && result is not null && result.PgTypeId != id)
ThrowPgTypeIdMismatch(nameof(GetForValueCore));
return result;
}
/// <summary>
/// Gets the concrete type info for a given value and expected type id.
/// </summary>
/// <remarks>
/// Implementations should not return new instances of the possible infos that can be returned, instead its expected these are cached once returned.
/// Composing providers depend on this to cache their own infos - wrapping the element info - with the cache key being the element info reference.
/// </remarks>
protected abstract PgConcreteTypeInfo? GetForValueCore(ProviderValueContext context, T? value, ref object? writeState);
internal sealed override Type TypeToConvert => typeof(T);
// If null was passed while it is not a valid value for T we directly return null.
// This allows concrete info to be produced by falling back to GetDefault afterwards.
private protected sealed override PgConcreteTypeInfo? GetForValueAsObjectCore(ProviderValueContext context, object? value, ref object? writeState)
=> default(T) is null || value is not null ? GetForValueCore(context, (T?)value, ref writeState) : null;
}
public readonly struct ProviderValueContext
{
public PgTypeId? ExpectedPgTypeId { get; init; }
}