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