536cd135cc
Former-commit-id: 5624ac747d633e885131e8349322922b6a59baaa
281 lines
12 KiB
C#
281 lines
12 KiB
C#
//------------------------------------------------------------------------------
|
|
// <copyright file="DataTableExtenstions.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
// <owner current="true" primary="true">Microsoft</owner>
|
|
// <owner current="true" primary="false">Microsoft</owner>
|
|
//------------------------------------------------------------------------------
|
|
|
|
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
|
|
{
|
|
|
|
/// <summary>
|
|
/// This static class defines the DataTable extension methods.
|
|
/// </summary>
|
|
public static class DataTableExtensions
|
|
{
|
|
|
|
/// <summary>
|
|
/// This method returns a IEnumerable of Datarows.
|
|
/// </summary>
|
|
/// <param name="source">
|
|
/// The source DataTable to make enumerable.
|
|
/// </param>
|
|
/// <returns>
|
|
/// IEnumerable of datarows.
|
|
/// </returns>
|
|
public static EnumerableRowCollection<DataRow> AsEnumerable(this DataTable source)
|
|
{
|
|
DataSetUtil.CheckArgumentNull(source, "source");
|
|
return new EnumerableRowCollection<DataRow>(source);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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).
|
|
/// </summary>
|
|
/// <param name="source">
|
|
/// The input sequence of DataRows
|
|
/// </param>
|
|
/// <returns>
|
|
/// DataTable containing copies of the source DataRows.
|
|
/// Properties for the DataTable table will be taken from first DataRow in the source.
|
|
/// </returns>
|
|
/// <exception cref="ArgumentNullException">if source is null</exception>
|
|
/// <exception cref="InvalidOperationException">if source is empty</exception>
|
|
public static DataTable CopyToDataTable<T>(this IEnumerable<T> source)
|
|
where T : DataRow
|
|
{
|
|
DataSetUtil.CheckArgumentNull(source, "source");
|
|
return LoadTableFromEnumerable(source, null, null, null);
|
|
}
|
|
|
|
/// <summary>
|
|
/// delegates to other CopyToDataTable overload with a null FillErrorEventHandler.
|
|
/// </summary>
|
|
public static void CopyToDataTable<T>(this IEnumerable<T> source, DataTable table, LoadOption options)
|
|
where T : DataRow
|
|
{
|
|
DataSetUtil.CheckArgumentNull(source, "source");
|
|
DataSetUtil.CheckArgumentNull(table, "table");
|
|
LoadTableFromEnumerable(source, table, options, null);
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// 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).
|
|
/// </summary>
|
|
/// <param name="source">
|
|
/// 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.
|
|
/// </param>
|
|
/// <param name="table">
|
|
/// The target DataTable to load.
|
|
/// </param>
|
|
/// <param name="options">
|
|
/// The target DataTable to load.
|
|
/// </param>
|
|
/// <param name="errorHandler">
|
|
/// 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
|
|
/// </param>
|
|
/// <returns>
|
|
/// DataTable containing copies of the source DataRows.
|
|
/// </returns>
|
|
/// <exception cref="ArgumentNullException">if source is null</exception>
|
|
/// <exception cref="ArgumentNullException">if table is null</exception>
|
|
/// <exception cref="InvalidOperationException">if source DataRow is in Deleted or Detached state</exception>
|
|
public static void CopyToDataTable<T>(this IEnumerable<T> 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<T>(IEnumerable<T> 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<T> 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<DataRow> 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
|
|
|
|
/// <summary>
|
|
/// Creates a LinkDataView of DataRow over the input table.
|
|
/// </summary>
|
|
/// <param name="table">DataTable that the view is over.</param>
|
|
/// <returns>An instance of LinkDataView.</returns>
|
|
public static DataView AsDataView(this DataTable table)
|
|
{
|
|
DataSetUtil.CheckArgumentNull<DataTable>(table, "table");
|
|
return new LinqDataView(table, null);
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Creates a LinqDataView from EnumerableDataTable
|
|
/// </summary>
|
|
/// <typeparam name="T">Type of the row in the table. Must inherit from DataRow</typeparam>
|
|
/// <param name="source">The enumerable-datatable over which view must be created.</param>
|
|
/// <returns>Generated LinkDataView of type T</returns>
|
|
public static DataView AsDataView<T>(this EnumerableRowCollection<T> source) where T : DataRow
|
|
{
|
|
DataSetUtil.CheckArgumentNull<EnumerableRowCollection<T>>(source, "source");
|
|
return source.GetLinqDataView();
|
|
}
|
|
|
|
#endregion LinqDataView
|
|
|
|
}
|
|
}
|