-
Notifications
You must be signed in to change notification settings - Fork 877
Expand file tree
/
Copy pathPgComposingTypeInfoProvider.cs
More file actions
76 lines (63 loc) · 3.6 KB
/
PgComposingTypeInfoProvider.cs
File metadata and controls
76 lines (63 loc) · 3.6 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
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using Npgsql.Internal.Postgres;
namespace Npgsql.Internal;
abstract class PgComposingTypeInfoProvider<T> : PgConcreteTypeInfoProvider<T>
{
readonly PgTypeId? _pgTypeId;
protected PgProviderTypeInfo EffectiveTypeInfo { get; }
readonly ConcurrentDictionary<PgConcreteTypeInfo, PgConcreteTypeInfo> _concreteInfoCache = new(ReferenceEqualityComparer.Instance);
protected PgComposingTypeInfoProvider(PgTypeId? pgTypeId, PgProviderTypeInfo effectiveTypeInfo)
{
if (pgTypeId is null && effectiveTypeInfo.PgTypeId is not null)
throw new ArgumentNullException(nameof(pgTypeId), $"Cannot be null if {nameof(effectiveTypeInfo)}.{nameof(PgTypeInfo.PgTypeId)} is not null.");
_pgTypeId = pgTypeId;
EffectiveTypeInfo = effectiveTypeInfo;
}
protected abstract PgTypeId GetEffectivePgTypeId(PgTypeId pgTypeId);
protected abstract PgTypeId GetPgTypeId(PgTypeId effectivePgTypeId);
protected abstract PgConverter<T> CreateConverter(PgConcreteTypeInfo effectiveConcreteTypeInfo);
protected abstract PgConcreteTypeInfo? GetEffectiveTypeInfo(ProviderValueContext effectiveContext, T? value, ref object? writeState);
protected override PgConcreteTypeInfo GetDefaultCore(PgTypeId? pgTypeId)
{
PgTypeId? effectiveTypeId = pgTypeId is { } id ? GetEffectiveTypeId(id) : null;
var concreteTypeInfo = EffectiveTypeInfo.GetDefaultConcreteTypeInfo(effectiveTypeId);
var composingPgTypeId = _pgTypeId ?? GetPgTypeId(concreteTypeInfo.PgTypeId);
return GetOrAdd(concreteTypeInfo, composingPgTypeId);
}
protected override PgConcreteTypeInfo? GetForValueCore(ProviderValueContext context, T? value, ref object? writeState)
{
PgTypeId? effectiveTypeId = context.ExpectedPgTypeId is { } id ? GetEffectiveTypeId(id) : null;
var effectiveContext = context with { ExpectedPgTypeId = effectiveTypeId };
if (GetEffectiveTypeInfo(effectiveContext, value, ref writeState) is { } effectiveTypeInfo)
return GetOrAdd(effectiveTypeInfo, context.ExpectedPgTypeId ?? _pgTypeId ?? GetPgTypeId(effectiveTypeInfo.PgTypeId));
return null;
}
protected override PgConcreteTypeInfo? GetForFieldCore(Field field)
{
if (EffectiveTypeInfo.GetConcreteTypeInfo(field with { PgTypeId = GetEffectivePgTypeId(field.PgTypeId)}) is not { } concreteTypeInfo)
return null;
var composingPgTypeId = _pgTypeId ?? GetPgTypeId(concreteTypeInfo.PgTypeId);
return GetOrAdd(concreteTypeInfo, composingPgTypeId);
}
PgTypeId GetEffectiveTypeId(PgTypeId pgTypeId)
{
// If we have a _pgTypeId match we already know the effective id, and the constructor has verified it is non-null.
if (pgTypeId == _pgTypeId)
return EffectiveTypeInfo.PgTypeId.GetValueOrDefault();
// We have an undecided type info which is asked to resolve for a specific type id
// we'll unfortunately have to look up the effective id, this is rare though.
return GetEffectivePgTypeId(pgTypeId);
}
PgConcreteTypeInfo GetOrAdd(PgConcreteTypeInfo concreteTypeInfo, PgTypeId pgTypeId)
{
(PgComposingTypeInfoProvider<T> Instance, PgConcreteTypeInfo ConcreteTypeInfo, PgTypeId PgTypeId)
state = (this, concreteTypeInfo, pgTypeId);
return _concreteInfoCache.GetOrAdd(
concreteTypeInfo,
static (_, state)
=> new(state.ConcreteTypeInfo.Options, state.Instance.CreateConverter(state.ConcreteTypeInfo), state.PgTypeId),
state);
}
}