1+ using System ;
2+ using System . Collections . Generic ;
3+ using System . Data . Common ;
4+ using System . Globalization ;
5+ using System . Linq ;
6+ using System . Reflection ;
7+ using System . Text ;
8+ using System . Threading ;
9+
10+ using CodeJam . Collections ;
11+ using CodeJam . Reflection ;
12+ using CodeJam . Strings ;
13+ using CodeJam . Targeting ;
14+
15+ using JetBrains . Annotations ;
16+
17+ namespace CodeJam . ConnectionStrings
18+ {
19+ partial class ConnectionStringBase
20+ {
21+ private class StringBuilderWrapper : DbConnectionStringBuilder
22+ {
23+ private const string _nonBrowsableValue = "..." ;
24+
25+ private static IReadOnlyDictionary < string , KeywordDescriptor > GetDescriptorsCore ( Type type )
26+ {
27+ KeywordDescriptor GetDescriptor ( PropertyInfo property ) =>
28+ new KeywordDescriptor (
29+ property . Name ,
30+ property . PropertyType ,
31+ #if NET35_OR_GREATER || TARGETS_NETSTANDARD || TARGETS_NETCOREAPP
32+ property . IsRequired ( ) ,
33+ #else
34+ false ,
35+ #endif
36+ property. IsBrowsable ( ) ) ;
37+
38+ // Explicit ordering from most derived to base. Explanation:
39+ // The GetProperties method does not return properties in a particular order, such as alphabetical or declaration order.
40+ // Your code must not depend on the order in which properties are returned, because that order varies.
41+ var typeChain = Sequence . CreateWhileNotNull (
42+ type ,
43+ t => t . GetBaseType ( ) is var baseType && baseType != typeof ( ConnectionStringBase )
44+ ? baseType
45+ : null ) ;
46+ var properties = typeChain
47+ . SelectMany ( t => t . GetProperties ( BindingFlags . Instance | BindingFlags . Public | BindingFlags . DeclaredOnly ) ) ;
48+ #if NET45_OR_GREATER || TARGETS_NETSTANDARD || TARGETS_NETCOREAPP
49+ var result = new Dictionary < string , KeywordDescriptor > ( StringComparer . OrdinalIgnoreCase ) ;
50+ #else
51+ var result = new DictionaryEx < string , KeywordDescriptor > ( StringComparer . OrdinalIgnoreCase ) ;
52+ #endif
53+ foreach ( var prop in properties )
54+ {
55+ // DONTTOUCH: most derived property wins
56+ if ( result . ContainsKey ( prop . Name ) )
57+ continue ;
58+
59+ result [ prop . Name ] = GetDescriptor ( prop ) ;
60+ }
61+ return result ;
62+ }
63+
64+ private static readonly Func < Type , IReadOnlyDictionary < string , KeywordDescriptor > > _keywordsCache = Algorithms
65+ . Memoize (
66+ ( Type type ) => GetDescriptorsCore ( type ) ,
67+ LazyThreadSafetyMode . ExecutionAndPublication ) ;
68+
69+ private readonly Type _descriptorType ;
70+
71+ public StringBuilderWrapper ( string connectionString , Type descriptorType )
72+ {
73+ _descriptorType = descriptorType ;
74+ if ( connectionString != null )
75+ ConnectionString = connectionString ;
76+ }
77+
78+ [ NotNull ]
79+ public IReadOnlyDictionary < string , KeywordDescriptor > Keywords => _keywordsCache ( _descriptorType ) ;
80+
81+ [ NotNull ]
82+ public new string ConnectionString
83+ {
84+ get => base . ConnectionString ;
85+ set
86+ {
87+ base . ConnectionString = value ;
88+ if ( value . NotNullNorEmpty ( ) )
89+ {
90+ foreach ( var nameRequiredPair in Keywords . Where ( p => p . Value . IsRequired ) )
91+ {
92+ if ( ! ContainsKey ( nameRequiredPair . Key ) )
93+ throw CodeExceptions . Argument (
94+ nameof ( ConnectionString ) ,
95+ $ "The value of required { nameRequiredPair . Key } connection string parameter is empty.") ;
96+ }
97+ }
98+ }
99+ }
100+
101+ /// <returns></returns>
102+ [ NotNull , MustUseReturnValue ]
103+ public string GetBrowsableConnectionString ( bool includeNonBrowsable = false )
104+ {
105+ var builder = new StringBuilder ( ) ;
106+ foreach ( var browsablePair in Keywords )
107+ {
108+ if ( ! browsablePair . Value . IsBrowsable && ! includeNonBrowsable )
109+ continue ;
110+
111+ if ( ShouldSerialize ( browsablePair . Key ) && TryGetValue ( browsablePair . Key , out var value ) )
112+ {
113+ if ( ! browsablePair . Value . IsBrowsable )
114+ value = _nonBrowsableValue ;
115+ var keyValue = Convert . ToString ( value , CultureInfo . InvariantCulture ) ;
116+ AppendKeyValuePair ( builder , browsablePair . Key , keyValue ) ;
117+ }
118+ }
119+
120+ return builder . ToString ( ) ;
121+ }
122+
123+ public string GetStringValue ( string keyword ) => ( string ) this [ keyword ] ;
124+
125+ public bool TryGetStringValue ( string keyword , out string value )
126+ {
127+ value = GetStringValue ( keyword ) ;
128+ return value != null ;
129+ }
130+
131+ #region Use only allowed keywords
132+ /// <inheritdoc />
133+ public override object this [ string keyword ]
134+ {
135+ get
136+ {
137+ if ( Keywords . ContainsKey ( keyword ) )
138+ {
139+ TryGetValue ( keyword , out var value ) ;
140+ return value ;
141+ }
142+ return base [ keyword ] ; // exception for not supported keyword.
143+ }
144+ set
145+ {
146+ if ( Keywords . TryGetValue ( keyword , out var descriptor ) )
147+ {
148+ base [ descriptor . Name ] = value switch
149+ {
150+ DateTimeOffset x => x . ToInvariantString ( ) ,
151+ Guid x => x . ToInvariantString ( ) ,
152+ #if NET40_OR_GREATER || TARGETS_NETSTANDARD || TARGETS_NETCOREAPP
153+ TimeSpan x => x . ToInvariantString ( ) ,
154+ #else
155+ TimeSpan x => x . ToString ( ) ,
156+ #endif
157+ Uri x => x. ToString ( ) ,
158+ _ => value
159+ } ;
160+ }
161+ }
162+ }
163+ #endregion
164+ }
165+ }
166+ }
0 commit comments