2 // System.Data.SqlClient.SqlDataAdapter.cs
5 // Rodrigo Moya (rodrigo@ximian.com)
6 // Daniel Morgan (danmorg@sc.rr.com)
7 // Tim Coleman (tim@timcoleman.com)
8 // Veerapuram Varadhan (vvaradhan@novell.com)
10 // (C) Ximian, Inc 2002
11 // Copyright (C) 2002 Tim Coleman
13 // Copyright (C) 2004, 2009 Novell, Inc (http://www.novell.com)
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 using System
.Collections
;
37 using System
.ComponentModel
;
39 using System
.Data
.Common
;
41 namespace System
.Data
.SqlClient
{
42 [DefaultEvent ("RowUpdated")]
43 [DesignerAttribute ("Microsoft.VSDesigner.Data.VS.SqlDataAdapterDesigner, "+ Consts
.AssemblyMicrosoft_VSDesigner
, "System.ComponentModel.Design.IDesigner")]
44 [ToolboxItemAttribute ("Microsoft.VSDesigner.Data.VS.SqlDataAdapterToolboxItem, "+ Consts
.AssemblyMicrosoft_VSDesigner
)]
46 public sealed class SqlDataAdapter
: DbDataAdapter
, IDbDataAdapter
, IDataAdapter
, ICloneable
49 #region Copy from old DataColumn
50 internal static bool CanAutoIncrement (Type type
)
52 switch (Type
.GetTypeCode (type
)) {
56 case TypeCode
.Decimal
:
64 #region Copy from old DataAdapter
66 private const string DefaultSourceColumnName
= "Column";
68 internal FillErrorEventArgs
CreateFillErrorEvent (DataTable dataTable
, object[] values
, Exception e
)
70 FillErrorEventArgs args
= new FillErrorEventArgs (dataTable
, values
);
72 args
.Continue
= false;
76 internal void OnFillErrorInternal (FillErrorEventArgs
value)
81 // this method builds the schema for a given datatable. it returns a int array with
82 // "array[ordinal of datatable column] == index of source column in data reader".
83 // each column in the datatable has a mapping to a specific column in the datareader,
84 // the int array represents this match.
85 internal int[] BuildSchema (IDataReader reader
, DataTable table
, SchemaType schemaType
)
87 return BuildSchema (reader
, table
, schemaType
, MissingSchemaAction
,
88 MissingMappingAction
, TableMappings
);
92 /// Creates or Modifies the schema of the given DataTable based on the schema of
93 /// the reader and the arguments passed.
95 internal static int[] BuildSchema (IDataReader reader
, DataTable table
,
96 SchemaType schemaType
,
97 MissingSchemaAction missingSchAction
,
98 MissingMappingAction missingMapAction
,
99 DataTableMappingCollection dtMapping
103 // FIXME : this fails if query has fewer columns than a table
104 int[] mapping
= new int[table
.Columns
.Count
]; // mapping the reader indexes to the datatable indexes
106 for(int i
=0; i
< mapping
.Length
; i
++) {
110 ArrayList primaryKey
= new ArrayList ();
111 ArrayList sourceColumns
= new ArrayList ();
112 bool createPrimaryKey
= true;
114 DataTable schemaTable
= reader
.GetSchemaTable ();
116 DataColumn ColumnNameCol
= schemaTable
.Columns
["ColumnName"];
117 DataColumn DataTypeCol
= schemaTable
.Columns
["DataType"];
118 DataColumn IsAutoIncrementCol
= schemaTable
.Columns
["IsAutoIncrement"];
119 DataColumn AllowDBNullCol
= schemaTable
.Columns
["AllowDBNull"];
120 DataColumn IsReadOnlyCol
= schemaTable
.Columns
["IsReadOnly"];
121 DataColumn IsKeyCol
= schemaTable
.Columns
["IsKey"];
122 DataColumn IsUniqueCol
= schemaTable
.Columns
["IsUnique"];
123 DataColumn ColumnSizeCol
= schemaTable
.Columns
["ColumnSize"];
125 foreach (DataRow schemaRow
in schemaTable
.Rows
) {
126 // generate a unique column name in the source table.
127 string sourceColumnName
;
128 string realSourceColumnName
;
129 if (ColumnNameCol
== null || schemaRow
.IsNull(ColumnNameCol
) ||
130 (string)schemaRow
[ColumnNameCol
] == String
.Empty
) {
131 sourceColumnName
= DefaultSourceColumnName
;
132 realSourceColumnName
= DefaultSourceColumnName
+ "1";
134 sourceColumnName
= (string) schemaRow
[ColumnNameCol
];
135 realSourceColumnName
= sourceColumnName
;
138 for (int i
= 1; sourceColumns
.Contains (realSourceColumnName
); i
+= 1)
139 realSourceColumnName
= String
.Format ("{0}{1}", sourceColumnName
, i
);
140 sourceColumns
.Add(realSourceColumnName
);
142 // generate DataSetColumnName from DataTableMapping, if any
143 DataTableMapping tableMapping
= null;
145 //FIXME : The sourcetable name shud get passed as a parameter..
146 int index
= dtMapping
.IndexOfDataSetTable (table
.TableName
);
147 string srcTable
= (index
!= -1 ? dtMapping
[index
].SourceTable
: table
.TableName
);
148 tableMapping
= DataTableMappingCollection
.GetTableMappingBySchemaAction (dtMapping
, ADP
.IsEmpty (srcTable
) ? " " : srcTable
, table
.TableName
, missingMapAction
);
149 if (tableMapping
!= null) {
150 table
.TableName
= tableMapping
.DataSetTable
;
151 // check to see if the column mapping exists
152 DataColumnMapping columnMapping
= DataColumnMappingCollection
.GetColumnMappingBySchemaAction(tableMapping
.ColumnMappings
, realSourceColumnName
, missingMapAction
);
153 if (columnMapping
!= null) {
154 Type columnType
= schemaRow
[DataTypeCol
] as Type
;
155 DataColumn col
= columnType
!= null ? columnMapping
.GetDataColumnBySchemaAction(
158 missingSchAction
) : null;
161 // if the column is not in the table - add it.
162 if (table
.Columns
.IndexOf(col
) == -1) {
163 if (missingSchAction
== MissingSchemaAction
.Add
164 || missingSchAction
== MissingSchemaAction
.AddWithKey
)
165 table
.Columns
.Add(col
);
167 int[] tmp
= new int[mapping
.Length
+ 1];
168 Array
.Copy(mapping
,0,tmp
,0,col
.Ordinal
);
169 Array
.Copy(mapping
,col
.Ordinal
,tmp
,col
.Ordinal
+ 1,mapping
.Length
- col
.Ordinal
);
173 if (missingSchAction
== MissingSchemaAction
.AddWithKey
) {
174 object value = (AllowDBNullCol
!= null) ? schemaRow
[AllowDBNullCol
] : null;
175 bool allowDBNull
= value is bool ? (bool)value : true;
177 value = (IsKeyCol
!= null) ? schemaRow
[IsKeyCol
] : null;
178 bool isKey
= value is bool ? (bool)value : false;
180 value = (IsAutoIncrementCol
!= null) ? schemaRow
[IsAutoIncrementCol
] : null;
181 bool isAutoIncrement
= value is bool ? (bool)value : false;
183 value = (IsReadOnlyCol
!= null) ? schemaRow
[IsReadOnlyCol
] : null;
184 bool isReadOnly
= value is bool ? (bool)value : false;
186 value = (IsUniqueCol
!= null) ? schemaRow
[IsUniqueCol
] : null;
187 bool isUnique
= value is bool ? (bool)value : false;
189 col
.AllowDBNull
= allowDBNull
;
190 // fill woth key info
191 if (isAutoIncrement
&& CanAutoIncrement(columnType
)) {
192 col
.AutoIncrement
= true;
194 col
.AllowDBNull
= false;
197 if (columnType
== DbTypes
.TypeOfString
) {
198 col
.MaxLength
= (ColumnSizeCol
!= null) ? (int)schemaRow
[ColumnSizeCol
] : 0;
204 if (!allowDBNull
&& (!isReadOnly
|| isKey
))
205 col
.AllowDBNull
= false;
206 if (isUnique
&& !isKey
&& !columnType
.IsArray
) {
209 col
.AllowDBNull
= false;
212 // This might not be set by all DataProviders
213 bool isHidden
= false;
214 if (schemaTable
.Columns
.Contains ("IsHidden")) {
215 value = schemaRow
["IsHidden"];
216 isHidden
= ((value is bool) ? (bool)value : false);
219 if (isKey
&& !isHidden
) {
220 primaryKey
.Add (col
);
222 createPrimaryKey
= false;
225 // add the ordinal of the column as a key and the index of the column in the datareader as a value.
226 mapping
[col
.Ordinal
] = readerIndex
++;
231 if (primaryKey
.Count
> 0) {
232 DataColumn
[] colKey
= (DataColumn
[])(primaryKey
.ToArray(typeof (DataColumn
)));
233 if (createPrimaryKey
)
234 table
.PrimaryKey
= colKey
;
236 UniqueConstraint uConstraint
= new UniqueConstraint(colKey
);
237 for (int i
= 0; i
< table
.Constraints
.Count
; i
++) {
238 if (table
.Constraints
[i
].Equals(uConstraint
)) {
244 if (uConstraint
!= null)
245 table
.Constraints
.Add(uConstraint
);
251 internal int FillInternal (DataTable dataTable
, IDataReader dataReader
)
253 if (dataReader
.FieldCount
== 0) {
261 string tableName
= SetupSchema (SchemaType
.Mapped
, dataTable
.TableName
);
262 if (tableName
!= null) {
263 dataTable
.TableName
= tableName
;
264 FillTable (dataTable
, dataReader
, 0, 0, ref count
);
273 internal bool FillTable (DataTable dataTable
, IDataReader dataReader
, int startRecord
, int maxRecords
, ref int counter
)
275 if (dataReader
.FieldCount
== 0)
278 int counterStart
= counter
;
280 int[] mapping
= BuildSchema (dataReader
, dataTable
, SchemaType
.Mapped
);
282 int [] sortedMapping
= new int [mapping
.Length
];
283 int length
= sortedMapping
.Length
;
284 for (int i
= 0; i
< sortedMapping
.Length
; i
++) {
285 if (mapping
[i
] >= 0)
286 sortedMapping
[mapping
[i
]] = i
;
288 sortedMapping
[--length
] = i
;
291 for (int i
= 0; i
< startRecord
; i
++) {
295 dataTable
.BeginLoadData ();
296 object [] values
= new object [length
];
297 while (dataReader
.Read () && (maxRecords
== 0 || (counter
- counterStart
) < maxRecords
)) {
299 for (int iColumn
= 0; iColumn
< values
.Length
; iColumn
++)
300 values
[iColumn
] = dataReader
[iColumn
];
301 dataTable
.LoadDataRow (values
, AcceptChangesDuringFill
);
304 catch (Exception e
) {
305 object[] readerArray
= new object [dataReader
.FieldCount
];
306 object[] tableArray
= new object [mapping
.Length
];
307 // we get the values from the datareader
308 dataReader
.GetValues (readerArray
);
309 // copy from datareader columns to table columns according to given mapping
310 for (int i
= 0; i
< mapping
.Length
; i
++) {
311 if (mapping
[i
] >= 0) {
312 tableArray
[i
] = readerArray
[mapping
[i
]];
315 FillErrorEventArgs args
= CreateFillErrorEvent (dataTable
, tableArray
, e
);
316 OnFillErrorInternal (args
);
318 // if args.Continue is not set to true or if a handler is not set, rethrow the error..
323 dataTable
.EndLoadData ();
327 internal string SetupSchema (SchemaType schemaType
, string sourceTableName
)
329 DataTableMapping tableMapping
= null;
331 if (schemaType
== SchemaType
.Mapped
) {
332 tableMapping
= DataTableMappingCollection
.GetTableMappingBySchemaAction (TableMappings
, sourceTableName
, sourceTableName
, MissingMappingAction
);
333 if (tableMapping
!= null)
334 return tableMapping
.DataSetTable
;
337 return sourceTableName
;
348 public SqlDataAdapter () : this ((SqlCommand
) null)
352 public SqlDataAdapter (SqlCommand selectCommand
)
354 SelectCommand
= selectCommand
;
358 public SqlDataAdapter (string selectCommandText
, SqlConnection selectConnection
)
359 : this (new SqlCommand (selectCommandText
, selectConnection
))
363 public SqlDataAdapter (string selectCommandText
, string selectConnectionString
)
364 : this (selectCommandText
, new SqlConnection (selectConnectionString
))
372 [DefaultValue (null)]
373 [EditorAttribute ("Microsoft.VSDesigner.Data.Design.DBCommandEditor, "+ Consts
.AssemblyMicrosoft_VSDesigner
, "System.Drawing.Design.UITypeEditor, "+ Consts
.AssemblySystem_Drawing
)]
374 public new SqlCommand DeleteCommand { get; set; }
376 [DefaultValue (null)]
377 [EditorAttribute ("Microsoft.VSDesigner.Data.Design.DBCommandEditor, "+ Consts
.AssemblyMicrosoft_VSDesigner
, "System.Drawing.Design.UITypeEditor, "+ Consts
.AssemblySystem_Drawing
)]
378 public new SqlCommand InsertCommand { get; set; }
380 [DefaultValue (null)]
381 [EditorAttribute ("Microsoft.VSDesigner.Data.Design.DBCommandEditor, "+ Consts
.AssemblyMicrosoft_VSDesigner
, "System.Drawing.Design.UITypeEditor, "+ Consts
.AssemblySystem_Drawing
)]
382 public new SqlCommand SelectCommand { get; set; }
384 [DefaultValue (null)]
385 [EditorAttribute ("Microsoft.VSDesigner.Data.Design.DBCommandEditor, "+ Consts
.AssemblyMicrosoft_VSDesigner
, "System.Drawing.Design.UITypeEditor, "+ Consts
.AssemblySystem_Drawing
)]
386 public new SqlCommand UpdateCommand { get; set; }
388 IDbCommand IDbDataAdapter
.SelectCommand
{
389 get { return SelectCommand; }
390 set { SelectCommand = (SqlCommand) value; }
393 IDbCommand IDbDataAdapter
.InsertCommand
{
394 get { return InsertCommand; }
395 set { InsertCommand = (SqlCommand) value; }
398 IDbCommand IDbDataAdapter
.UpdateCommand
{
399 get { return UpdateCommand; }
400 set { UpdateCommand = (SqlCommand) value; }
402 IDbCommand IDbDataAdapter
.DeleteCommand
{
403 get { return DeleteCommand; }
404 set { DeleteCommand = (SqlCommand) value; }
407 public override int UpdateBatchSize
{
408 get { return updateBatchSize; }
411 throw new ArgumentOutOfRangeException ("UpdateBatchSize");
412 updateBatchSize
= value;
416 #endregion // Properties
420 protected override RowUpdatedEventArgs
CreateRowUpdatedEvent (DataRow dataRow
, IDbCommand command
, StatementType statementType
, DataTableMapping tableMapping
)
422 return new SqlRowUpdatedEventArgs (dataRow
, command
, statementType
, tableMapping
);
426 protected override RowUpdatingEventArgs
CreateRowUpdatingEvent (DataRow dataRow
, IDbCommand command
, StatementType statementType
, DataTableMapping tableMapping
)
428 return new SqlRowUpdatingEventArgs (dataRow
, command
, statementType
, tableMapping
);
432 protected override void OnRowUpdated (RowUpdatedEventArgs
value)
434 if (RowUpdated
!= null)
435 RowUpdated (this, (SqlRowUpdatedEventArgs
) value);
438 protected override void OnRowUpdating (RowUpdatingEventArgs
value)
440 if (RowUpdating
!= null)
441 RowUpdating (this, (SqlRowUpdatingEventArgs
) value);
445 object ICloneable
.Clone()
447 throw new NotImplementedException ();
450 // All the batch methods, should be implemented, if supported,
451 // by individual providers
454 protected override int AddToBatch (IDbCommand command
)
456 throw new NotImplementedException ();
460 protected override void ClearBatch ()
462 throw new NotImplementedException ();
466 protected override int ExecuteBatch ()
468 throw new NotImplementedException ();
472 protected override IDataParameter
GetBatchedParameter (int commandIdentifier
, int parameterIndex
)
474 throw new NotImplementedException ();
478 protected override void InitializeBatching ()
480 throw new NotImplementedException ();
484 protected override void TerminateBatching ()
486 throw new NotImplementedException ();
488 #endregion // Methods
490 #region Events and Delegates
492 public event SqlRowUpdatedEventHandler RowUpdated
;
494 public event SqlRowUpdatingEventHandler RowUpdating
;
496 #endregion // Events and Delegates