99
1010namespace Npgsql . EntityFrameworkCore . PostgreSQL . Metadata
1111{
12+ /// <summary>
13+ /// Represents the metadata for a PostgreSQL range.
14+ /// </summary>
15+ [ PublicAPI ]
1216 public class PostgresRange
1317 {
1418 [ NotNull ] readonly IAnnotatable _annotatable ;
1519 [ NotNull ] readonly string _annotationName ;
1620
21+ /// <summary>
22+ /// Creates a <see cref="PostgresRange"/>.
23+ /// </summary>
24+ /// <param name="annotatable">The annotatable to search for the annotation.</param>
25+ /// <param name="annotationName">The annotation name to search for in the annotatable.</param>
26+ /// <exception cref="ArgumentNullException"><paramref name="annotatable"/></exception>
27+ /// <exception cref="ArgumentNullException"><paramref name="annotationName"/></exception>
1728 internal PostgresRange ( [ NotNull ] IAnnotatable annotatable , [ NotNull ] string annotationName )
1829 {
19- _annotatable = annotatable ;
20- _annotationName = annotationName ;
30+ _annotatable = Check . NotNull ( annotatable , nameof ( annotatable ) ) ;
31+ _annotationName = Check . NotNull ( annotationName , nameof ( annotationName ) ) ;
2132 }
2233
34+ /// <summary>
35+ /// Gets or adds a <see cref="PostgresRange"/> from or to the <see cref="IMutableAnnotatable"/>.
36+ /// </summary>
37+ /// <param name="annotatable">The annotatable from which to get or add the range.</param>
38+ /// <param name="schema">The range schema or null to use the model's default schema.</param>
39+ /// <param name="name">The range name.</param>
40+ /// <param name="subtype">The range subtype.</param>
41+ /// <param name="canonicalFunction"></param>
42+ /// <param name="subtypeOpClass"></param>
43+ /// <param name="collation"></param>
44+ /// <param name="subtypeDiff"></param>
45+ /// <returns>
46+ /// The <see cref="PostgresRange"/> from the <see cref="IMutableAnnotatable"/>.
47+ /// </returns>
48+ /// <exception cref="ArgumentException"><paramref name="schema"/></exception>
49+ /// <exception cref="ArgumentNullException"><paramref name="annotatable"/></exception>
50+ /// <exception cref="ArgumentNullException"><paramref name="name"/></exception>
51+ /// <exception cref="ArgumentNullException"><paramref name="subtype"/></exception>
52+ [ NotNull ]
2353 public static PostgresRange GetOrAddPostgresRange (
2454 [ NotNull ] IMutableAnnotatable annotatable ,
2555 [ CanBeNull ] string schema ,
@@ -30,143 +60,175 @@ public static PostgresRange GetOrAddPostgresRange(
3060 string collation = null ,
3161 string subtypeDiff = null )
3262 {
33- if ( FindPostgresRange ( annotatable , schema , name ) is PostgresRange rangeType )
34- return rangeType ;
63+ Check . NotNull ( annotatable , nameof ( annotatable ) ) ;
64+ Check . NullButNotEmpty ( schema , nameof ( schema ) ) ;
65+ Check . NotEmpty ( name , nameof ( name ) ) ;
66+ Check . NotNull ( subtype , nameof ( subtype ) ) ;
67+
68+ if ( FindPostgresRange ( annotatable , schema , name ) is PostgresRange postgresRange )
69+ return postgresRange ;
70+
71+ var annotationName = BuildAnnotationName ( schema , name ) ;
3572
36- rangeType = new PostgresRange ( annotatable , BuildAnnotationName ( annotatable , schema , name ) ) ;
37- rangeType . SetData ( subtype , canonicalFunction , subtypeOpClass , collation , subtypeDiff ) ;
38- return rangeType ;
73+ return new PostgresRange ( annotatable , annotationName )
74+ {
75+ Subtype = subtype ,
76+ CanonicalFunction = canonicalFunction ,
77+ SubtypeOpClass = subtypeOpClass ,
78+ Collation = collation ,
79+ SubtypeDiff = subtypeDiff ,
80+ } ;
3981 }
4082
83+ /// <summary>
84+ /// Finds a <see cref="PostgresRange"/> in the <see cref="IAnnotatable"/>, or returns null if not found.
85+ /// </summary>
86+ /// <param name="annotatable">The annotatable to search for the range.</param>
87+ /// <param name="schema">The range schema or null to use the model's default schema.</param>
88+ /// <param name="name">The range name.</param>
89+ /// <returns>
90+ /// The <see cref="PostgresRange"/> from the <see cref="IAnnotatable"/>.
91+ /// </returns>
92+ /// <exception cref="ArgumentException"><paramref name="schema"/></exception>
93+ /// <exception cref="ArgumentNullException"><paramref name="annotatable"/></exception>
94+ /// <exception cref="ArgumentNullException"><paramref name="name"/></exception>
4195 [ CanBeNull ]
4296 public static PostgresRange FindPostgresRange (
4397 [ NotNull ] IAnnotatable annotatable ,
4498 [ CanBeNull ] string schema ,
4599 [ NotNull ] string name )
46100 {
47101 Check . NotNull ( annotatable , nameof ( annotatable ) ) ;
102+ Check . NullButNotEmpty ( schema , nameof ( schema ) ) ;
48103 Check . NotEmpty ( name , nameof ( name ) ) ;
49104
50- var annotationName = BuildAnnotationName ( annotatable , schema , name ) ;
105+ var annotationName = BuildAnnotationName ( schema , name ) ;
51106
52107 return annotatable [ annotationName ] == null ? null : new PostgresRange ( annotatable , annotationName ) ;
53108 }
54109
55110 [ NotNull ]
56- static string BuildAnnotationName ( [ NotNull ] IAnnotatable annotatable , [ CanBeNull ] string schema , [ NotNull ] string name )
57- => ! string . IsNullOrEmpty ( schema )
111+ static string BuildAnnotationName ( string schema , string name )
112+ => schema != null
58113 ? $ "{ NpgsqlAnnotationNames . RangePrefix } { schema } .{ name } "
59- : annotatable [ RelationalAnnotationNames . DefaultSchema ] is string defaultSchema && ! string . IsNullOrEmpty ( defaultSchema )
60- ? $ "{ NpgsqlAnnotationNames . RangePrefix } { defaultSchema } .{ name } "
61- : $ "{ NpgsqlAnnotationNames . RangePrefix } { name } ";
62-
114+ : $ "{ NpgsqlAnnotationNames . RangePrefix } { name } ";
115+
116+ /// <summary>
117+ /// Gets the collection of <see cref="PostgresRange"/> stored in the <see cref="IAnnotatable"/>.
118+ /// </summary>
119+ /// <param name="annotatable">The annotatable to search for <see cref="PostgresRange"/> annotations.</param>
120+ /// <returns>
121+ /// The collection of <see cref="PostgresRange"/> stored in the <see cref="IAnnotatable"/>.
122+ /// </returns>
123+ /// <exception cref="ArgumentNullException"><paramref name="annotatable"/></exception>
63124 [ NotNull ]
125+ [ ItemNotNull ]
64126 public static IEnumerable < PostgresRange > GetPostgresRanges ( [ NotNull ] IAnnotatable annotatable )
65127 => Check . NotNull ( annotatable , nameof ( annotatable ) )
66128 . GetAnnotations ( )
67129 . Where ( a => a . Name . StartsWith ( NpgsqlAnnotationNames . RangePrefix , StringComparison . Ordinal ) )
68130 . Select ( a => new PostgresRange ( annotatable , a . Name ) ) ;
69131
132+ /// <summary>
133+ /// The <see cref="Annotatable"/> that stores the range.
134+ /// </summary>
70135 [ NotNull ]
71136 public Annotatable Annotatable => ( Annotatable ) _annotatable ;
72137
138+ /// <summary>
139+ /// The range schema or null to represent the default schema.
140+ /// </summary>
73141 [ CanBeNull ]
74- public string Schema => GetData ( ) . Schema ?? ( string ) _annotatable [ RelationalAnnotationNames . DefaultSchema ] ;
142+ public string Schema => GetData ( ) . Schema ;
75143
144+ /// <summary>
145+ /// The range name.
146+ /// </summary>
76147 [ NotNull ]
77148 public string Name => GetData ( ) . Name ;
78149
150+ /// <summary>
151+ /// The subtype of the range.
152+ /// </summary>
79153 [ NotNull ]
80154 public string Subtype
81155 {
82156 get => GetData ( ) . Subtype ;
83- set
84- {
85- var x = GetData ( ) ;
86- var ( _, _, _, canonicalFunction , subtypeOpClass , collation , subtypeDiff ) = GetData ( ) ;
87- SetData ( value , canonicalFunction , subtypeOpClass , collation , subtypeDiff ) ;
88- }
157+ set => SetData ( subtype : value ) ;
89158 }
90159
160+ /// <summary>
161+ /// The function defining a "step" in a discrete range.
162+ /// </summary>
91163 [ CanBeNull ]
92164 public string CanonicalFunction
93165 {
94166 get => GetData ( ) . CanonicalFunction ;
95- set
96- {
97- var x = GetData ( ) ;
98- var ( _, _, subtype , _, subtypeOpClass , collation , subtypeDiff ) = GetData ( ) ;
99- SetData ( subtype , value , subtypeOpClass , collation , subtypeDiff ) ;
100- }
167+ set => SetData ( canonicalFunction : value ) ;
101168 }
102169
170+ /// <summary>
171+ /// The operator class to use.
172+ /// </summary>
103173 [ CanBeNull ]
104174 public string SubtypeOpClass
105175 {
106176 get => GetData ( ) . SubtypeOpClass ;
107- set
108- {
109- var x = GetData ( ) ;
110- var ( _, _, subtype , canonicalFunction , _, collation , subtypeDiff ) = GetData ( ) ;
111- SetData ( subtype , canonicalFunction , value , collation , subtypeDiff ) ;
112- }
177+ set => SetData ( subtypeOpClass : value ) ;
113178 }
114179
180+ /// <summary>
181+ /// The collation to use.
182+ /// </summary>
115183 [ CanBeNull ]
116184 public string Collation
117185 {
118186 get => GetData ( ) . Collation ;
119- set
120- {
121- var x = GetData ( ) ;
122- var ( _, _, subtype , canonicalFunction , subtypeOpClass , _, subtypeDiff ) = GetData ( ) ;
123- SetData ( subtype , canonicalFunction , subtypeOpClass , value , subtypeDiff ) ;
124- }
187+ set => SetData ( collation : value ) ;
125188 }
126189
190+ /// <summary>
191+ /// The function defining a difference in subtype values.
192+ /// </summary>
127193 [ CanBeNull ]
128194 public string SubtypeDiff
129195 {
130196 get => GetData ( ) . SubtypeDiff ;
131- set
132- {
133- var x = GetData ( ) ;
134- var ( _, _, subtype , canonicalFunction , subtypeOpClass , collation , _) = GetData ( ) ;
135- SetData ( subtype , canonicalFunction , subtypeOpClass , collation , value ) ;
136- }
197+ set => SetData ( subtypeDiff : value ) ;
137198 }
138199
139- ( string Schema , string Name , string Subtype , string CanonicalFunction , string SubtypeOpClass , string Collation ,
140- string SubtypeDiff ) GetData ( )
141- => ! ( Annotatable [ _annotationName ] is string annotationValue )
142- ? ( null , null , null , null , null , null , null )
143- : Deserialize ( _annotationName , annotationValue ) ;
200+ ( string Schema , string Name , string Subtype , string CanonicalFunction , string SubtypeOpClass , string Collation , string SubtypeDiff ) GetData ( )
201+ => Deserialize ( Annotatable . FindAnnotation ( _annotationName ) ) ;
144202
145- void SetData ( string subtype , string canonicalFunction , string subtypeOpClass , string collation , string subtypeDiff )
146- => Annotatable [ _annotationName ] = $ "{ subtype } ,{ canonicalFunction } ,{ subtypeOpClass } ,{ collation } ,{ subtypeDiff } ";
203+ void SetData ( string subtype = null , string canonicalFunction = null , string subtypeOpClass = null , string collation = null , string subtypeDiff = null )
204+ => Annotatable [ _annotationName ] =
205+ $ "{ subtype ?? Subtype } ,{ canonicalFunction ?? CanonicalFunction } ,{ subtypeOpClass ?? SubtypeOpClass } ,{ collation ?? Collation } ,{ subtypeDiff ?? SubtypeDiff } ";
147206
148- static ( string Schema , string Name , string Subtype , string CanonicalFunction , string SubtypeOpClass , string collation , string SubtypeDiff )
149- Deserialize ( [ NotNull ] string annotationName , [ NotNull ] string annotationValue )
207+ static ( string Schema , string Name , string Subtype , string CanonicalFunction , string SubtypeOpClass , string Collation , string SubtypeDiff )
208+ Deserialize ( [ CanBeNull ] IAnnotation annotation )
150209 {
151- Check . NotEmpty ( annotationValue , nameof ( annotationValue ) ) ;
210+ if ( annotation == null || ! ( annotation . Value is string value ) || string . IsNullOrEmpty ( value ) )
211+ return ( null , null , null , null , null , null , null ) ;
152212
153- var elements = annotationValue . Split ( ',' ) . ToArray ( ) ;
213+ var elements = value . Split ( ',' ) ;
154214 if ( elements . Length != 5 )
155- throw new ArgumentException ( "Cannot parse range annotation value: " + annotationValue ) ;
215+ throw new ArgumentException ( $ "Cannot parse range annotation value: { value } ") ;
216+
156217 for ( var i = 0 ; i < 5 ; i ++ )
157218 if ( elements [ i ] == "" )
158219 elements [ i ] = null ;
159220
221+ // TODO: This would be a safer operation if we stored schema and name in the annotation value (see Sequence.cs).
160222 // Yes, this doesn't support dots in the schema/range name, let somebody complain first.
161- var schemaAndName = annotationName . Substring ( NpgsqlAnnotationNames . RangePrefix . Length ) . Split ( '.' ) ;
223+ var schemaAndName = annotation . Name . Substring ( NpgsqlAnnotationNames . RangePrefix . Length ) . Split ( '.' ) ;
162224 switch ( schemaAndName . Length )
163225 {
164226 case 1 :
165227 return ( null , schemaAndName [ 0 ] , elements [ 0 ] , elements [ 1 ] , elements [ 2 ] , elements [ 3 ] , elements [ 4 ] ) ;
166228 case 2 :
167229 return ( schemaAndName [ 0 ] , schemaAndName [ 1 ] , elements [ 0 ] , elements [ 1 ] , elements [ 2 ] , elements [ 3 ] , elements [ 4 ] ) ;
168230 default :
169- throw new ArgumentException ( "Cannot parse enum name from annotation: " + annotationName ) ;
231+ throw new ArgumentException ( $ "Cannot parse range name from annotation: { annotation . Name } " ) ;
170232 }
171233 }
172234 }
0 commit comments