//------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// Microsoft
// Microsoft
//------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.Linq.Expressions;
using System.Globalization;
using System.Diagnostics;
using System.Data.DataSetExtensions;
namespace System.Data
{
///
/// This static class defines the DataTable extension methods.
///
public static class DataTableExtensions
{
///
/// This method returns a IEnumerable of Datarows.
///
///
/// The source DataTable to make enumerable.
///
///
/// IEnumerable of datarows.
///
public static EnumerableRowCollection AsEnumerable(this DataTable source)
{
DataSetUtil.CheckArgumentNull(source, "source");
return new EnumerableRowCollection(source);
}
///
/// This method takes an input sequence of DataRows and produces a DataTable object
/// with copies of the source rows.
/// Also note that this will cause the rest of the query to execute at this point in time
/// (e.g. there is no more delayed execution after this sequence operator).
///
///
/// The input sequence of DataRows
///
///
/// DataTable containing copies of the source DataRows.
/// Properties for the DataTable table will be taken from first DataRow in the source.
///
/// if source is null
/// if source is empty
public static DataTable CopyToDataTable(this IEnumerable source)
where T : DataRow
{
DataSetUtil.CheckArgumentNull(source, "source");
return LoadTableFromEnumerable(source, null, null, null);
}
///
/// delegates to other CopyToDataTable overload with a null FillErrorEventHandler.
///
public static void CopyToDataTable(this IEnumerable source, DataTable table, LoadOption options)
where T : DataRow
{
DataSetUtil.CheckArgumentNull(source, "source");
DataSetUtil.CheckArgumentNull(table, "table");
LoadTableFromEnumerable(source, table, options, null);
}
///
/// This method takes an input sequence of DataRows and produces a DataTable object
/// with copies of the source rows.
/// Also note that this will cause the rest of the query to execute at this point in time
/// (e.g. there is no more delayed execution after this sequence operator).
///
///
/// The input sequence of DataRows
///
/// CopyToDataTable uses DataRowVersion.Default when retrieving values from source DataRow
/// which will include proposed values for DataRow being edited.
///
/// Null DataRow in the sequence are skipped.
///
///
/// The target DataTable to load.
///
///
/// The target DataTable to load.
///
///
/// Error handler for recoverable errors.
/// Recoverable errors include:
/// A source DataRow is in the deleted or detached state state.
/// DataTable.LoadDataRow threw an exception, i.e. wrong # of columns in source row
/// Unrecoverable errors include:
/// exceptions from IEnumerator, DataTable.BeginLoadData or DataTable.EndLoadData
///
///
/// DataTable containing copies of the source DataRows.
///
/// if source is null
/// if table is null
/// if source DataRow is in Deleted or Detached state
public static void CopyToDataTable(this IEnumerable source, DataTable table, LoadOption options, FillErrorEventHandler errorHandler)
where T : DataRow
{
DataSetUtil.CheckArgumentNull(source, "source");
DataSetUtil.CheckArgumentNull(table, "table");
LoadTableFromEnumerable(source, table, options, errorHandler);
}
private static DataTable LoadTableFromEnumerable(IEnumerable source, DataTable table, LoadOption? options, FillErrorEventHandler errorHandler)
where T : DataRow
{
if (options.HasValue) {
switch(options.Value) {
case LoadOption.OverwriteChanges:
case LoadOption.PreserveChanges:
case LoadOption.Upsert:
break;
default:
throw DataSetUtil.InvalidLoadOption(options.Value);
}
}
using (IEnumerator rows = source.GetEnumerator())
{
// need to get first row to create table
if (!rows.MoveNext())
{
if (table == null)
{
throw DataSetUtil.InvalidOperation(Strings.DataSetLinq_EmptyDataRowSource);
}
else
{
return table;
}
}
DataRow current;
if (table == null)
{
current = rows.Current;
if (current == null)
{
throw DataSetUtil.InvalidOperation(Strings.DataSetLinq_NullDataRow);
}
table = new DataTable();
table.Locale = CultureInfo.CurrentCulture;
// We do not copy the same properties that DataView.ToTable does.
// If user needs that functionality, use other CopyToDataTable overloads.
// The reasoning being, the IEnumerator can be sourced from
// different DataTable, so we just use the "Default" instead of resolving the difference.
foreach (DataColumn column in current.Table.Columns)
{
table.Columns.Add(column.ColumnName, column.DataType);
}
}
table.BeginLoadData();
try
{
do
{
current = rows.Current;
if (current == null)
{
continue;
}
object[] values = null;
try
{ // 'recoverable' error block
switch(current.RowState)
{
case DataRowState.Detached:
if (!current.HasVersion(DataRowVersion.Proposed))
{
throw DataSetUtil.InvalidOperation(Strings.DataSetLinq_CannotLoadDetachedRow);
}
goto case DataRowState.Added;
case DataRowState.Unchanged:
case DataRowState.Added:
case DataRowState.Modified:
values = current.ItemArray;
if (options.HasValue)
{
table.LoadDataRow(values, options.Value);
}
else
{
table.LoadDataRow(values, true);
}
break;
case DataRowState.Deleted:
throw DataSetUtil.InvalidOperation(Strings.DataSetLinq_CannotLoadDeletedRow);
default:
throw DataSetUtil.InvalidDataRowState(current.RowState);
}
}
catch (Exception e)
{
if (!DataSetUtil.IsCatchableExceptionType(e))
{
throw;
}
FillErrorEventArgs fillError = null;
if (null != errorHandler)
{
fillError = new FillErrorEventArgs(table, values);
fillError.Errors = e;
errorHandler.Invoke(rows, fillError);
}
if (null == fillError) {
throw;
}
else if (!fillError.Continue)
{
if (Object.ReferenceEquals(fillError.Errors ?? e, e))
{ // if user didn't change exception to throw (or set it to null)
throw;
}
else
{ // user may have changed exception to throw in handler
throw fillError.Errors;
}
}
}
} while (rows.MoveNext());
}
finally
{
table.EndLoadData();
}
}
Debug.Assert(null != table, "null DataTable");
return table;
}
#region Methods to Create LinqDataView
///
/// Creates a LinkDataView of DataRow over the input table.
///
/// DataTable that the view is over.
/// An instance of LinkDataView.
public static DataView AsDataView(this DataTable table)
{
DataSetUtil.CheckArgumentNull(table, "table");
return new LinqDataView(table, null);
}
///
/// Creates a LinqDataView from EnumerableDataTable
///
/// Type of the row in the table. Must inherit from DataRow
/// The enumerable-datatable over which view must be created.
/// Generated LinkDataView of type T
public static DataView AsDataView(this EnumerableRowCollection source) where T : DataRow
{
DataSetUtil.CheckArgumentNull>(source, "source");
return source.GetLinqDataView();
}
#endregion LinqDataView
}
}