1 //------------------------------------------------------------------------------ 2 // <copyright file="DataColumn.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 { 10 using System; 11 using System.Xml; 12 using System.Data.Common; 13 using System.ComponentModel; 14 using System.Diagnostics; 15 using System.Collections; 16 using System.Globalization; 17 using System.Data.SqlTypes; 18 using System.Xml.Serialization; 19 using System.Collections.Generic; 20 using System.Runtime.CompilerServices; 21 22 /// <devdoc> 23 /// <para> 24 /// Represents one column of data in a <see cref='System.Data.DataTable'/>. 25 /// </para> 26 /// </devdoc> 27 [ 28 ToolboxItem(false), 29 DesignTimeVisible(false), 30 DefaultProperty("ColumnName"), 31 Editor("Microsoft.VSDesigner.Data.Design.DataColumnEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing), 32 ] 33 public class DataColumn : MarshalByValueComponent { 34 35 // properties 36 private bool allowNull = true; 37 private string caption = null; 38 private string _columnName = null; 39 private Type dataType = null; 40 private StorageType _storageType; 41 internal object defaultValue = DBNull.Value; // DefaultValue Converter 42 private DataSetDateTime _dateTimeMode = DataSetDateTime.UnspecifiedLocal; 43 private DataExpression expression = null; 44 private int maxLength = -1; 45 private int _ordinal = -1; 46 private bool readOnly = false; 47 internal Index sortIndex = null; 48 internal DataTable table = null; 49 private bool unique = false; 50 internal MappingType columnMapping = MappingType.Element; 51 internal int _hashCode; 52 53 internal int errors; 54 private bool isSqlType = false; 55 private bool implementsINullable = false; 56 private bool implementsIChangeTracking = false; 57 private bool implementsIRevertibleChangeTracking = false; 58 private bool implementsIXMLSerializable = false; 59 60 private bool defaultValueIsNull = true; 61 62 // list of columns whose expression consume values from this column 63 internal List<DataColumn> dependentColumns = null; 64 65 // collections 66 internal PropertyCollection extendedProperties = null; 67 68 // events 69 private PropertyChangedEventHandler onPropertyChangingDelegate = null; 70 71 // state 72 private DataStorage _storage; 73 74 /// <summary>represents current value to return, usage pattern is .get_Current then MoveAfter</summary> 75 private AutoIncrementValue autoInc; 76 77 // 78 // The _columnClass member is the class for the unfoliated virtual nodes in the XML. 79 // 80 internal string _columnUri = null; 81 private string _columnPrefix = ""; 82 internal string encodedColumnName = null; 83 84 // 85 internal string dttype = ""; // The type specified in dt:type attribute 86 internal SimpleType simpleType = null; 87 88 private static int _objectTypeCount; // Bid counter 89 private readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); 90 91 /// <devdoc> 92 /// <para> 93 /// Initializes a new instance of a <see cref='System.Data.DataColumn'/> 94 /// class. 95 /// </para> 96 /// </devdoc> DataColumn()97 public DataColumn() : this(null, typeof(string), null, MappingType.Element) { 98 } 99 100 /// <devdoc> 101 /// <para> 102 /// Inititalizes a new instance of the <see cref='System.Data.DataColumn'/> class 103 /// using the specified column name. 104 /// </para> 105 /// </devdoc> DataColumn(string columnName)106 public DataColumn(string columnName) : this(columnName, typeof(string), null, MappingType.Element) { 107 } 108 109 /// <devdoc> 110 /// <para> 111 /// Inititalizes a new instance of the <see cref='System.Data.DataColumn'/> class 112 /// using the specified column name and data type. 113 /// </para> 114 /// </devdoc> DataColumn(string columnName, Type dataType)115 public DataColumn(string columnName, Type dataType) : this(columnName, dataType, null, MappingType.Element) { 116 } 117 118 /// <devdoc> 119 /// <para> 120 /// Initializes a new instance 121 /// of the <see cref='System.Data.DataColumn'/> class 122 /// using the specified name, data type, and expression. 123 /// </para> 124 /// </devdoc> DataColumn(string columnName, Type dataType, string expr)125 public DataColumn(string columnName, Type dataType, string expr) : this(columnName, dataType, expr, MappingType.Element) { 126 } 127 128 /// <devdoc> 129 /// <para> 130 /// Initializes a new instance of the <see cref='System.Data.DataColumn'/> class 131 /// using 132 /// the specified name, data type, expression, and value that determines whether the 133 /// column is an attribute. 134 /// </para> 135 /// </devdoc> DataColumn(string columnName, Type dataType, string expr, MappingType type)136 public DataColumn(string columnName, Type dataType, string expr, MappingType type) { 137 GC.SuppressFinalize(this); 138 Bid.Trace("<ds.DataColumn.DataColumn|API> %d#, columnName='%ls', expr='%ls', type=%d{ds.MappingType}\n", 139 ObjectID, columnName, expr, (int)type); 140 141 if (dataType == null) { 142 throw ExceptionBuilder.ArgumentNull("dataType"); 143 } 144 145 StorageType typeCode = DataStorage.GetStorageType(dataType); 146 if (DataStorage.ImplementsINullableValue(typeCode, dataType)) { 147 throw ExceptionBuilder.ColumnTypeNotSupported(); 148 } 149 _columnName = columnName ?? string.Empty; 150 151 SimpleType stype = SimpleType.CreateSimpleType(typeCode, dataType); 152 if (null != stype) { 153 this.SimpleType = stype; 154 } 155 UpdateColumnType(dataType, typeCode); 156 157 if ((null != expr) && (0 < expr.Length)) { 158 // @perfnote: its a performance hit to set Expression to the empty str when we know it will come out null 159 this.Expression = expr; 160 } 161 this.columnMapping = type; 162 } 163 164 UpdateColumnType(Type type, StorageType typeCode)165 private void UpdateColumnType(Type type, StorageType typeCode) { 166 dataType = type; 167 _storageType = typeCode; 168 if (StorageType.DateTime != typeCode) { // revert _dateTimeMode back to default, when column type is changed 169 _dateTimeMode = DataSetDateTime.UnspecifiedLocal; 170 } 171 DataStorage.ImplementsInterfaces( 172 typeCode, type, 173 out isSqlType, 174 out implementsINullable, 175 out implementsIXMLSerializable, 176 out implementsIChangeTracking, 177 out implementsIRevertibleChangeTracking); 178 179 if (!isSqlType && implementsINullable) { 180 SqlUdtStorage.GetStaticNullForUdtType(type); 181 } 182 } 183 184 // PUBLIC PROPERTIES 185 186 /// <devdoc> 187 /// <para> 188 /// Gets or sets a value indicating whether null 189 /// values are 190 /// allowed in this column for rows belonging to the table. 191 /// </para> 192 /// </devdoc> 193 [ 194 ResCategoryAttribute(Res.DataCategory_Data), 195 DefaultValue(true), 196 ResDescriptionAttribute(Res.DataColumnAllowNullDescr) 197 ] 198 public bool AllowDBNull { 199 get { 200 return allowNull; 201 } 202 set { 203 IntPtr hscp; 204 Bid.ScopeEnter(out hscp, "<ds.DataColumn.set_AllowDBNull|API> %d#, %d{bool}\n", ObjectID, value); 205 try { 206 if (allowNull != value) { 207 if (table != null) { 208 if (!value && table.EnforceConstraints) 209 CheckNotAllowNull(); 210 } 211 this.allowNull = value; 212 } 213 // 214 } 215 finally { 216 Bid.ScopeLeave(ref hscp); 217 } 218 } 219 } 220 221 /// <devdoc> 222 /// <para> 223 /// Gets or 224 /// sets a value indicating whether the column automatically increments the value of the column for new 225 /// rows added to the table. 226 /// </para> 227 /// </devdoc> 228 [ 229 ResCategoryAttribute(Res.DataCategory_Data), 230 RefreshProperties(RefreshProperties.All), 231 DefaultValue(false), 232 ResDescriptionAttribute(Res.DataColumnAutoIncrementDescr) 233 ] 234 public bool AutoIncrement { 235 get { 236 return ((null != autoInc) && (autoInc.Auto)); 237 } 238 set { 239 Bid.Trace("<ds.DataColumn.set_AutoIncrement|API> %d#, %d{bool}\n", ObjectID, value); 240 if (this.AutoIncrement != value) { 241 if (value) { 242 if (expression != null) { 243 throw ExceptionBuilder.AutoIncrementAndExpression(); 244 } 245 // if (defaultValue != null && defaultValue != DBNull.Value) { 246 if (!DefaultValueIsNull) { 247 throw ExceptionBuilder.AutoIncrementAndDefaultValue(); 248 } 249 if (!IsAutoIncrementType(DataType)) { 250 if (HasData) { 251 throw ExceptionBuilder.AutoIncrementCannotSetIfHasData(DataType.Name); 252 } 253 DataType = typeof(int); 254 } 255 } 256 257 this.AutoInc.Auto = value; 258 } 259 } 260 } 261 262 internal object AutoIncrementCurrent { 263 get { return ((null != this.autoInc) ? this.autoInc.Current : this.AutoIncrementSeed); } 264 set { 265 if ((System.Numerics.BigInteger)this.AutoIncrementSeed != BigIntegerStorage.ConvertToBigInteger(value, this.FormatProvider)) { 266 this.AutoInc.SetCurrent(value, this.FormatProvider); 267 } 268 } 269 } 270 271 internal AutoIncrementValue AutoInc { 272 get { 273 return (this.autoInc ?? (this.autoInc = ((this.DataType == typeof(System.Numerics.BigInteger)) 274 ? (AutoIncrementValue)new AutoIncrementBigInteger() 275 : new AutoIncrementInt64()))); 276 } 277 } 278 279 280 /// <devdoc> 281 /// <para> 282 /// Gets 283 /// or sets the starting value for a column that has its 284 /// <see cref='System.Data.DataColumn.AutoIncrement'/> property 285 /// set to <see langword='true'/> 286 /// . 287 /// </para> 288 /// </devdoc> 289 [ 290 ResCategoryAttribute(Res.DataCategory_Data), 291 DefaultValue((Int64)0), 292 ResDescriptionAttribute(Res.DataColumnAutoIncrementSeedDescr) 293 ] 294 public Int64 AutoIncrementSeed { 295 get { 296 return ((null != this.autoInc) ? this.autoInc.Seed : 0L); 297 } 298 set { 299 Bid.Trace("<ds.DataColumn.set_AutoIncrementSeed|API> %d#, %I64d\n", ObjectID, value); 300 if (this.AutoIncrementSeed != value) { 301 this.AutoInc.Seed = value; 302 } 303 } 304 } 305 306 /// <devdoc> 307 /// <para> 308 /// Gets or sets the increment used by a column with its <see cref='System.Data.DataColumn.AutoIncrement'/> 309 /// property set to <see langword='true'/> 310 /// . 311 /// </para> 312 /// </devdoc> 313 [ 314 ResCategoryAttribute(Res.DataCategory_Data), 315 DefaultValue((Int64)1), 316 ResDescriptionAttribute(Res.DataColumnAutoIncrementStepDescr) 317 ] 318 public Int64 AutoIncrementStep { 319 get { 320 return ((null != this.autoInc) ? this.autoInc.Step : 1L); 321 } 322 set { 323 Bid.Trace("<ds.DataColumn.set_AutoIncrementStep|API> %d#, %I64d\n", ObjectID, value); 324 if (this.AutoIncrementStep != value) { 325 this.AutoInc.Step = value; 326 } 327 } 328 } 329 330 /// <devdoc> 331 /// <para> 332 /// Gets or sets 333 /// the caption for this column. 334 /// </para> 335 /// </devdoc> 336 [ 337 ResCategoryAttribute(Res.DataCategory_Data), 338 ResDescriptionAttribute(Res.DataColumnCaptionDescr) 339 ] 340 public string Caption { 341 get { 342 return (caption != null) ? caption : _columnName; 343 } 344 set { 345 if (value == null) 346 value = ""; 347 348 if (caption == null || String.Compare(caption, value, true, Locale) != 0) { 349 caption = value; 350 } 351 } 352 } 353 354 /// <devdoc> 355 /// <para> 356 /// Resets the <see cref='System.Data.DataColumn.Caption'/> property to its previous value, or 357 /// to <see langword='null'/> . 358 /// </para> 359 /// </devdoc> ResetCaption()360 private void ResetCaption() { 361 if (caption != null) { 362 caption = null; 363 } 364 } 365 366 /// <devdoc> 367 /// <para> 368 /// Gets a value indicating whether the <see cref='System.Data.DataColumn.Caption'/> has been explicitly set. 369 /// </para> 370 /// </devdoc> ShouldSerializeCaption()371 private bool ShouldSerializeCaption() { 372 return (caption != null); 373 } 374 375 /// <devdoc> 376 /// <para> 377 /// Gets or sets the name of the column within the <see cref='System.Data.DataColumnCollection'/>. 378 /// </para> 379 /// </devdoc> 380 [ 381 RefreshProperties(RefreshProperties.All), 382 ResCategoryAttribute(Res.DataCategory_Data), 383 DefaultValue(""), 384 ResDescriptionAttribute(Res.DataColumnColumnNameDescr) 385 ] 386 public string ColumnName { 387 get { 388 return _columnName; 389 } 390 set { 391 IntPtr hscp; 392 Bid.ScopeEnter(out hscp, "<ds.DataColumn.set_ColumnName|API> %d#, '%ls'\n", ObjectID, value); 393 try { 394 if (value == null) { 395 value = ""; 396 } 397 398 if (String.Compare(_columnName, value, true, Locale) != 0) { 399 if (table != null) { 400 if (value.Length == 0) 401 throw ExceptionBuilder.ColumnNameRequired(); 402 403 table.Columns.RegisterColumnName(value, this); 404 if (_columnName.Length != 0) 405 table.Columns.UnregisterName(_columnName); 406 } 407 408 RaisePropertyChanging("ColumnName"); 409 _columnName = value; 410 encodedColumnName = null; 411 if (table != null) { 412 table.Columns.OnColumnPropertyChanged(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, this)); 413 } 414 } 415 else if (_columnName != value) { 416 RaisePropertyChanging("ColumnName"); 417 _columnName = value; 418 encodedColumnName = null; 419 if (table != null) { 420 table.Columns.OnColumnPropertyChanged(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, this)); 421 } 422 } 423 } 424 finally { 425 Bid.ScopeLeave(ref hscp); 426 } 427 } 428 } 429 430 internal string EncodedColumnName { 431 get { 432 if (this.encodedColumnName == null) { 433 this.encodedColumnName = XmlConvert.EncodeLocalName(this.ColumnName); 434 } 435 Debug.Assert(this.encodedColumnName != null && this.encodedColumnName.Length != 0); 436 return this.encodedColumnName; 437 } 438 } 439 440 internal IFormatProvider FormatProvider { 441 get { 442 // used for formating/parsing not comparing 443 return ((null != table) ? table.FormatProvider : CultureInfo.CurrentCulture); 444 } 445 } 446 447 internal CultureInfo Locale { 448 get { 449 // used for comparing not formating/parsing 450 return ((null != table) ? table.Locale : CultureInfo.CurrentCulture); 451 } 452 } 453 454 internal int ObjectID { 455 get { 456 return _objectID; 457 } 458 } 459 460 [ 461 ResCategoryAttribute(Res.DataCategory_Data), 462 DefaultValue(""), 463 ResDescriptionAttribute(Res.DataColumnPrefixDescr) 464 ] 465 public string Prefix { 466 get { return _columnPrefix; } 467 set { 468 if (value == null) 469 value = ""; 470 Bid.Trace("<ds.DataColumn.set_Prefix|API> %d#, '%ls'\n", ObjectID, value); 471 472 if ((XmlConvert.DecodeName(value) == value) && (XmlConvert.EncodeName(value) != value)) 473 throw ExceptionBuilder.InvalidPrefix(value); 474 475 _columnPrefix = value; 476 477 } 478 } 479 480 // Return the field value as a string. If the field value is NULL, then NULL is return. 481 // If the column type is string and it's value is empty, then the empty string is returned. 482 // If the column type is not string, or the column type is string and the value is not empty string, then a non-empty string is returned 483 // This method does not throw any formatting exceptions, since we can always format the field value to a string. GetColumnValueAsString(DataRow row, DataRowVersion version)484 internal string GetColumnValueAsString(DataRow row, DataRowVersion version) { 485 486 object objValue = this[row.GetRecordFromVersion(version)]; 487 488 if (DataStorage.IsObjectNull(objValue)) { 489 return null; 490 } 491 492 string value = ConvertObjectToXml(objValue); 493 Debug.Assert(value != null); 494 495 return value; 496 } 497 498 /// <devdoc> 499 /// Whether this column computes values. 500 /// </devdoc> 501 internal bool Computed { 502 get { 503 return this.expression != null; 504 } 505 } 506 507 /// <devdoc> 508 /// The internal expression object that computes the values. 509 /// </devdoc> 510 internal DataExpression DataExpression { 511 get { 512 return this.expression; 513 } 514 } 515 516 /// <devdoc> 517 /// <para> 518 /// The type 519 /// of data stored in thecolumn. 520 /// </para> 521 /// </devdoc> 522 [ 523 ResCategoryAttribute(Res.DataCategory_Data), 524 DefaultValue(typeof(string)), 525 RefreshProperties(RefreshProperties.All), 526 TypeConverter(typeof(ColumnTypeConverter)), 527 ResDescriptionAttribute(Res.DataColumnDataTypeDescr) 528 ] 529 public Type DataType { 530 get { 531 return dataType; 532 } 533 set { 534 if (dataType != value) { 535 if (HasData) { 536 throw ExceptionBuilder.CantChangeDataType(); 537 } 538 if (value == null) { 539 throw ExceptionBuilder.NullDataType(); 540 } 541 StorageType typeCode = DataStorage.GetStorageType(value); 542 if (DataStorage.ImplementsINullableValue(typeCode, value)) { 543 throw ExceptionBuilder.ColumnTypeNotSupported(); 544 } 545 if (table != null && IsInRelation()) { 546 throw ExceptionBuilder.ColumnsTypeMismatch(); 547 } 548 if (typeCode == StorageType.BigInteger && this.expression != null) 549 { 550 throw ExprException.UnsupportedDataType(value); 551 } 552 553 // If the DefualtValue is different from the Column DataType, we will coerce the value to the DataType 554 if (!DefaultValueIsNull) { 555 try { 556 if (this.defaultValue is System.Numerics.BigInteger) { 557 this.defaultValue = BigIntegerStorage.ConvertFromBigInteger((System.Numerics.BigInteger)this.defaultValue, value, this.FormatProvider); 558 } 559 else if (typeof(System.Numerics.BigInteger) == value) { 560 this.defaultValue = BigIntegerStorage.ConvertToBigInteger(this.defaultValue, this.FormatProvider); 561 } 562 else if (typeof(string) == value) { // since string types can be null in value! DO NOT REMOVE THIS 563 defaultValue = DefaultValue.ToString(); 564 } 565 else if (typeof(SqlString) == value) { // since string types can be null in value! DO NOT REMOVE THIS 566 defaultValue = SqlConvert.ConvertToSqlString(DefaultValue); 567 } 568 else if (typeof(object) != value) { 569 DefaultValue = SqlConvert.ChangeTypeForDefaultValue(DefaultValue, value, FormatProvider); 570 } 571 } 572 catch (InvalidCastException ex) { 573 throw ExceptionBuilder.DefaultValueDataType(ColumnName, DefaultValue.GetType(), value, ex); 574 } 575 catch (FormatException ex) { 576 throw ExceptionBuilder.DefaultValueDataType(ColumnName, DefaultValue.GetType(), value, ex); 577 } 578 } 579 580 if (this.ColumnMapping == MappingType.SimpleContent) 581 if (value == typeof(Char)) 582 throw ExceptionBuilder.CannotSetSimpleContentType(ColumnName, value); 583 584 SimpleType = SimpleType.CreateSimpleType(typeCode, value); 585 if (StorageType.String == typeCode) { 586 maxLength = -1; 587 } 588 UpdateColumnType(value, typeCode); 589 XmlDataType = null; 590 591 if (AutoIncrement) { 592 if (!IsAutoIncrementType(value)) { 593 AutoIncrement = false; 594 } 595 596 if (null != this.autoInc) { 597 // if you already have data you can't change the data type 598 // if you don't have data - you wouldn't have incremented AutoIncrementCurrent. 599 AutoIncrementValue inc = this.autoInc; 600 this.autoInc = null; 601 this.AutoInc.Auto = inc.Auto; // recreate with correct datatype 602 this.AutoInc.Seed = inc.Seed; 603 this.AutoInc.Step = inc.Step; 604 if (this.autoInc.DataType == inc.DataType) { 605 this.autoInc.Current = inc.Current; 606 } 607 else if (inc.DataType == typeof(Int64)) { 608 this.AutoInc.Current = (System.Numerics.BigInteger)(long)inc.Current; 609 } 610 else { 611 this.AutoInc.Current = checked((long)(System.Numerics.BigInteger)inc.Current); 612 } 613 } 614 } 615 } 616 } 617 } 618 619 [ 620 ResCategoryAttribute(Res.DataCategory_Data), 621 DefaultValue(DataSetDateTime.UnspecifiedLocal), 622 RefreshProperties(RefreshProperties.All), 623 ResDescriptionAttribute(Res.DataColumnDateTimeModeDescr) 624 ] 625 public DataSetDateTime DateTimeMode { 626 get { 627 return _dateTimeMode; 628 } 629 set { 630 if (_dateTimeMode != value) { 631 if (DataType != typeof(DateTime) && value != DataSetDateTime.UnspecifiedLocal) { //Check for column being DateTime. If the column is not DateTime make sure the value that is being is only the default[UnspecifiedLocal]. 632 throw ExceptionBuilder.CannotSetDateTimeModeForNonDateTimeColumns(); 633 } 634 switch (value) { 635 case DataSetDateTime.Utc: 636 case DataSetDateTime.Local: 637 if (HasData) { 638 throw ExceptionBuilder.CantChangeDateTimeMode(_dateTimeMode, value); 639 } 640 break; 641 case DataSetDateTime.Unspecified: 642 case DataSetDateTime.UnspecifiedLocal: 643 if (_dateTimeMode == DataSetDateTime.Unspecified || _dateTimeMode == DataSetDateTime.UnspecifiedLocal) { 644 break; 645 } 646 if (HasData) { 647 throw ExceptionBuilder.CantChangeDateTimeMode(_dateTimeMode, value); 648 } 649 break; 650 default: 651 throw ExceptionBuilder.InvalidDateTimeMode(value); 652 } 653 _dateTimeMode = value; 654 } 655 } 656 } 657 658 /// <devdoc> 659 /// <para>Gets or sets the default value for the 660 /// column when creating new rows.</para> 661 /// </devdoc> 662 [ 663 ResCategoryAttribute(Res.DataCategory_Data), 664 ResDescriptionAttribute(Res.DataColumnDefaultValueDescr), 665 TypeConverter(typeof(DefaultValueTypeConverter)) 666 ] 667 public object DefaultValue { 668 get { 669 Debug.Assert(defaultValue != null, "It should not have been set to null."); 670 if (defaultValue == DBNull.Value && this.implementsINullable) { // for perf I dont access property 671 if (_storage != null) 672 defaultValue = _storage._nullValue; 673 else if (this.isSqlType) 674 defaultValue = SqlConvert.ChangeTypeForDefaultValue(defaultValue, this.dataType, FormatProvider); 675 else if (this.implementsINullable) { 676 System.Reflection.PropertyInfo propInfo = this.dataType.GetProperty("Null", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static); 677 if (propInfo != null) 678 defaultValue = propInfo.GetValue(null, null); 679 } 680 } 681 682 return defaultValue; 683 } 684 set { 685 Bid.Trace("<ds.DataColumn.set_DefaultValue|API> %d#\n", ObjectID); 686 if (defaultValue == null || !DefaultValue.Equals(value)) { 687 if (AutoIncrement) { 688 throw ExceptionBuilder.DefaultValueAndAutoIncrement(); 689 } 690 691 object newDefaultValue = (value == null) ? DBNull.Value : value; 692 if (newDefaultValue != DBNull.Value && DataType != typeof(Object)) { 693 // If the DefualtValue is different from the Column DataType, we will coerce the value to the DataType 694 try { 695 newDefaultValue = SqlConvert.ChangeTypeForDefaultValue(newDefaultValue, DataType, FormatProvider); 696 } 697 catch (InvalidCastException ex) { 698 throw ExceptionBuilder.DefaultValueColumnDataType(ColumnName, newDefaultValue.GetType(), DataType, ex); 699 } 700 } 701 defaultValue = newDefaultValue; 702 // SQL BU Defect Tracking 401640: should not assign any value until conversion is successful. 703 defaultValueIsNull = ((newDefaultValue == DBNull.Value) || (this.ImplementsINullable && DataStorage.IsObjectSqlNull(newDefaultValue))) ? true : false; 704 } 705 } 706 } 707 708 internal bool DefaultValueIsNull { 709 get { 710 return defaultValueIsNull; 711 } 712 } 713 BindExpression()714 internal void BindExpression() { 715 this.DataExpression.Bind(this.table); 716 } 717 718 /// <devdoc> 719 /// <para>Gets 720 /// or sets the expresssion used to either filter rows, calculate the column's 721 /// value, or create an aggregate column.</para> 722 /// </devdoc> 723 [ 724 ResCategoryAttribute(Res.DataCategory_Data), 725 RefreshProperties(RefreshProperties.All), 726 DefaultValue(""), 727 ResDescriptionAttribute(Res.DataColumnExpressionDescr) 728 ] 729 public string Expression { 730 get { 731 return (this.expression == null ? "" : this.expression.Expression); 732 } 733 set { 734 IntPtr hscp; 735 Bid.ScopeEnter(out hscp, "<ds.DataColumn.set_Expression|API> %d#, '%ls'\n", ObjectID, value); 736 737 if (value == null) { 738 value = ""; 739 } 740 741 try { 742 DataExpression newExpression = null; 743 if (value.Length > 0) { 744 DataExpression testExpression = new DataExpression(this.table, value, this.dataType); 745 if (testExpression.HasValue) { 746 newExpression = testExpression; 747 } 748 } 749 750 if (expression == null && newExpression != null) { 751 if (AutoIncrement || Unique) { 752 throw ExceptionBuilder.ExpressionAndUnique(); 753 } 754 755 // We need to make sure the column is not involved in any Constriants 756 if (table != null) { 757 for (int i = 0; i < table.Constraints.Count; i++) { 758 if (table.Constraints[i].ContainsColumn(this)) { 759 throw ExceptionBuilder.ExpressionAndConstraint(this, table.Constraints[i]); 760 } 761 } 762 } 763 764 bool oldReadOnly = ReadOnly; 765 try { 766 ReadOnly = true; 767 } 768 catch (ReadOnlyException e) { 769 ExceptionBuilder.TraceExceptionForCapture(e); 770 ReadOnly = oldReadOnly; 771 throw ExceptionBuilder.ExpressionAndReadOnly(); 772 } 773 } 774 775 // re-calculate the evaluation queue 776 if (this.table != null) { 777 if (newExpression != null && newExpression.DependsOn(this)) { 778 throw ExceptionBuilder.ExpressionCircular(); 779 } 780 HandleDependentColumnList(expression, newExpression); 781 //hold onto oldExpression in case of error applying new Expression. 782 DataExpression oldExpression = this.expression; 783 this.expression = newExpression; 784 785 // because the column is attached to a table we need to re-calc values 786 try { 787 if (newExpression == null) { 788 for (int i = 0; i < table.RecordCapacity; i++) { 789 InitializeRecord(i); 790 } 791 } 792 else { 793 this.table.EvaluateExpressions(this); 794 } 795 // SQLBU 501916: DataTable internal index is corrupted:'5' 796 this.table.ResetInternalIndexes(this); 797 this.table.EvaluateDependentExpressions(this); 798 } 799 catch (Exception e1) { 800 // 801 if (!ADP.IsCatchableExceptionType(e1)) { 802 throw; 803 } 804 ExceptionBuilder.TraceExceptionForCapture(e1); 805 try { 806 // in the case of error we need to set the column expression to the old value 807 this.expression = oldExpression; 808 HandleDependentColumnList(newExpression, expression); 809 if (oldExpression == null) { 810 for (int i = 0; i < table.RecordCapacity; i++) { 811 InitializeRecord(i); 812 } 813 } 814 else { 815 this.table.EvaluateExpressions(this); 816 } 817 this.table.ResetInternalIndexes(this); 818 this.table.EvaluateDependentExpressions(this); 819 } 820 catch (Exception e2) { 821 // 822 if (!ADP.IsCatchableExceptionType(e2)) { 823 throw; 824 } 825 ExceptionBuilder.TraceExceptionWithoutRethrow(e2); 826 } 827 throw; 828 } 829 } 830 else { 831 //if column is not attached to a table, just set. 832 this.expression = newExpression; 833 } 834 } 835 finally { 836 Bid.ScopeLeave(ref hscp); 837 } 838 } 839 } 840 841 /// <devdoc> 842 /// <para>Gets the collection of custom user information.</para> 843 /// </devdoc> 844 [ 845 ResCategoryAttribute(Res.DataCategory_Data), 846 Browsable(false), 847 ResDescriptionAttribute(Res.ExtendedPropertiesDescr) 848 ] 849 public PropertyCollection ExtendedProperties { 850 get { 851 if (extendedProperties == null) { 852 extendedProperties = new PropertyCollection(); 853 } 854 return extendedProperties; 855 } 856 } 857 858 /// <devdoc> 859 /// Indicates whether this column is now storing data. 860 /// </devdoc> 861 internal bool HasData { 862 get { 863 return (_storage != null); 864 } 865 } 866 867 internal bool ImplementsINullable { 868 get { 869 return implementsINullable; 870 } 871 } 872 873 internal bool ImplementsIChangeTracking { 874 get { 875 return implementsIChangeTracking; 876 } 877 } 878 879 internal bool ImplementsIRevertibleChangeTracking { 880 get { 881 return implementsIRevertibleChangeTracking; 882 } 883 } 884 885 internal bool IsCloneable { 886 get { 887 Debug.Assert(null != _storage, "no storage"); 888 return _storage._isCloneable; 889 } 890 } 891 892 internal bool IsStringType { 893 get { 894 Debug.Assert(null != _storage, "no storage"); 895 return _storage._isStringType; 896 } 897 } 898 899 internal bool IsValueType { 900 get { 901 Debug.Assert(null != _storage, "no storage"); 902 return _storage._isValueType; 903 } 904 } 905 906 internal bool IsSqlType { 907 get { 908 return isSqlType; 909 } 910 } 911 SetMaxLengthSimpleType()912 private void SetMaxLengthSimpleType() { 913 if (this.simpleType != null) { 914 Debug.Assert(this.simpleType.CanHaveMaxLength(), "expected simpleType to be string"); 915 916 this.simpleType.MaxLength = maxLength; 917 // check if we reset the simpleType back to plain string 918 if (this.simpleType.IsPlainString()) { 919 this.simpleType = null; 920 } 921 else { 922 // Named Simple Type's Name should not be null 923 if (this.simpleType.Name != null && this.dttype != null) { 924 // if MaxLength is changed, we need to make namedsimpletype annonymous simpletype 925 this.simpleType.ConvertToAnnonymousSimpleType(); 926 this.dttype = null; 927 } 928 } 929 } 930 else if (-1 < maxLength) { 931 this.SimpleType = SimpleType.CreateLimitedStringType(maxLength); 932 } 933 } 934 [ 935 ResCategoryAttribute(Res.DataCategory_Data), 936 ResDescriptionAttribute(Res.DataColumnMaxLengthDescr), 937 DefaultValue(-1) 938 ] 939 public int MaxLength { 940 get { 941 return maxLength; 942 } 943 set { 944 IntPtr hscp; 945 Bid.ScopeEnter(out hscp, "<ds.DataColumn.set_MaxLength|API> %d#, %d\n", ObjectID, value); 946 947 try { 948 if (maxLength != value) { 949 if (this.ColumnMapping == MappingType.SimpleContent) { 950 throw ExceptionBuilder.CannotSetMaxLength2(this); 951 } 952 if ((DataType != typeof(string)) && (DataType != typeof(SqlString))) { 953 throw ExceptionBuilder.HasToBeStringType(this); 954 } 955 int oldValue = maxLength; 956 maxLength = Math.Max(value, -1); 957 958 if (((oldValue < 0) || (value < oldValue)) && (null != table) && table.EnforceConstraints) { 959 if (!CheckMaxLength()) { 960 maxLength = oldValue; 961 throw ExceptionBuilder.CannotSetMaxLength(this, value); 962 } 963 } 964 SetMaxLengthSimpleType(); 965 } 966 } 967 finally { 968 Bid.ScopeLeave(ref hscp); 969 } 970 } 971 } 972 973 [ 974 ResCategoryAttribute(Res.DataCategory_Data), 975 ResDescriptionAttribute(Res.DataColumnNamespaceDescr) 976 ] 977 public string Namespace { 978 get { 979 if (_columnUri == null) { 980 if (Table != null && columnMapping != MappingType.Attribute) { 981 return Table.Namespace; 982 } 983 return ""; 984 } 985 return _columnUri; 986 } 987 set { 988 Bid.Trace("<ds.DataColumn.set_Namespace|API> %d#, '%ls'\n", ObjectID, value); 989 990 if (_columnUri != value) { 991 if (columnMapping != MappingType.SimpleContent) { 992 RaisePropertyChanging("Namespace"); 993 _columnUri = value; 994 } 995 else if (value != this.Namespace) { 996 throw ExceptionBuilder.CannotChangeNamespace(this.ColumnName); 997 } 998 } 999 } 1000 } 1001 ShouldSerializeNamespace()1002 private bool ShouldSerializeNamespace() { 1003 return (_columnUri != null); 1004 } 1005 ResetNamespace()1006 private void ResetNamespace() { 1007 this.Namespace = null; 1008 } 1009 1010 /// <devdoc> 1011 /// <para> 1012 /// Gets the position of the column in the <see cref='System.Data.DataColumnCollection'/> 1013 /// collection. 1014 /// </para> 1015 /// </devdoc> 1016 [ 1017 ResCategoryAttribute(Res.DataCategory_Data), 1018 Browsable(false), 1019 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), 1020 ResDescriptionAttribute(Res.DataColumnOrdinalDescr) 1021 ] 1022 public int Ordinal { 1023 get { 1024 return _ordinal; 1025 } 1026 } 1027 SetOrdinal(int ordinal)1028 public void SetOrdinal(int ordinal) { 1029 if (_ordinal == -1) { 1030 throw ExceptionBuilder.ColumnNotInAnyTable(); 1031 } 1032 1033 if (this._ordinal != ordinal) { 1034 table.Columns.MoveTo(this, ordinal); 1035 } 1036 } 1037 SetOrdinalInternal(int ordinal)1038 internal void SetOrdinalInternal(int ordinal) { 1039 // 1040 if (this._ordinal != ordinal) { 1041 if (Unique && this._ordinal != -1 && ordinal == -1) { 1042 UniqueConstraint key = table.Constraints.FindKeyConstraint(this); 1043 if (key != null) 1044 table.Constraints.Remove(key); 1045 } 1046 // SQLBU 429176: remove the sortIndex when DataColumn is removed 1047 if ((null != sortIndex) && (-1 == ordinal)) { 1048 Debug.Assert(2 <= sortIndex.RefCount, "bad sortIndex refcount"); 1049 sortIndex.RemoveRef(); 1050 sortIndex.RemoveRef(); // second should remove it from index collection 1051 sortIndex = null; 1052 } 1053 int originalOrdinal = this._ordinal; 1054 this._ordinal = ordinal; 1055 if (originalOrdinal == -1 && this._ordinal != -1) { 1056 if (Unique) { 1057 UniqueConstraint key = new UniqueConstraint(this); 1058 table.Constraints.Add(key); 1059 } 1060 } 1061 } 1062 } 1063 1064 /// <devdoc> 1065 /// <para> 1066 /// Gets or sets a value 1067 /// indicating whether the column allows changes once a row has been added to the table. 1068 /// </para> 1069 /// </devdoc> 1070 [ 1071 ResCategoryAttribute(Res.DataCategory_Data), 1072 DefaultValue(false), 1073 ResDescriptionAttribute(Res.DataColumnReadOnlyDescr) 1074 ] 1075 public bool ReadOnly { 1076 get { 1077 return readOnly; 1078 } 1079 set { 1080 Bid.Trace("<ds.DataColumn.set_ReadOnly|API> %d#, %d{bool}\n", ObjectID, value); 1081 if (readOnly != value) { 1082 if (!value && expression != null) { 1083 throw ExceptionBuilder.ReadOnlyAndExpression(); 1084 } 1085 this.readOnly = value; 1086 } 1087 } 1088 } 1089 1090 [DebuggerBrowsable(DebuggerBrowsableState.Never)] // don't have debugger view expand this 1091 private Index SortIndex { 1092 get { 1093 if (sortIndex == null) { 1094 IndexField[] indexDesc = new IndexField[] { new IndexField(this, false) }; 1095 sortIndex = table.GetIndex(indexDesc, DataViewRowState.CurrentRows, (IFilter)null); 1096 sortIndex.AddRef(); 1097 } 1098 return sortIndex; 1099 } 1100 } 1101 1102 /// <devdoc> 1103 /// <para> 1104 /// Gets the <see cref='System.Data.DataTable'/> to which the column belongs to. 1105 /// </para> 1106 /// </devdoc> 1107 [ 1108 ResCategoryAttribute(Res.DataCategory_Data), 1109 Browsable(false), 1110 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), 1111 ResDescriptionAttribute(Res.DataColumnDataTableDescr) 1112 ] 1113 public DataTable Table { 1114 get { 1115 return table; 1116 } 1117 } 1118 1119 /// <devdoc> 1120 /// Internal mechanism for changing the table pointer. 1121 /// </devdoc> SetTable(DataTable table)1122 internal void SetTable(DataTable table) { 1123 if (this.table != table) { 1124 if (this.Computed) 1125 if ((table == null) || 1126 (!table.fInitInProgress && ((table.DataSet == null) || (!table.DataSet.fIsSchemaLoading && !table.DataSet.fInitInProgress)))) { 1127 // We need to re-bind all expression columns. 1128 this.DataExpression.Bind(table); 1129 } 1130 1131 if (Unique && this.table != null) { 1132 UniqueConstraint constraint = table.Constraints.FindKeyConstraint(this); 1133 if (constraint != null) 1134 table.Constraints.CanRemove(constraint, true); 1135 } 1136 this.table = table; 1137 _storage = null; // empty out storage for reuse. 1138 } 1139 } 1140 GetDataRow(int index)1141 private DataRow GetDataRow(int index) { 1142 return table.recordManager[index]; 1143 } 1144 1145 /// <devdoc> 1146 /// This is how data is pushed in and out of the column. 1147 /// </devdoc> 1148 internal object this[int record] { 1149 get { 1150 table.recordManager.VerifyRecord(record); 1151 Debug.Assert(null != _storage, "null storage"); 1152 return _storage.Get(record); 1153 } 1154 set { 1155 try { 1156 table.recordManager.VerifyRecord(record); 1157 Debug.Assert(null != _storage, "no storage"); 1158 Debug.Assert(null != value, "setting null, expecting dbnull"); 1159 _storage.Set(record, value); 1160 Debug.Assert(null != this.table, "storage with no DataTable on column"); 1161 } 1162 catch (Exception e) { 1163 ExceptionBuilder.TraceExceptionForCapture(e); 1164 throw ExceptionBuilder.SetFailed(value, this, DataType, e); 1165 } 1166 1167 if (AutoIncrement) { 1168 if (!_storage.IsNull(record)) { 1169 this.AutoInc.SetCurrentAndIncrement(_storage.Get(record)); 1170 } 1171 } 1172 if (Computed) {// if and only if it is Expression column, we will cache LastChangedColumn, otherwise DO NOT 1173 DataRow dr = GetDataRow(record); 1174 if (dr != null) { 1175 // at initialization time (datatable.NewRow(), we would fill the storage with default value, but at that time we wont have datarow) 1176 dr.LastChangedColumn = this; 1177 } 1178 } 1179 } 1180 } 1181 InitializeRecord(int record)1182 internal void InitializeRecord(int record) { 1183 Debug.Assert(null != _storage, "no storage"); 1184 _storage.Set(record, DefaultValue); 1185 } 1186 SetValue(int record, object value)1187 internal void SetValue(int record, object value) { // just silently set the value 1188 try { 1189 Debug.Assert(null != value, "setting null, expecting dbnull"); 1190 Debug.Assert(null != this.table, "storage with no DataTable on column"); 1191 Debug.Assert(null != _storage, "no storage"); 1192 _storage.Set(record, value); 1193 } 1194 catch (Exception e) { 1195 ExceptionBuilder.TraceExceptionForCapture(e); 1196 throw ExceptionBuilder.SetFailed(value, this, DataType, e); 1197 } 1198 1199 DataRow dr = GetDataRow(record); 1200 if (dr != null) { // at initialization time (datatable.NewRow(), we would fill the storage with default value, but at that time we wont have datarow) 1201 dr.LastChangedColumn = this; 1202 } 1203 } 1204 FreeRecord(int record)1205 internal void FreeRecord(int record) { 1206 Debug.Assert(null != _storage, "no storage"); 1207 _storage.Set(record, _storage._nullValue); 1208 } 1209 1210 /// <devdoc> 1211 /// <para> 1212 /// Gets or sets a value indicating whether the values in each row of the column must be unique. 1213 /// </para> 1214 /// </devdoc> 1215 [ 1216 ResCategoryAttribute(Res.DataCategory_Data), 1217 DefaultValue(false), 1218 ResDescriptionAttribute(Res.DataColumnUniqueDescr), 1219 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) 1220 ] 1221 public bool Unique { 1222 get { 1223 return unique; 1224 } 1225 set { 1226 IntPtr hscp; 1227 Bid.ScopeEnter(out hscp, "<ds.DataColumn.set_Unique|API> %d#, %d{bool}\n", ObjectID, value); 1228 try { 1229 if (unique != value) { 1230 if (value && expression != null) { 1231 throw ExceptionBuilder.UniqueAndExpression(); 1232 } 1233 UniqueConstraint oldConstraint = null; 1234 if (table != null) { 1235 if (value) 1236 CheckUnique(); 1237 else { 1238 for (System.Collections.IEnumerator e = Table.Constraints.GetEnumerator(); e.MoveNext(); ) { 1239 UniqueConstraint o = (e.Current as UniqueConstraint); 1240 if ((null != o) && (o.ColumnsReference.Length == 1) && (o.ColumnsReference[0] == this)) 1241 oldConstraint = o; 1242 } 1243 Debug.Assert(oldConstraint != null, "Should have found a column to remove from the collection."); 1244 table.Constraints.CanRemove(oldConstraint, true); 1245 } 1246 } 1247 1248 this.unique = value; 1249 1250 if (table != null) { 1251 if (value) { 1252 // This should not fail due to a duplicate constraint. unique would have 1253 // already been true if there was an existed UniqueConstraint for this column 1254 1255 UniqueConstraint constraint = new UniqueConstraint(this); 1256 Debug.Assert(table.Constraints.FindKeyConstraint(this) == null, "Should not be a duplication constraint in collection"); 1257 table.Constraints.Add(constraint); 1258 } 1259 else 1260 { 1261 table.Constraints.Remove(oldConstraint); 1262 // 1263 } 1264 } 1265 } 1266 } 1267 finally { 1268 Bid.ScopeLeave(ref hscp); 1269 } 1270 } 1271 } 1272 1273 1274 // FxCop Rule; getter not used! WebData 101301; so changing from Property to method InternalUnique(bool value)1275 internal void InternalUnique(bool value) { 1276 this.unique = value; 1277 } 1278 1279 internal string XmlDataType { 1280 get { 1281 return dttype; 1282 } 1283 set { 1284 dttype = value; 1285 } 1286 } 1287 1288 internal SimpleType SimpleType { 1289 get { 1290 return simpleType; 1291 } 1292 set { 1293 simpleType = value; 1294 // there is a change, since we are supporting hierarchy(bacause of Names Simple Type) old check (just one leel base check) is wrong 1295 if (value != null && value.CanHaveMaxLength()) 1296 maxLength = simpleType.MaxLength;// this is temp solution, since we dont let simple content to have 1297 //maxlength set but for simple type we want to set it, after coming to decision about it , we should 1298 // use MaxLength property 1299 } 1300 } 1301 1302 /// <devdoc> 1303 /// <para>Gets the <see cref='System.Data.MappingType'/> of the column.</para> 1304 /// </devdoc> 1305 [ 1306 DefaultValue(MappingType.Element), 1307 ResDescriptionAttribute(Res.DataColumnMappingDescr) 1308 ] 1309 public virtual MappingType ColumnMapping { 1310 get { 1311 return columnMapping; 1312 } 1313 set { 1314 Bid.Trace("<ds.DataColumn.set_ColumnMapping|API> %d#, %d{ds.MappingType}\n", ObjectID, (int)value); 1315 if (value != columnMapping) { 1316 1317 if (value == MappingType.SimpleContent && table != null) { 1318 int threshold = 0; 1319 if (columnMapping == MappingType.Element) 1320 threshold = 1; 1321 if (this.dataType == typeof(Char)) 1322 throw ExceptionBuilder.CannotSetSimpleContent(ColumnName, this.dataType); 1323 1324 if (table.XmlText != null && table.XmlText != this) 1325 throw ExceptionBuilder.CannotAddColumn3(); 1326 if (table.ElementColumnCount > threshold) 1327 throw ExceptionBuilder.CannotAddColumn4(this.ColumnName); 1328 } 1329 1330 RaisePropertyChanging("ColumnMapping"); 1331 1332 if (table != null) { 1333 if (columnMapping == MappingType.SimpleContent) 1334 table.xmlText = null; 1335 1336 if (value == MappingType.Element) 1337 table.ElementColumnCount++; 1338 else if (columnMapping == MappingType.Element) 1339 table.ElementColumnCount--; 1340 } 1341 1342 columnMapping = value; 1343 if (value == MappingType.SimpleContent) { 1344 _columnUri = null; 1345 if (table != null) { 1346 table.XmlText = this; 1347 } 1348 this.SimpleType = null; 1349 } 1350 } 1351 } 1352 } 1353 1354 internal event PropertyChangedEventHandler PropertyChanging { 1355 add { 1356 onPropertyChangingDelegate += value; 1357 } 1358 remove { 1359 onPropertyChangingDelegate -= value; 1360 } 1361 } 1362 CheckColumnConstraint(DataRow row, DataRowAction action)1363 internal void CheckColumnConstraint(DataRow row, DataRowAction action) { 1364 if (table.UpdatingCurrent(row, action)) { 1365 CheckNullable(row); 1366 CheckMaxLength(row); 1367 } 1368 } 1369 CheckMaxLength()1370 internal bool CheckMaxLength() { 1371 if ((0 <= maxLength) && (null != Table) && (0 < Table.Rows.Count)) { 1372 Debug.Assert(IsStringType, "not a String or SqlString column"); 1373 foreach (DataRow dr in Table.Rows) { 1374 if (dr.HasVersion(DataRowVersion.Current)) { 1375 if (maxLength < GetStringLength(dr.GetCurrentRecordNo())) { 1376 return false; 1377 } 1378 } 1379 } 1380 } 1381 return true; 1382 } 1383 CheckMaxLength(DataRow dr)1384 internal void CheckMaxLength(DataRow dr) { 1385 if (0 <= maxLength) { 1386 Debug.Assert(IsStringType, "not a String or SqlString column"); 1387 if (maxLength < GetStringLength(dr.GetDefaultRecord())) { 1388 throw ExceptionBuilder.LongerThanMaxLength(this); 1389 } 1390 } 1391 } 1392 CheckNotAllowNull()1393 internal protected void CheckNotAllowNull() { 1394 if (_storage == null) 1395 return; 1396 1397 if (sortIndex != null) { 1398 if (sortIndex.IsKeyInIndex(_storage._nullValue)) {// here we do use strong typed NULL for Sql types 1399 throw ExceptionBuilder.NullKeyValues(ColumnName); 1400 } 1401 } 1402 else { // since we do not have index, we so sequential search 1403 foreach (DataRow dr in this.table.Rows) { 1404 if (dr.RowState == DataRowState.Deleted) 1405 continue; 1406 if (!implementsINullable) { 1407 if (dr[this] == DBNull.Value) { 1408 throw ExceptionBuilder.NullKeyValues(ColumnName); 1409 } 1410 } 1411 else { 1412 if (DataStorage.IsObjectNull(dr[this])) { 1413 throw ExceptionBuilder.NullKeyValues(ColumnName); 1414 } 1415 } 1416 } 1417 } 1418 } 1419 CheckNullable(DataRow row)1420 internal void CheckNullable(DataRow row) { 1421 if (!AllowDBNull) { 1422 Debug.Assert(null != _storage, "no storage"); 1423 if (_storage.IsNull(row.GetDefaultRecord())) { 1424 throw ExceptionBuilder.NullValues(ColumnName); 1425 } 1426 } 1427 } 1428 CheckUnique()1429 protected void CheckUnique() { 1430 if (!SortIndex.CheckUnique()) { 1431 // Throws an exception and the name of any column if its Unique property set to 1432 // True and non-unique values are found in the column. 1433 throw ExceptionBuilder.NonUniqueValues(ColumnName); 1434 } 1435 } 1436 Compare(int record1, int record2)1437 internal int Compare(int record1, int record2) { 1438 Debug.Assert(null != _storage, "null storage"); 1439 return _storage.Compare(record1, record2); 1440 } 1441 CompareValueTo(int record1, object value, bool checkType)1442 internal bool CompareValueTo(int record1, object value, bool checkType) { 1443 // this method is used to make sure value and exact type match. 1444 int valuesMatch = CompareValueTo(record1, value); 1445 // if values match according to storage, do extra checks for exact compare 1446 if (valuesMatch == 0) { 1447 Type leftType = value.GetType(); 1448 Type rightType = _storage.Get(record1).GetType(); 1449 // if strings, then do exact character by character check 1450 if (leftType == typeof(System.String) && rightType == typeof(System.String)) { 1451 return String.CompareOrdinal((string)_storage.Get(record1), (string)value) == 0 ? true : false; 1452 } 1453 // make sure same type 1454 else if (leftType == rightType) { 1455 return true; 1456 } 1457 } 1458 return false; 1459 } 1460 CompareValueTo(int record1, object value)1461 internal int CompareValueTo(int record1, object value) { 1462 Debug.Assert(null != _storage, "null storage"); 1463 return _storage.CompareValueTo(record1, value); 1464 } 1465 ConvertValue(object value)1466 internal object ConvertValue(object value) { 1467 Debug.Assert(null != _storage, "null storage"); 1468 return _storage.ConvertValue(value); 1469 } 1470 Copy(int srcRecordNo, int dstRecordNo)1471 internal void Copy(int srcRecordNo, int dstRecordNo) { 1472 Debug.Assert(null != _storage, "null storage"); 1473 _storage.Copy(srcRecordNo, dstRecordNo); 1474 } 1475 1476 // Prevent inlining so that reflection calls are not moved to caller that may be in a different assembly that may have a different grant set. 1477 [MethodImpl(MethodImplOptions.NoInlining)] Clone()1478 internal DataColumn Clone() { 1479 DataColumn clone = (DataColumn)Activator.CreateInstance(this.GetType()); 1480 // set All properties 1481 // clone.columnMapping = columnMapping; 1482 1483 clone.SimpleType = SimpleType; 1484 1485 clone.allowNull = allowNull; 1486 if (null != this.autoInc) { 1487 clone.autoInc = this.autoInc.Clone(); 1488 } 1489 clone.caption = caption; 1490 clone.ColumnName = ColumnName; 1491 clone._columnUri = _columnUri; 1492 clone._columnPrefix = _columnPrefix; 1493 clone.DataType = DataType; 1494 clone.defaultValue = defaultValue; 1495 clone.defaultValueIsNull = ((defaultValue == DBNull.Value) || (clone.ImplementsINullable && DataStorage.IsObjectSqlNull(defaultValue))) ? true : false; 1496 clone.columnMapping = columnMapping;// clone column Mapping since we dont let MaxLength to be set throu API 1497 // 1498 clone.readOnly = readOnly; 1499 clone.MaxLength = MaxLength; 1500 clone.dttype = dttype; 1501 clone._dateTimeMode = _dateTimeMode; 1502 1503 1504 // so if we have set it, we should continue preserving the information 1505 1506 // ...Extended Properties 1507 if (this.extendedProperties != null) { 1508 foreach (Object key in this.extendedProperties.Keys) { 1509 clone.ExtendedProperties[key] = this.extendedProperties[key]; 1510 } 1511 } 1512 1513 return clone; 1514 } 1515 1516 /// <devdoc> 1517 /// <para>Finds a relation that this column is the sole child of or null.</para> 1518 /// </devdoc> FindParentRelation()1519 internal DataRelation FindParentRelation() { 1520 DataRelation[] parentRelations = new DataRelation[Table.ParentRelations.Count]; 1521 Table.ParentRelations.CopyTo(parentRelations, 0); 1522 1523 for (int i = 0; i < parentRelations.Length; i++) { 1524 DataRelation relation = parentRelations[i]; 1525 DataKey key = relation.ChildKey; 1526 if (key.ColumnsReference.Length == 1 && key.ColumnsReference[0] == this) { 1527 return relation; 1528 } 1529 } 1530 // should we throw an exception? 1531 return null; 1532 } 1533 1534 GetAggregateValue(int[] records, AggregateType kind)1535 internal object GetAggregateValue(int[] records, AggregateType kind) { 1536 if (_storage == null) { 1537 if (kind == AggregateType.Count) 1538 return 0; 1539 else 1540 return DBNull.Value; 1541 } 1542 return _storage.Aggregate(records, kind); 1543 } 1544 GetStringLength(int record)1545 private int GetStringLength(int record) { 1546 Debug.Assert(null != _storage, "no storage"); 1547 return _storage.GetStringLength(record); 1548 } 1549 Init(int record)1550 internal void Init(int record) { 1551 if (AutoIncrement) { 1552 object value = this.autoInc.Current; 1553 this.autoInc.MoveAfter(); 1554 Debug.Assert(null != _storage, "no storage"); 1555 _storage.Set(record, value); 1556 } 1557 else 1558 this[record] = defaultValue; 1559 } 1560 IsAutoIncrementType(Type dataType)1561 internal static bool IsAutoIncrementType(Type dataType) { 1562 return ((dataType == typeof(Int32)) || (dataType == typeof(Int64)) || (dataType == typeof(Int16)) || (dataType == typeof(Decimal)) || (dataType == typeof(System.Numerics.BigInteger)) || 1563 (dataType == typeof(SqlInt32)) || (dataType == typeof(SqlInt64)) || (dataType == typeof(SqlInt16)) || (dataType == typeof(SqlDecimal))); 1564 } 1565 IsColumnMappingValid(StorageType typeCode, MappingType mapping)1566 private bool IsColumnMappingValid(StorageType typeCode, MappingType mapping) { 1567 if ((mapping != MappingType.Element) && DataStorage.IsTypeCustomType(typeCode)) { 1568 return false; 1569 } 1570 return true; 1571 } 1572 1573 internal bool IsCustomType { 1574 get { 1575 if (null != _storage) 1576 return _storage._isCustomDefinedType; 1577 return DataStorage.IsTypeCustomType(DataType); 1578 } 1579 } 1580 IsValueCustomTypeInstance(object value)1581 internal bool IsValueCustomTypeInstance(object value) { 1582 // if instance is not a storage supported type (built in or SQL types) 1583 return (DataStorage.IsTypeCustomType(value.GetType()) && !(value is Type)); 1584 } 1585 1586 internal bool ImplementsIXMLSerializable { 1587 get { 1588 return implementsIXMLSerializable; 1589 } 1590 } 1591 IsNull(int record)1592 internal bool IsNull(int record) { 1593 Debug.Assert(null != _storage, "no storage"); 1594 return _storage.IsNull(record); 1595 } 1596 1597 /// <devdoc> 1598 /// Returns true if this column is a part of a Parent or Child key for a relation. 1599 /// </devdoc> IsInRelation()1600 internal bool IsInRelation() { 1601 DataKey key; 1602 DataRelationCollection rels = table.ParentRelations; 1603 1604 Debug.Assert(rels != null, "Invalid ParentRelations"); 1605 for (int i = 0; i < rels.Count; i++) { 1606 key = rels[i].ChildKey; 1607 Debug.Assert(key.HasValue, "Invalid child key (null)"); 1608 if (key.ContainsColumn(this)) { 1609 return true; 1610 } 1611 } 1612 rels = table.ChildRelations; 1613 Debug.Assert(rels != null, "Invalid ChildRelations"); 1614 for (int i = 0; i < rels.Count; i++) { 1615 key = rels[i].ParentKey; 1616 Debug.Assert(key.HasValue, "Invalid parent key (null)"); 1617 if (key.ContainsColumn(this)) { 1618 return true; 1619 } 1620 } 1621 return false; 1622 } 1623 IsMaxLengthViolated()1624 internal bool IsMaxLengthViolated() { 1625 if (MaxLength < 0) 1626 return true; 1627 1628 bool error = false; 1629 object value; 1630 string errorText = null; 1631 1632 foreach (DataRow dr in Table.Rows) { 1633 if (dr.HasVersion(DataRowVersion.Current)) { 1634 value = dr[this]; 1635 if (!this.isSqlType) { 1636 if (value != null && value != DBNull.Value && ((string)value).Length > MaxLength) { 1637 if (errorText == null) { 1638 errorText = ExceptionBuilder.MaxLengthViolationText(this.ColumnName); 1639 } 1640 dr.RowError = errorText; 1641 dr.SetColumnError(this, errorText); 1642 error = true; 1643 } 1644 } 1645 else { 1646 if (!DataStorage.IsObjectNull(value) && ((SqlString)value).Value.Length > MaxLength) { 1647 if (errorText == null) { 1648 errorText = ExceptionBuilder.MaxLengthViolationText(this.ColumnName); 1649 } 1650 dr.RowError = errorText; 1651 dr.SetColumnError(this, errorText); 1652 error = true; 1653 } 1654 } 1655 } 1656 } 1657 return error; 1658 } 1659 IsNotAllowDBNullViolated()1660 internal bool IsNotAllowDBNullViolated() {// 1661 Index index = this.SortIndex; 1662 DataRow[] rows = index.GetRows(index.FindRecords(DBNull.Value)); 1663 for (int i = 0; i < rows.Length; i++) { 1664 string errorText = ExceptionBuilder.NotAllowDBNullViolationText(this.ColumnName); 1665 rows[i].RowError = errorText; 1666 rows[i].SetColumnError(this, errorText); 1667 } 1668 return (rows.Length > 0); 1669 } 1670 FinishInitInProgress()1671 internal void FinishInitInProgress() { 1672 if (this.Computed) 1673 BindExpression(); 1674 } 1675 OnPropertyChanging(PropertyChangedEventArgs pcevent)1676 protected virtual void OnPropertyChanging(PropertyChangedEventArgs pcevent) { 1677 if (onPropertyChangingDelegate != null) 1678 onPropertyChangingDelegate(this, pcevent); 1679 } 1680 RaisePropertyChanging(string name)1681 protected internal void RaisePropertyChanging(string name) { 1682 OnPropertyChanging(new PropertyChangedEventArgs(name)); 1683 } 1684 InsureStorage()1685 private void InsureStorage() { 1686 if (_storage == null) { 1687 _storage = DataStorage.CreateStorage(this, dataType, _storageType); 1688 } 1689 } 1690 SetCapacity(int capacity)1691 internal void SetCapacity(int capacity) { 1692 InsureStorage(); 1693 _storage.SetCapacity(capacity); 1694 } 1695 ShouldSerializeDefaultValue()1696 private bool ShouldSerializeDefaultValue() { 1697 return (!DefaultValueIsNull); 1698 } 1699 OnSetDataSet()1700 internal void OnSetDataSet() { 1701 } 1702 1703 // Returns the <see cref='System.Data.DataColumn.Expression'/> of the column, if one exists. ToString()1704 public override string ToString() { 1705 if (this.expression == null) 1706 return this.ColumnName; 1707 else 1708 return this.ColumnName + " + " + this.Expression; 1709 1710 } 1711 1712 ConvertXmlToObject(string s)1713 internal object ConvertXmlToObject(string s) { 1714 Debug.Assert(s != null, "Caller is resposible for missing element/attribure case"); 1715 InsureStorage(); 1716 return _storage.ConvertXmlToObject(s); 1717 } 1718 ConvertXmlToObject(XmlReader xmlReader, XmlRootAttribute xmlAttrib)1719 internal object ConvertXmlToObject(XmlReader xmlReader, XmlRootAttribute xmlAttrib) { 1720 InsureStorage(); 1721 return _storage.ConvertXmlToObject(xmlReader, xmlAttrib); 1722 } 1723 1724 ConvertObjectToXml(object value)1725 internal string ConvertObjectToXml(object value) { 1726 Debug.Assert(value != null && (value != DBNull.Value), "Caller is resposible for checking on DBNull"); 1727 InsureStorage(); 1728 return _storage.ConvertObjectToXml(value); 1729 } 1730 ConvertObjectToXml(object value, XmlWriter xmlWriter, XmlRootAttribute xmlAttrib)1731 internal void ConvertObjectToXml(object value, XmlWriter xmlWriter, XmlRootAttribute xmlAttrib) { 1732 Debug.Assert(value != null && (value != DBNull.Value), "Caller is resposible for checking on DBNull"); 1733 InsureStorage(); 1734 _storage.ConvertObjectToXml(value, xmlWriter, xmlAttrib); 1735 } 1736 GetEmptyColumnStore(int recordCount)1737 internal object GetEmptyColumnStore(int recordCount) { 1738 InsureStorage(); 1739 return _storage.GetEmptyStorageInternal(recordCount); 1740 } 1741 CopyValueIntoStore(int record, object store, BitArray nullbits, int storeIndex)1742 internal void CopyValueIntoStore(int record, object store, BitArray nullbits, int storeIndex) { 1743 Debug.Assert(null != _storage, "no storage"); 1744 _storage.CopyValueInternal(record, store, nullbits, storeIndex); 1745 } 1746 SetStorage(object store, BitArray nullbits)1747 internal void SetStorage(object store, BitArray nullbits) { 1748 InsureStorage(); 1749 _storage.SetStorageInternal(store, nullbits); 1750 } 1751 AddDependentColumn(DataColumn expressionColumn)1752 internal void AddDependentColumn(DataColumn expressionColumn) { 1753 if (dependentColumns == null) { 1754 dependentColumns = new List<DataColumn>(); 1755 } 1756 Debug.Assert(!dependentColumns.Contains(expressionColumn), "duplicate column - expected to be unique"); 1757 dependentColumns.Add(expressionColumn); 1758 this.table.AddDependentColumn(expressionColumn); 1759 } 1760 RemoveDependentColumn(DataColumn expressionColumn)1761 internal void RemoveDependentColumn(DataColumn expressionColumn) { 1762 if (dependentColumns != null && dependentColumns.Contains(expressionColumn)) { 1763 dependentColumns.Remove(expressionColumn); 1764 } 1765 this.table.RemoveDependentColumn(expressionColumn); 1766 } 1767 HandleDependentColumnList(DataExpression oldExpression, DataExpression newExpression)1768 internal void HandleDependentColumnList(DataExpression oldExpression, DataExpression newExpression) { 1769 DataColumn[] dependency; 1770 // remove this column from the dependentColumn list of the columns this column depends on. 1771 if (oldExpression != null) { 1772 dependency = oldExpression.GetDependency(); 1773 foreach (DataColumn col in dependency) { 1774 Debug.Assert(null != col, "null datacolumn in expression dependencies"); 1775 col.RemoveDependentColumn(this); 1776 if (col.table != this.table) { 1777 this.table.RemoveDependentColumn(this); 1778 } 1779 } 1780 this.table.RemoveDependentColumn(this); 1781 } 1782 1783 if (newExpression != null) { 1784 // get the list of columns that this expression depends on 1785 dependency = newExpression.GetDependency(); 1786 // add this column to dependent column list of each column this column depends on 1787 foreach (DataColumn col in dependency) { 1788 col.AddDependentColumn(this); 1789 if (col.table != this.table) { 1790 this.table.AddDependentColumn(this); 1791 } 1792 } 1793 this.table.AddDependentColumn(this); 1794 } 1795 } 1796 } 1797 1798 internal abstract class AutoIncrementValue { 1799 private bool auto; 1800 1801 internal bool Auto { 1802 get { return this.auto; } 1803 set { this.auto = value; } 1804 } 1805 internal abstract object Current { get; set; } 1806 internal abstract long Seed { get; set; } 1807 internal abstract long Step { get; set; } 1808 internal abstract Type DataType { get; } 1809 SetCurrent(object value, IFormatProvider formatProvider)1810 internal abstract void SetCurrent(object value, IFormatProvider formatProvider); SetCurrentAndIncrement(object value)1811 internal abstract void SetCurrentAndIncrement(object value); MoveAfter()1812 internal abstract void MoveAfter(); 1813 Clone()1814 internal AutoIncrementValue Clone() { 1815 AutoIncrementValue clone = (this is AutoIncrementInt64) ? (AutoIncrementValue)new AutoIncrementInt64() : (AutoIncrementValue)new AutoIncrementBigInteger(); 1816 clone.Auto = this.Auto; 1817 clone.Seed = this.Seed; 1818 clone.Step = this.Step; 1819 clone.Current = this.Current; 1820 return clone; 1821 } 1822 } 1823 1824 /// <summary>the auto stepped value with Int64 representation</summary> 1825 /// <remarks>use unchecked behavior for Dev10 Bug 568510</remarks> 1826 internal sealed class AutoIncrementInt64 : AutoIncrementValue { 1827 /// <summary>the last returned auto incremented value</summary> 1828 private System.Int64 current; 1829 1830 /// <summary>the initial value use to set current</summary> 1831 private System.Int64 seed; 1832 1833 /// <summary>the value by which to offset the next value</summary> 1834 private System.Int64 step = 1; 1835 1836 /// <summary>Gets and sets the current auto incremented value to use</summary> 1837 internal override object Current { 1838 get { return this.current; } 1839 set { this.current = (Int64)value; } 1840 } 1841 1842 internal override Type DataType { get { return typeof(System.Int64); } } 1843 1844 /// <summary>Get and sets the initial seed value.</summary> 1845 internal override long Seed { 1846 get { return this.seed; } 1847 set { 1848 if ((this.current == this.seed) || this.BoundaryCheck(value)) { 1849 this.current = value; 1850 } 1851 this.seed = value; 1852 } 1853 } 1854 1855 /// <summary>Get and sets the stepping value.</summary> 1856 /// <exception cref="ArugmentException">if value is 0</exception> 1857 internal override long Step { 1858 get { return this.step; } 1859 set { 1860 if (0 == value) { 1861 throw ExceptionBuilder.AutoIncrementSeed(); 1862 } 1863 if (this.step != value) { 1864 if (this.current != this.Seed) { 1865 this.current = unchecked(this.current - this.step + value); 1866 } 1867 this.step = value; 1868 } 1869 } 1870 } 1871 MoveAfter()1872 internal override void MoveAfter() { 1873 this.current = unchecked(this.current + this.step); 1874 } 1875 SetCurrent(object value, IFormatProvider formatProvider)1876 internal override void SetCurrent(object value, IFormatProvider formatProvider) { 1877 this.current = Convert.ToInt64(value, formatProvider); 1878 } 1879 SetCurrentAndIncrement(object value)1880 internal override void SetCurrentAndIncrement(object value) { 1881 Debug.Assert(null != value && DataColumn.IsAutoIncrementType(value.GetType()) && !(value is System.Numerics.BigInteger), "unexpected value for autoincrement"); 1882 System.Int64 v = (Int64)SqlConvert.ChangeType2(value, StorageType.Int64, typeof(Int64), CultureInfo.InvariantCulture); 1883 if (this.BoundaryCheck(v)) { 1884 this.current = unchecked(v + this.step); 1885 } 1886 } 1887 BoundaryCheck(System.Numerics.BigInteger value)1888 private bool BoundaryCheck(System.Numerics.BigInteger value) { 1889 return (((this.step < 0) && (value <= this.current)) || ((0 < this.step) && (this.current <= value))); 1890 } 1891 } 1892 1893 /// <summary>the auto stepped value with BigInteger representation</summary> 1894 internal sealed class AutoIncrementBigInteger : AutoIncrementValue { 1895 /// <summary>the current auto incremented value to use</summary> 1896 private System.Numerics.BigInteger current; 1897 1898 /// <summary>the initial value use to set current</summary> 1899 private System.Int64 seed; 1900 1901 /// <summary>the value by which to offset the next value</summary> 1902 private System.Numerics.BigInteger step = 1; 1903 1904 /// <summary>Gets and sets the current auto incremented value to use</summary> 1905 internal override object Current { 1906 get { return this.current; } 1907 set { this.current = (System.Numerics.BigInteger)value; } 1908 } 1909 1910 internal override Type DataType { get { return typeof(System.Numerics.BigInteger); } } 1911 1912 /// <summary>Get and sets the initial seed value.</summary> 1913 internal override long Seed { 1914 get { return this.seed; } 1915 set { 1916 if ((this.current == this.seed) || this.BoundaryCheck(value)) { 1917 this.current = value; 1918 } 1919 this.seed = value; 1920 } 1921 } 1922 1923 /// <summary>Get and sets the stepping value.</summary> 1924 /// <exception cref="ArugmentException">if value is 0</exception> 1925 internal override long Step { 1926 get { return (long)this.step; } 1927 set { 1928 if (0 == value) { 1929 throw ExceptionBuilder.AutoIncrementSeed(); 1930 } 1931 if (this.step != value) { 1932 if (this.current != this.Seed) { 1933 this.current = checked(this.current - this.step + value); 1934 } 1935 this.step = value; 1936 } 1937 } 1938 } 1939 MoveAfter()1940 internal override void MoveAfter() { 1941 this.current = checked(this.current + this.step); 1942 } 1943 SetCurrent(object value, IFormatProvider formatProvider)1944 internal override void SetCurrent(object value, IFormatProvider formatProvider) { 1945 this.current = BigIntegerStorage.ConvertToBigInteger(value, formatProvider); 1946 } 1947 SetCurrentAndIncrement(object value)1948 internal override void SetCurrentAndIncrement(object value) { 1949 System.Numerics.BigInteger v = (System.Numerics.BigInteger)value; 1950 if (this.BoundaryCheck(v)) { 1951 this.current = v + this.step; 1952 } 1953 } 1954 BoundaryCheck(System.Numerics.BigInteger value)1955 private bool BoundaryCheck(System.Numerics.BigInteger value) { 1956 return (((this.step < 0) && (value <= this.current)) || ((0 < this.step) && (this.current <= value))); 1957 } 1958 } 1959 } 1960