1 //------------------------------------------------------------------------------
2 // <copyright file="DbConnectionStringBuilder.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">Microsoft</owner>
7 //------------------------------------------------------------------------------
8 
9 namespace System.Data.Common {
10 
11     using System;
12     using System.Collections;
13     using System.Collections.Generic;
14     using System.ComponentModel;
15     using System.Data;
16     using System.Data.Common;
17     using System.Diagnostics;
18     using System.Globalization;
19     using System.Runtime.Serialization;
20     using System.Security.Permissions;
21     using System.Text;
22     using System.Text.RegularExpressions;
23     using System.Diagnostics.CodeAnalysis;
24 
25     public class DbConnectionStringBuilder : System.Collections.IDictionary, ICustomTypeDescriptor {
26 
27         // keyword->value currently listed in the connection string
28         private Dictionary<string,object>  _currentValues;
29 
30         // cached connectionstring to avoid constant rebuilding
31         // and to return a user's connectionstring as is until editing occurs
32         private string _connectionString = "";
33 
34         private PropertyDescriptorCollection _propertyDescriptors;
35         private bool _browsableConnectionString = true;
36         private readonly bool UseOdbcRules;
37 
38         private static int _objectTypeCount; // Bid counter
39         internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
40 
DbConnectionStringBuilder()41         public DbConnectionStringBuilder() {
42         }
43 
DbConnectionStringBuilder(bool useOdbcRules)44         public DbConnectionStringBuilder(bool useOdbcRules) {
45             UseOdbcRules = useOdbcRules;
46         }
47 
48         private ICollection Collection {
49             get { return (ICollection)CurrentValues; }
50         }
51         private IDictionary Dictionary {
52             get { return (IDictionary)CurrentValues; }
53         }
54         private Dictionary<string,object> CurrentValues {
55             get {
56                 Dictionary<string,object> values = _currentValues;
57                 if (null == values) {
58                     values = new Dictionary<string,object>(StringComparer.OrdinalIgnoreCase);
59                     _currentValues = values;
60                 }
61                 return values;
62             }
63         }
64 
65         object System.Collections.IDictionary.this[object keyword] {
66             // delegate to this[string keyword]
67             get { return this[ObjectToString(keyword)]; }
68             set { this[ObjectToString(keyword)] = value; }
69         }
70 
71         [Browsable(false)]
72         public virtual object this[string keyword] {
73             get {
74                 Bid.Trace("<comm.DbConnectionStringBuilder.get_Item|API> %d#, keyword='%ls'\n", ObjectID, keyword);
75                 ADP.CheckArgumentNull(keyword, "keyword");
76                 object value;
77                 if (CurrentValues.TryGetValue(keyword, out value)) {
78                     return value;
79                 }
80                 throw ADP.KeywordNotSupported(keyword);
81             }
82             set {
83                 ADP.CheckArgumentNull(keyword, "keyword");
84                 bool flag = false;
85                 if (null != value) {
86                     string keyvalue = DbConnectionStringBuilderUtil.ConvertToString(value);
87                     DbConnectionOptions.ValidateKeyValuePair(keyword, keyvalue);
88 
89                     flag = CurrentValues.ContainsKey(keyword);
90 
91                     // store keyword/value pair
92                     CurrentValues[keyword] = keyvalue;
93 
94                 }
95                 else {
96                     flag = Remove(keyword);
97                 }
98                 _connectionString = null;
99                 if (flag) {
100                     _propertyDescriptors = null;
101                 }
102             }
103         }
104 
105         [Browsable(false)]
106         [DesignOnly(true)]
107         [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
108         [EditorBrowsableAttribute(EditorBrowsableState.Never)]
109         public bool BrowsableConnectionString {
110             get {
111                 return _browsableConnectionString;
112             }
113             set {
114                 _browsableConnectionString = value;
115                 _propertyDescriptors = null;
116             }
117         }
118 
119         [RefreshPropertiesAttribute(RefreshProperties.All)]
120         [ResCategoryAttribute(Res.DataCategory_Data)]
121         [ResDescriptionAttribute(Res.DbConnectionString_ConnectionString)]
122         public string ConnectionString {
123             get {
124                 Bid.Trace("<comm.DbConnectionStringBuilder.get_ConnectionString|API> %d#\n", ObjectID);
125                 string connectionString = _connectionString;
126                 if (null == connectionString) {
127                     StringBuilder builder = new StringBuilder();
128                     foreach(string keyword in Keys) {
129                         object value;
130                         if (ShouldSerialize(keyword) && TryGetValue(keyword, out value)) {
131                             string keyvalue = ConvertValueToString(value);
132                             AppendKeyValuePair(builder, keyword, keyvalue, UseOdbcRules);
133                         }
134                     }
135                     connectionString = builder.ToString();
136                     _connectionString = connectionString;
137                 }
138                 return connectionString;
139             }
140             set {
141                 Bid.Trace("<comm.DbConnectionStringBuilder.set_ConnectionString|API> %d#\n", ObjectID);
142                 DbConnectionOptions constr = new DbConnectionOptions(value, null, UseOdbcRules);
143                 string originalValue = ConnectionString;
144                 Clear();
145                 try {
146                     for(NameValuePair pair = constr.KeyChain; null != pair; pair = pair.Next) {
147                         if (null != pair.Value) {
148                             this[pair.Name] = pair.Value;
149                         }
150                         else {
151                             Remove(pair.Name);
152                         }
153                     }
154                     _connectionString = null;
155                 }
156                 catch(ArgumentException) { // restore original string
157                     ConnectionString = originalValue;
158                     _connectionString = originalValue;
159                     throw;
160                 }
161             }
162         }
163 
164         [Browsable(false)]
165         public virtual int Count {
166             get { return CurrentValues.Count; }
167         }
168 
169         [Browsable(false)]
170         public bool IsReadOnly {
171             get { return false; }
172         }
173 
174         [Browsable(false)]
175         public virtual bool IsFixedSize {
176             get { return false; }
177         }
178         bool System.Collections.ICollection.IsSynchronized {
179             get { return Collection.IsSynchronized; }
180         }
181 
182         [Browsable(false)]
183         public virtual ICollection Keys {
184             get {
185                 Bid.Trace("<comm.DbConnectionStringBuilder.Keys|API> %d#\n", ObjectID);
186                 return Dictionary.Keys;
187             }
188         }
189 
190         internal int ObjectID {
191             get {
192                 return _objectID;
193             }
194         }
195 
196         object System.Collections.ICollection.SyncRoot {
197             get { return Collection.SyncRoot; }
198         }
199 
200         [Browsable(false)]
201         public virtual ICollection Values {
202              get {
203                 Bid.Trace("<comm.DbConnectionStringBuilder.Values|API> %d#\n", ObjectID);
204                 System.Collections.Generic.ICollection<string> keys = (System.Collections.Generic.ICollection<string>)Keys;
205                 System.Collections.Generic.IEnumerator<string> keylist = keys.GetEnumerator();
206                 object[] values = new object[keys.Count];
207                 for(int i = 0; i < values.Length; ++i) {
208                     keylist.MoveNext();
209                     values[i] = this[keylist.Current];
210                     Debug.Assert(null != values[i], "null value " + keylist.Current);
211                 }
212                 return new System.Data.Common.ReadOnlyCollection<object>(values);
213             }
214         }
215 
ConvertValueToString(object value)216         internal virtual string ConvertValueToString(object value) {
217             return (value == null) ? (string)null : Convert.ToString(value, CultureInfo.InvariantCulture);
218         }
219 
System.Collections.IDictionary.Add(object keyword, object value)220         void System.Collections.IDictionary.Add(object keyword, object value) {
221             Add(ObjectToString(keyword), value);
222         }
Add(string keyword, object value)223         public void Add(string keyword, object value) {
224             this[keyword] = value;
225         }
226 
AppendKeyValuePair(StringBuilder builder, string keyword, string value)227         public static void AppendKeyValuePair(StringBuilder builder, string keyword, string value) {
228             DbConnectionOptions.AppendKeyValuePairBuilder(builder, keyword, value, false);
229         }
230 
AppendKeyValuePair(StringBuilder builder, string keyword, string value, bool useOdbcRules)231         public static void AppendKeyValuePair(StringBuilder builder, string keyword, string value, bool useOdbcRules) {
232             DbConnectionOptions.AppendKeyValuePairBuilder(builder, keyword, value, useOdbcRules);
233         }
234 
Clear()235         public virtual void Clear() {
236             Bid.Trace("<comm.DbConnectionStringBuilder.Clear|API>\n");
237             _connectionString = "";
238             _propertyDescriptors = null;
239             CurrentValues.Clear();
240         }
241 
ClearPropertyDescriptors()242         protected internal void ClearPropertyDescriptors() {
243             _propertyDescriptors = null;
244         }
245 
246         // does the keyword exist as a strongly typed keyword or as a stored value
System.Collections.IDictionary.Contains(object keyword)247         bool System.Collections.IDictionary.Contains(object keyword) {
248             return ContainsKey(ObjectToString(keyword));
249         }
ContainsKey(string keyword)250         public virtual bool ContainsKey(string keyword) {
251             ADP.CheckArgumentNull(keyword, "keyword");
252             return CurrentValues.ContainsKey(keyword);
253         }
254 
ICollection.CopyTo(Array array, int index)255         void ICollection.CopyTo(Array array, int index) {
256             Bid.Trace("<comm.DbConnectionStringBuilder.ICollection.CopyTo|API> %d#\n", ObjectID);
257             Collection.CopyTo(array, index);
258         }
259 
EquivalentTo(DbConnectionStringBuilder connectionStringBuilder)260         public virtual bool EquivalentTo(DbConnectionStringBuilder connectionStringBuilder) {
261             ADP.CheckArgumentNull(connectionStringBuilder, "connectionStringBuilder");
262 
263 
264             Bid.Trace("<comm.DbConnectionStringBuilder.EquivalentTo|API> %d#, connectionStringBuilder=%d#\n", ObjectID, connectionStringBuilder.ObjectID);
265             if ((GetType() != connectionStringBuilder.GetType()) || (CurrentValues.Count != connectionStringBuilder.CurrentValues.Count)) {
266                 return false;
267             }
268             object value;
269             foreach(KeyValuePair<string, object> entry in CurrentValues) {
270                 if (!connectionStringBuilder.CurrentValues.TryGetValue(entry.Key, out value) || !entry.Value.Equals(value)) {
271                     return false;
272                 }
273             }
274             return true;
275         }
276 
System.Collections.IEnumerable.GetEnumerator()277         IEnumerator System.Collections.IEnumerable.GetEnumerator() {
278             Bid.Trace("<comm.DbConnectionStringBuilder.IEnumerable.GetEnumerator|API> %d#\n", ObjectID);
279             return Collection.GetEnumerator();
280         }
System.Collections.IDictionary.GetEnumerator()281         IDictionaryEnumerator System.Collections.IDictionary.GetEnumerator() {
282             Bid.Trace("<comm.DbConnectionStringBuilder.IDictionary.GetEnumerator|API> %d#\n", ObjectID);
283             return Dictionary.GetEnumerator();
284         }
285 
286         [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Justification = "See Dev11 bug 875012")]
ObjectToString(object keyword)287         private string ObjectToString(object keyword) {
288             try {
289                 return (string)keyword;
290             }
291             catch(InvalidCastException) {
292                 //
293 
294 
295 
296                 throw new ArgumentException("keyword", "not a string");
297             }
298         }
299 
System.Collections.IDictionary.Remove(object keyword)300         void System.Collections.IDictionary.Remove(object keyword) {
301             Remove(ObjectToString(keyword));
302         }
Remove(string keyword)303         public virtual bool Remove(string keyword) {
304             Bid.Trace("<comm.DbConnectionStringBuilder.Remove|API> %d#, keyword='%ls'\n", ObjectID, keyword);
305             ADP.CheckArgumentNull(keyword, "keyword");
306             if (CurrentValues.Remove(keyword)) {
307                 _connectionString = null;
308                 _propertyDescriptors = null;
309                 return true;
310             }
311             return false;
312         }
313 
314         // does the keyword exist as a stored value or something that should always be persisted
ShouldSerialize(string keyword)315         public virtual bool ShouldSerialize(string keyword) {
316             ADP.CheckArgumentNull(keyword, "keyword");
317             return CurrentValues.ContainsKey(keyword);
318         }
319 
ToString()320         public override string ToString() {
321             return ConnectionString;
322         }
323 
TryGetValue(string keyword, out object value)324         public virtual bool TryGetValue(string keyword, out object value) {
325             ADP.CheckArgumentNull(keyword, "keyword");
326             return CurrentValues.TryGetValue(keyword, out value);
327         }
328 
GetAttributesFromCollection(AttributeCollection collection)329         internal Attribute[] GetAttributesFromCollection(AttributeCollection collection) {
330             Attribute[] attributes = new Attribute[collection.Count];
331             collection.CopyTo(attributes, 0);
332             return attributes;
333         }
334 
GetProperties()335         private PropertyDescriptorCollection GetProperties() {
336             PropertyDescriptorCollection propertyDescriptors = _propertyDescriptors;
337             if (null == propertyDescriptors) {
338                 IntPtr hscp;
339                 Bid.ScopeEnter(out hscp, "<comm.DbConnectionStringBuilder.GetProperties|INFO> %d#", ObjectID);
340                 try {
341                     Hashtable descriptors = new Hashtable(StringComparer.OrdinalIgnoreCase);
342 
343                     GetProperties(descriptors);
344 
345                     PropertyDescriptor[] properties = new PropertyDescriptor[descriptors.Count];
346                     descriptors.Values.CopyTo(properties, 0);
347                     propertyDescriptors = new PropertyDescriptorCollection(properties);
348                     _propertyDescriptors = propertyDescriptors;
349                 }
350                 finally {
351                     Bid.ScopeLeave(ref hscp);
352                 }
353             }
354             return propertyDescriptors;
355         }
356 
GetProperties(Hashtable propertyDescriptors)357         protected virtual void GetProperties(Hashtable propertyDescriptors) {
358             IntPtr hscp;
359             Bid.ScopeEnter(out hscp, "<comm.DbConnectionStringBuilder.GetProperties|API> %d#", ObjectID);
360             try {
361                 // show all strongly typed properties (not already added)
362                 // except ConnectionString iff BrowsableConnectionString
363                 Attribute[] attributes;
364                 foreach(PropertyDescriptor reflected in TypeDescriptor.GetProperties(this, true)) {
365 
366                     if (ADP.ConnectionString != reflected.Name) {
367                         string displayName = reflected.DisplayName;
368                         if (!propertyDescriptors.ContainsKey(displayName)) {
369                             attributes = GetAttributesFromCollection(reflected.Attributes);
370                             PropertyDescriptor descriptor = new DbConnectionStringBuilderDescriptor(reflected.Name,
371                                     reflected.ComponentType, reflected.PropertyType, reflected.IsReadOnly, attributes);
372                             propertyDescriptors[displayName] = descriptor;
373                         }
374                         // else added by derived class first
375                     }
376                     else if (BrowsableConnectionString) {
377                         propertyDescriptors[ADP.ConnectionString] = reflected;
378                     }
379                     else {
380                         propertyDescriptors.Remove(ADP.ConnectionString);
381                     }
382                 }
383 
384                 // all keywords in Keys list that do not have strongly typed property, ODBC case
385                 // ignore 'Workaround Oracle Bug 914652' via IsFixedSize
386                 if (!IsFixedSize) {
387                     attributes = null;
388                     foreach(string keyword in Keys) {
389 
390                         if (!propertyDescriptors.ContainsKey(keyword)) {
391                             object value = this[keyword];
392 
393                             Type vtype;
394                             if (null != value) {
395                                 vtype = value.GetType();
396                                 if (typeof(string) == vtype) {
397                                     int tmp1;
398                                     if (Int32.TryParse((string)value, out tmp1)) {
399                                         vtype = typeof(Int32);
400                                     }
401                                     else {
402                                         bool tmp2;
403                                         if (Boolean.TryParse((string)value, out tmp2)) {
404                                             vtype = typeof(Boolean);
405                                         }
406                                     }
407                                 }
408                             }
409                             else {
410                                 vtype = typeof(string);
411                             }
412 
413                             Attribute[] useAttributes = attributes;
414                             if (StringComparer.OrdinalIgnoreCase.Equals(DbConnectionStringKeywords.Password, keyword) ||
415                                 StringComparer.OrdinalIgnoreCase.Equals(DbConnectionStringSynonyms.Pwd, keyword)) {
416                                 useAttributes = new Attribute[] {
417                                     BrowsableAttribute.Yes,
418                                     PasswordPropertyTextAttribute.Yes,
419                                     new ResCategoryAttribute(Res.DataCategory_Security),
420                                     RefreshPropertiesAttribute.All,
421                                 };
422                             }
423                             else if (null == attributes) {
424                                 attributes = new Attribute[] {
425                                     BrowsableAttribute.Yes,
426                                     RefreshPropertiesAttribute.All,
427                                 };
428                                 useAttributes = attributes;
429                             }
430 
431                             PropertyDescriptor descriptor = new DbConnectionStringBuilderDescriptor(keyword,
432                                                                     this.GetType(), vtype, false, useAttributes);
433                             propertyDescriptors[keyword] = descriptor;
434                         }
435                     }
436                 }
437             }
438             finally {
439                 Bid.ScopeLeave(ref hscp);
440             }
441         }
442 
GetProperties(Attribute[] attributes)443         private PropertyDescriptorCollection GetProperties(Attribute[] attributes) {
444             PropertyDescriptorCollection propertyDescriptors = GetProperties();
445             if ((null == attributes) || (0 == attributes.Length)) {
446                 // Basic case has no filtering
447                 return propertyDescriptors;
448             }
449 
450             // Create an array that is guaranteed to hold all attributes
451             PropertyDescriptor[] propertiesArray = new PropertyDescriptor[propertyDescriptors.Count];
452 
453             // Create an index to reference into this array
454             int index = 0;
455 
456             // Iterate over each property
457             foreach (PropertyDescriptor property in propertyDescriptors) {
458                 // Identify if this property's attributes match the specification
459                 bool match = true;
460                 foreach (Attribute attribute in attributes) {
461                     Attribute attr = property.Attributes[attribute.GetType()];
462                     if ((attr == null && !attribute.IsDefaultAttribute()) || !attr.Match(attribute)) {
463                         match = false;
464                         break;
465                     }
466                 }
467 
468                 // If this property matches, add it to the array
469                 if (match) {
470                     propertiesArray[index] = property;
471                     index++;
472                 }
473             }
474 
475             // Create a new array that only contains the filtered properties
476             PropertyDescriptor[] filteredPropertiesArray = new PropertyDescriptor[index];
477             Array.Copy(propertiesArray, filteredPropertiesArray, index);
478 
479             return new PropertyDescriptorCollection(filteredPropertiesArray);
480         }
481 
ICustomTypeDescriptor.GetClassName()482         string ICustomTypeDescriptor.GetClassName() {
483             return TypeDescriptor.GetClassName(this, true);
484         }
ICustomTypeDescriptor.GetComponentName()485         string ICustomTypeDescriptor.GetComponentName() {
486             return TypeDescriptor.GetComponentName(this, true);
487         }
ICustomTypeDescriptor.GetAttributes()488         AttributeCollection ICustomTypeDescriptor.GetAttributes() {
489             return TypeDescriptor.GetAttributes(this, true);
490         }
ICustomTypeDescriptor.GetEditor(Type editorBaseType)491         object ICustomTypeDescriptor.GetEditor(Type editorBaseType) {
492             return TypeDescriptor.GetEditor(this, editorBaseType, true);
493         }
ICustomTypeDescriptor.GetConverter()494         TypeConverter ICustomTypeDescriptor.GetConverter() {
495             return TypeDescriptor.GetConverter(this, true);
496         }
ICustomTypeDescriptor.GetDefaultProperty()497         PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() {
498             return TypeDescriptor.GetDefaultProperty(this, true);
499         }
ICustomTypeDescriptor.GetProperties()500         PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() {
501             return GetProperties();
502         }
ICustomTypeDescriptor.GetProperties(Attribute[] attributes)503         PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) {
504             return GetProperties(attributes);
505         }
ICustomTypeDescriptor.GetDefaultEvent()506         EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() {
507             return TypeDescriptor.GetDefaultEvent(this, true);
508         }
ICustomTypeDescriptor.GetEvents()509         EventDescriptorCollection ICustomTypeDescriptor.GetEvents() {
510             return TypeDescriptor.GetEvents(this, true);
511         }
ICustomTypeDescriptor.GetEvents(Attribute[] attributes)512         EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) {
513             return TypeDescriptor.GetEvents(this, attributes, true);
514         }
ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd)515         object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) {
516             return this;
517         }
518     }
519 }
520 
521