1 //------------------------------------------------------------------------------
2 // <copyright file="Parameter.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6 
7 namespace System.Web.UI.WebControls {
8 
9     using System;
10     using System.Collections;
11     using System.ComponentModel;
12     using System.Data;
13     using System.Diagnostics;
14     using System.Globalization;
15 
16 
17     /// <devdoc>
18     /// Represents a parameter to a DataSourceControl.
19     /// Parameters can be session variables, web request parameters, or of custom types.
20     /// </devdoc>
21     [
22     DefaultProperty("DefaultValue"),
23     ]
24     public class Parameter : ICloneable, IStateManager {
25 
26         private ParameterCollection _owner;
27         private bool _tracking;
28         private StateBag _viewState;
29 
30 
31 
32         /// <devdoc>
33         /// Creates an instance of the Parameter class.
34         /// </devdoc>
Parameter()35         public Parameter() {
36         }
37 
38 
39         /// <devdoc>
40         /// Creates an instance of the Parameter class with the specified parameter name.
41         /// </devdoc>
Parameter(string name)42         public Parameter(string name) {
43             Name = name;
44         }
45 
46 
47         /// <devdoc>
48         /// Creates an instance of the Parameter class with the specified parameter name and db type.
49         /// </devdoc>
Parameter(string name, DbType dbType)50         public Parameter(string name, DbType dbType) {
51             Name = name;
52             DbType = dbType;
53         }
54 
55 
56         /// <devdoc>
57         /// Creates an instance of the Parameter class with the specified parameter name, db type, and default value.
58         /// </devdoc>
Parameter(string name, DbType dbType, string defaultValue)59         public Parameter(string name, DbType dbType, string defaultValue) {
60             Name = name;
61             DbType = dbType;
62             DefaultValue = defaultValue;
63         }
64 
65 
66         /// <devdoc>
67         /// Creates an instance of the Parameter class with the specified parameter name and type.
68         /// </devdoc>
Parameter(string name, TypeCode type)69         public Parameter(string name, TypeCode type) {
70             Name = name;
71             Type = type;
72         }
73 
74 
75         /// <devdoc>
76         /// Creates an instance of the Parameter class with the specified parameter name, type, and default value.
77         /// </devdoc>
Parameter(string name, TypeCode type, string defaultValue)78         public Parameter(string name, TypeCode type, string defaultValue) {
79             Name = name;
80             Type = type;
81             DefaultValue = defaultValue;
82         }
83 
84 
85         /// <devdoc>
86         /// Used to clone a parameter.
87         /// </devdoc>
Parameter(Parameter original)88         protected Parameter(Parameter original) {
89             DefaultValue = original.DefaultValue;
90             Direction = original.Direction;
91             Name = original.Name;
92             ConvertEmptyStringToNull = original.ConvertEmptyStringToNull;
93             Size = original.Size;
94             Type = original.Type;
95             DbType = original.DbType;
96         }
97 
98 
99 
100         /// <devdoc>
101         /// Indicates whether the Parameter is tracking view state.
102         /// </devdoc>
103         protected bool IsTrackingViewState {
104             get {
105                 return _tracking;
106             }
107         }
108 
109 
110         /// <devdoc>
111         /// Gets/sets the db type of the parameter's value.
112         /// When DbType is DbType.Object, the Type property will be used instead
113         /// </devdoc>
114         [
115         DefaultValue(DbType.Object),
116         WebCategory("Parameter"),
117         WebSysDescription(SR.Parameter_DbType),
118         ]
119         public DbType DbType {
120             get {
121                 object o = ViewState["DbType"];
122                 if (o == null)
123                     return DbType.Object;
124                 return (DbType)o;
125             }
126             set {
127                 if (value < DbType.AnsiString || value > DbType.DateTimeOffset) {
128                     throw new ArgumentOutOfRangeException("value");
129                 }
130                 if (DbType != value) {
131                     ViewState["DbType"] = value;
132                     OnParameterChanged();
133                 }
134             }
135         }
136 
137 
138         /// <devdoc>
139         /// The default value to use in GetValue() if it cannot obtain a value.
140         /// </devdoc>
141         [
142         DefaultValue(null),
143         WebCategory("Parameter"),
144         WebSysDescription(SR.Parameter_DefaultValue),
145         ]
146         public string DefaultValue {
147             get {
148                 object o = ViewState["DefaultValue"];
149                 return (o as string);
150             }
151             set {
152                 if (DefaultValue != value) {
153                     ViewState["DefaultValue"] = value;
154                     OnParameterChanged();
155                 }
156             }
157         }
158 
159 
160         /// <devdoc>
161         /// Gets/sets the direction of the parameter.
162         /// </devdoc>
163         [
164         DefaultValue(ParameterDirection.Input),
165         WebCategory("Parameter"),
166         WebSysDescription(SR.Parameter_Direction),
167         ]
168         public ParameterDirection Direction {
169             get {
170                 object o = ViewState["Direction"];
171                 if (o == null)
172                     return ParameterDirection.Input;
173                 return (ParameterDirection)o;
174             }
175             set {
176                 if (Direction != value) {
177                     ViewState["Direction"] = value;
178                     OnParameterChanged();
179                 }
180             }
181         }
182 
183 
184         /// <devdoc>
185         /// Gets/sets the name of the parameter.
186         /// </devdoc>
187         [
188         DefaultValue(""),
189         WebCategory("Parameter"),
190         WebSysDescription(SR.Parameter_Name),
191         ]
192         public string Name {
193             get {
194                 object o = ViewState["Name"];
195                 if (o == null)
196                     return String.Empty;
197                 return (string)o;
198             }
199             set {
200                 if (Name != value) {
201                     ViewState["Name"] = value;
202                     OnParameterChanged();
203                 }
204             }
205         }
206 
207         /// <devdoc>
208         /// Returns the value of parameter after converting it to the proper type.
209         /// </devdoc>
210         [
211         Browsable(false),
212         ]
213         internal object ParameterValue {
214             get {
215                 return GetValue(ViewState["ParameterValue"], false);
216             }
217         }
218 
GetDatabaseType()219         public DbType GetDatabaseType() {
220             DbType dbType = DbType;
221             if (dbType == DbType.Object) {
222                 return ConvertTypeCodeToDbType(Type);
223             }
224             if (Type != TypeCode.Empty) {
225                 throw new InvalidOperationException(SR.GetString(SR.Parameter_TypeNotSupported, Name));
226             }
227             return dbType;
228         }
229 
GetValue(object value, bool ignoreNullableTypeChanges)230         internal object GetValue(object value, bool ignoreNullableTypeChanges) {
231             DbType dbType = DbType;
232             if (dbType == DbType.Object) {
233                 return GetValue(value, DefaultValue, Type, ConvertEmptyStringToNull, ignoreNullableTypeChanges);
234             }
235             if (Type != TypeCode.Empty) {
236                 throw new InvalidOperationException(SR.GetString(SR.Parameter_TypeNotSupported, Name));
237             }
238             return GetValue(value, DefaultValue, dbType, ConvertEmptyStringToNull, ignoreNullableTypeChanges);
239         }
240 
GetValue(object value, string defaultValue, DbType dbType, bool convertEmptyStringToNull, bool ignoreNullableTypeChanges)241         internal static object GetValue(object value, string defaultValue, DbType dbType, bool convertEmptyStringToNull,
242             bool ignoreNullableTypeChanges) {
243 
244             // use the TypeCode conversion logic for Whidbey types.
245             if ((dbType != DbType.DateTimeOffset) && (dbType != DbType.Time) && (dbType != DbType.Guid)) {
246                 TypeCode type = ConvertDbTypeToTypeCode(dbType);
247                 return GetValue(value, defaultValue, type, convertEmptyStringToNull, ignoreNullableTypeChanges);
248             }
249 
250             value = HandleNullValue(value, defaultValue, convertEmptyStringToNull);
251             if (value == null) {
252                 return null;
253             }
254 
255             // For ObjectDataSource we special-case Nullable<T> and do nothing because these
256             // types will get converted when we actually call the method.
257             if (ignoreNullableTypeChanges && IsNullableType(value.GetType())) {
258                 return value;
259             }
260 
261             if (dbType == DbType.DateTimeOffset) {
262                 if (value is DateTimeOffset) {
263                     return value;
264                 }
265                 return DateTimeOffset.Parse(value.ToString(), CultureInfo.CurrentCulture);
266             }
267             else if (dbType == DbType.Time) {
268                 if (value is TimeSpan) {
269                     return value;
270                 }
271                 return TimeSpan.Parse(value.ToString(), CultureInfo.CurrentCulture);
272             }
273             else if (dbType == DbType.Guid) {
274                 if (value is Guid) {
275                     return value;
276                 }
277                 return new Guid(value.ToString());
278             }
279 
280             Debug.Fail("Should never reach this point.");
281             return null;
282         }
283 
GetValue(object value, string defaultValue, TypeCode type, bool convertEmptyStringToNull, bool ignoreNullableTypeChanges)284         internal static object GetValue(object value, string defaultValue, TypeCode type, bool convertEmptyStringToNull, bool ignoreNullableTypeChanges) {
285             // Convert.ChangeType() throws if you attempt to convert to DBNull, so we have to special case it.
286             if (type == TypeCode.DBNull) {
287                 return DBNull.Value;
288             }
289 
290             value = HandleNullValue(value, defaultValue, convertEmptyStringToNull);
291             if (value == null) {
292                 return null;
293             }
294 
295             if (type == TypeCode.Object || type == TypeCode.Empty) {
296                 return value;
297             }
298 
299             // For ObjectDataSource we special-case Nullable<T> and do nothing because these
300             // types will get converted when we actually call the method.
301             if (ignoreNullableTypeChanges && IsNullableType(value.GetType())) {
302                 return value;
303             }
304             return value = Convert.ChangeType(value, type, CultureInfo.CurrentCulture);;
305         }
306 
HandleNullValue(object value, string defaultValue, bool convertEmptyStringToNull)307         private static object HandleNullValue(object value, string defaultValue, bool convertEmptyStringToNull) {
308             // Get the value and convert it to the default value if it is null
309             if (convertEmptyStringToNull) {
310                 string stringValue = value as string;
311                 if ((stringValue != null) && (stringValue.Length == 0)) {
312                     value = null;
313                 }
314             }
315             if (value == null) {
316                 // Attempt to use the default value, but if it is null too, just return null immediately
317                 if (convertEmptyStringToNull && String.IsNullOrEmpty(defaultValue)) {
318                     defaultValue = null;
319                 }
320                 if (defaultValue == null) {
321                     return null;
322                 }
323                 value = defaultValue;
324             }
325             return value;
326         }
327 
IsNullableType(Type type)328         private static bool IsNullableType(Type type) {
329             return type.IsGenericType && (type.GetGenericTypeDefinition() == typeof(Nullable<>));
330         }
331 
332         /// <devdoc>
333         /// Gets/sets the size of the parameter.
334         /// </devdoc>
335         [
336         DefaultValue(0),
337         WebCategory("Parameter"),
338         WebSysDescription(SR.Parameter_Size),
339         ]
340         public int Size {
341             get {
342                 object o = ViewState["Size"];
343                 if (o == null)
344                     return 0;
345                 return (int)o;
346             }
347             set {
348                 if (Size != value) {
349                     ViewState["Size"] = value;
350                     OnParameterChanged();
351                 }
352             }
353         }
354 
355 
356         /// <devdoc>
357         /// Gets/sets the type of the parameter's value.
358         /// </devdoc>
359         [
360         DefaultValue(TypeCode.Empty),
361         WebCategory("Parameter"),
362         WebSysDescription(SR.Parameter_Type),
363         ]
364         public TypeCode Type {
365             get {
366                 object o = ViewState["Type"];
367                 if (o == null)
368                     return TypeCode.Empty;
369                 return (TypeCode)o;
370             }
371             set {
372                 if (value < TypeCode.Empty || value > TypeCode.String) {
373                     throw new ArgumentOutOfRangeException("value");
374                 }
375 
376                 if (Type != value) {
377                     ViewState["Type"] = value;
378                     OnParameterChanged();
379                 }
380             }
381         }
382 
383 
384         /// <devdoc>
385         /// Gets/sets whether an empty string should be treated as a null value. If this property is set to true
386         /// and the value is an empty string, the default value will be used.
387         /// </devdoc>
388         [
389         DefaultValue(true),
390         WebCategory("Parameter"),
391         WebSysDescription(SR.Parameter_ConvertEmptyStringToNull),
392         ]
393         public bool ConvertEmptyStringToNull {
394             get {
395                 object o = ViewState["ConvertEmptyStringToNull"];
396                 if (o == null)
397                     return true;
398                 return (bool)o;
399             }
400             set {
401                 if (ConvertEmptyStringToNull != value) {
402                     ViewState["ConvertEmptyStringToNull"] = value;
403                     OnParameterChanged();
404                 }
405             }
406         }
407 
408 
409         /// <devdoc>
410         /// Indicates a dictionary of state information that allows you to save and restore
411         /// the state of a Parameter across multiple requests for the same page.
412         /// </devdoc>
413         [
414         Browsable(false),
415         DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
416         ]
417         protected StateBag ViewState {
418             get {
419                 if (_viewState == null) {
420                     _viewState = new StateBag();
421                     if (_tracking)
422                         _viewState.TrackViewState();
423                 }
424 
425                 return _viewState;
426             }
427         }
428 
429 
430 
431         /// <devdoc>
432         /// Creates a new Parameter that is a copy of this Parameter.
433         /// </devdoc>
Clone()434         protected virtual Parameter Clone() {
435             return new Parameter(this);
436         }
437 
438 
ConvertDbTypeToTypeCode(DbType dbType)439         public static TypeCode ConvertDbTypeToTypeCode(DbType dbType) {
440             switch (dbType) {
441                 case DbType.AnsiString:
442                 case DbType.AnsiStringFixedLength:
443                 case DbType.String:
444                 case DbType.StringFixedLength:
445                     return TypeCode.String;
446                 case DbType.Boolean:
447                     return TypeCode.Boolean;
448                 case DbType.Byte:
449                     return TypeCode.Byte;
450                 case DbType.VarNumeric:     // ???
451                 case DbType.Currency:
452                 case DbType.Decimal:
453                     return TypeCode.Decimal;
454                 case DbType.Date:
455                 case DbType.DateTime:
456                 case DbType.DateTime2: // new Katmai type
457                 case DbType.Time:      // new Katmai type - no TypeCode for TimeSpan
458                     return TypeCode.DateTime;
459                 case DbType.Double:
460                     return TypeCode.Double;
461                 case DbType.Int16:
462                     return TypeCode.Int16;
463                 case DbType.Int32:
464                     return TypeCode.Int32;
465                 case DbType.Int64:
466                     return TypeCode.Int64;
467                 case DbType.SByte:
468                     return TypeCode.SByte;
469                 case DbType.Single:
470                     return TypeCode.Single;
471                 case DbType.UInt16:
472                     return TypeCode.UInt16;
473                 case DbType.UInt32:
474                     return TypeCode.UInt32;
475                 case DbType.UInt64:
476                     return TypeCode.UInt64;
477                 case DbType.Guid:           // ???
478                 case DbType.Binary:
479                 case DbType.Object:
480                 case DbType.DateTimeOffset: // new Katmai type - no TypeCode for DateTimeOffset
481                 default:
482                     return TypeCode.Object;
483             }
484         }
485 
486 
ConvertTypeCodeToDbType(TypeCode typeCode)487         public static DbType ConvertTypeCodeToDbType(TypeCode typeCode) {
488             // no TypeCode equivalent for TimeSpan or DateTimeOffset
489             switch (typeCode) {
490                 case TypeCode.Boolean:
491                     return DbType.Boolean;
492                 case TypeCode.Byte:
493                     return DbType.Byte;
494                 case TypeCode.Char:
495                     return DbType.StringFixedLength;    // ???
496                 case TypeCode.DateTime: // Used for Date, DateTime and DateTime2 DbTypes
497                     return DbType.DateTime;
498                 case TypeCode.Decimal:
499                     return DbType.Decimal;
500                 case TypeCode.Double:
501                     return DbType.Double;
502                 case TypeCode.Int16:
503                     return DbType.Int16;
504                 case TypeCode.Int32:
505                     return DbType.Int32;
506                 case TypeCode.Int64:
507                     return DbType.Int64;
508                 case TypeCode.SByte:
509                     return DbType.SByte;
510                 case TypeCode.Single:
511                     return DbType.Single;
512                 case TypeCode.String:
513                     return DbType.String;
514                 case TypeCode.UInt16:
515                     return DbType.UInt16;
516                 case TypeCode.UInt32:
517                     return DbType.UInt32;
518                 case TypeCode.UInt64:
519                     return DbType.UInt64;
520                 case TypeCode.DBNull:
521                 case TypeCode.Empty:
522                 case TypeCode.Object:
523                 default:
524                     return DbType.Object;
525             }
526         }
527 
528 
529         /// <devdoc>
530         /// Evaluates the parameter and returns the new value.
531         /// The control parameter is used to access the page's framework.
532         /// By default it returns the null, implying that the DefaultValue will
533         /// be the value.
534         /// </devdoc>
Evaluate(HttpContext context, Control control)535         protected internal virtual object Evaluate(HttpContext context, Control control) {
536             return null;
537         }
538 
539 
540         /// <devdoc>
541         /// Loads view state.
542         /// </devdoc>
LoadViewState(object savedState)543         protected virtual void LoadViewState(object savedState) {
544             if (savedState != null) {
545                 ViewState.LoadViewState(savedState);
546             }
547         }
548 
549 
550         /// <devdoc>
551         /// Raises the ParameterChanged event. This notifies a listener that it should re-evaluate the value.
552         /// </devdoc>
OnParameterChanged()553         protected void OnParameterChanged() {
554             if (_owner != null) {
555                 _owner.CallOnParametersChanged();
556             }
557         }
558 
559 
560         /// <devdoc>
561         /// Saves view state.
562         /// </devdoc>
SaveViewState()563         protected virtual object SaveViewState() {
564             return (_viewState != null) ? _viewState.SaveViewState() : null;
565         }
566 
567 
568         /// <devdoc>
569         /// Tells the Parameter to record its entire state into view state.
570         /// </devdoc>
SetDirty()571         protected internal virtual void SetDirty() {
572             ViewState.SetDirty(true);
573         }
574 
575         /// <devdoc>
576         /// Tells the Parameter the collection it belongs to
577         /// </devdoc>
SetOwner(ParameterCollection owner)578         internal void SetOwner(ParameterCollection owner) {
579             _owner = owner;
580         }
581 
582 
583         /// <devdoc>
584         /// Converts the Parameter to a string value.
585         /// </devdoc>
ToString()586         public override string ToString() {
587             return this.Name;
588         }
589 
590 
591         /// <devdoc>
592         /// Tells the Parameter to start tracking property changes.
593         /// </devdoc>
TrackViewState()594         protected virtual void TrackViewState() {
595             _tracking = true;
596 
597             if (_viewState != null) {
598                 _viewState.TrackViewState();
599             }
600         }
601 
602         /// <devdoc>
603         /// Updates the value of parameter.
604         /// If the value changed, this will raise the ParametersChanged event of the ParameterCollection it belongs to.
605         /// The control parameter is used to access the page's framework.
606         /// </devdoc>
UpdateValue(HttpContext context, Control control)607         internal void UpdateValue(HttpContext context, Control control) {
608             object oldValue = ViewState["ParameterValue"];
609             object newValue = Evaluate(context, control);
610 
611             ViewState["ParameterValue"] = newValue;
612 
613             // If you have chains of dependency, like one control with a control parameter on another, and then a third with a control
614             // parameter on the second, the order in which the evaluations take place is non-deterministic and may create incorrect
615             // evaluation of parameters because all our evaluation happens during LoadComplete.  The correct solution is to call DataBind
616             // on the third control when the second control's selected value changes.  Hacky, but we don't support specifying dependency
617             // chains on data sources.
618             if ((newValue == null && oldValue != null) || (newValue != null && !newValue.Equals(oldValue))) {
619                 OnParameterChanged();
620             }
621         }
622 
623 
624         #region Implementation of ICloneable
625 
626         /// <internalonly/>
ICloneable.Clone()627         object ICloneable.Clone() {
628             return Clone();
629         }
630         #endregion
631 
632 
633         #region Implementation of IStateManager
634 
635         /// <internalonly/>
636         bool IStateManager.IsTrackingViewState {
637             get {
638                 return IsTrackingViewState;
639             }
640         }
641 
642 
643         /// <internalonly/>
IStateManager.LoadViewState(object savedState)644         void IStateManager.LoadViewState(object savedState) {
645             LoadViewState(savedState);
646         }
647 
648 
649         /// <internalonly/>
IStateManager.SaveViewState()650         object IStateManager.SaveViewState() {
651             return SaveViewState();
652         }
653 
654 
655         /// <internalonly/>
IStateManager.TrackViewState()656         void IStateManager.TrackViewState() {
657             TrackViewState();
658         }
659         #endregion
660     }
661 }
662 
663 
664