Imported Upstream version 4.0.0~alpha1

Former-commit-id: 806294f5ded97629b74c85c09952f2a74fe182d9
This commit is contained in:
Jo Shields
2015-04-07 09:35:12 +01:00
parent 283343f570
commit 3c1f479b9d
22469 changed files with 2931443 additions and 869343 deletions

View File

@@ -0,0 +1,246 @@
//------------------------------------------------------------------------------
// <copyright file="DataRowComparer.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
// <owner current="true" primary="false">spather</owner>
//------------------------------------------------------------------------------
using System;
using System.Data;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Data.DataSetExtensions;
namespace System.Data
{
/// <summary>
/// This class implements IEqualityComparer using value based semantics
/// when comparing DataRows.
/// </summary>
public static class DataRowComparer
{
/// <summary>
/// Gets the singleton instance of the data row comparer.
/// </summary>
public static DataRowComparer<DataRow> Default { get { return DataRowComparer<DataRow>.Default; } }
internal static bool AreEqual(object a, object b)
{
if (Object.ReferenceEquals(a, b))
{ // same reference or (null, null) or (DBNull.Value, DBNull.Value)
return true;
}
if (Object.ReferenceEquals(a, null) || Object.ReferenceEquals(a, DBNull.Value) ||
Object.ReferenceEquals(b, null) || Object.ReferenceEquals(b, DBNull.Value))
{ // (null, non-null) or (null, DBNull.Value) or vice versa
return false;
}
return (a.Equals(b) || (a.GetType().IsArray && CompareArray((Array)a, b as Array)));
}
private static bool AreElementEqual(object a, object b)
{
if (Object.ReferenceEquals(a, b))
{ // same reference or (null, null) or (DBNull.Value, DBNull.Value)
return true;
}
if (Object.ReferenceEquals(a, null) || Object.ReferenceEquals(a, DBNull.Value) ||
Object.ReferenceEquals(b, null) || Object.ReferenceEquals(b, DBNull.Value))
{ // (null, non-null) or (null, DBNull.Value) or vice versa
return false;
}
return a.Equals(b);
}
private static bool CompareArray(Array a, Array b)
{
if ((null == b) ||
(1 != a.Rank) ||
(1 != b.Rank) ||
(a.Length != b.Length))
{ // automatically consider array's with Rank>1 not-equal
return false;
}
int index1 = a.GetLowerBound(0);
int index2 = b.GetLowerBound(0);
if (a.GetType() == b.GetType() && (0 == index1) && (0 == index2))
{
switch (Type.GetTypeCode(a.GetType().GetElementType()))
{
case TypeCode.Byte:
return DataRowComparer.CompareEquatableArray<Byte>((Byte[])a, (Byte[])b);
case TypeCode.Int16:
return DataRowComparer.CompareEquatableArray<Int16>((Int16[])a, (Int16[])b);
case TypeCode.Int32:
return DataRowComparer.CompareEquatableArray<Int32>((Int32[])a, (Int32[])b);
case TypeCode.Int64:
return DataRowComparer.CompareEquatableArray<Int64>((Int64[])a, (Int64[])b);
case TypeCode.String:
return DataRowComparer.CompareEquatableArray<String>((String[])a, (String[])b);
}
}
//Compare every element. But don't recurse if we have Array of array.
int length = index1 + a.Length;
for (; index1 < length; ++index1, ++index2)
{
if (!AreElementEqual(a.GetValue(index1), b.GetValue(index2)))
{
return false;
}
}
return true;
}
private static bool CompareEquatableArray<TElem>(TElem[] a, TElem[] b) where TElem : IEquatable<TElem>
{
if (Object.ReferenceEquals(a, b))
{
return true;
}
if (Object.ReferenceEquals(a, null) ||
Object.ReferenceEquals(b, null))
{
return false;
}
if (a.Length != b.Length)
{
return false;
}
for (int i = 0; i < a.Length; ++i)
{
if (!a[i].Equals(b[i]))
{
return false;
}
}
return true;
}
}
/// <summary>
/// This class implements IEqualityComparer using value based semantics
/// when comparing DataRows.
/// </summary>
public sealed class DataRowComparer<TRow> : IEqualityComparer<TRow> where TRow : DataRow
{
/// <summary>
/// Private constructor to prevent initialization outside of Default singleton instance.
/// </summary>
private DataRowComparer() { }
private static DataRowComparer<TRow> _instance = new DataRowComparer<TRow>();
/// <summary>
/// Gets the singleton instance of the data row comparer.
/// </summary>
public static DataRowComparer<TRow> Default { get { return _instance; } }
/// <summary>
/// This method compares to DataRows by doing a column by column value based
/// comparision.
/// </summary>
/// <param name="leftRow">
/// The first input DataRow
/// </param>
/// <param name="rightRow">
/// The second input DataRow
/// </param>
/// <returns>
/// True if rows are equal, false if not.
/// </returns>
public bool Equals(TRow leftRow, TRow rightRow)
{
if (Object.ReferenceEquals(leftRow, rightRow))
{
return true;
}
if (Object.ReferenceEquals(leftRow, null) ||
Object.ReferenceEquals(rightRow, null))
{
return false;
}
if (leftRow.RowState == DataRowState.Deleted || rightRow.RowState == DataRowState.Deleted)
{
throw DataSetUtil.InvalidOperation(Strings.DataSetLinq_CannotCompareDeletedRow);
}
int count = leftRow.Table.Columns.Count;
if (count != rightRow.Table.Columns.Count)
{
return false;
}
for (int i = 0; i < count; ++i)
{
if (!DataRowComparer.AreEqual(leftRow[i], rightRow[i]))
{
return false;
}
}
return true;
}
/// <summary>
/// This mtheod retrieves a hash code for the source row.
/// </summary>
/// <param name="row">
/// The source DataRow
/// </param>
/// <returns>
/// HashCode for row based on values in the row.
/// </returns>
public int GetHashCode(TRow row)
{
DataSetUtil.CheckArgumentNull(row, "row");
if (row.RowState == DataRowState.Deleted)
{
throw DataSetUtil.InvalidOperation(Strings.DataSetLinq_CannotCompareDeletedRow);
}
int hash = 0;
Debug.Assert(row.Table != null);
if (row.Table.Columns.Count > 0)
{
// if the row has at least one column, then use the first column value
object value = row[0];
Type valueType = value.GetType();
if (valueType.IsArray)
{
Array array = value as Array;
if (array.Rank > 1)
{
hash = value.GetHashCode();
}
else if (array.Length > 0)
{
hash = array.GetValue(array.GetLowerBound(0)).GetHashCode();
}
}
else
{
System.ValueType vt = value as System.ValueType;
// have to unbox value types.
if (vt != null)
{
hash = vt.GetHashCode();
}
else
{
hash = value.GetHashCode();
}
}
}
// if table has no columns, the hash code is 0
return hash;
}
}
}

View File

@@ -0,0 +1,243 @@
//------------------------------------------------------------------------------
// <copyright file="DataRowExtenstions.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
// <owner current="true" primary="false">spather</owner>
//------------------------------------------------------------------------------
using System;
using System.Data.DataSetExtensions;
namespace System.Data {
/// <summary>
/// This static class defines the DataRow extension methods.
/// </summary>
public static class DataRowExtensions {
/// <summary>
/// This method provides access to the values in each of the columns in a given row.
/// This method makes casts unnecessary when accessing columns.
/// Additionally, Field supports nullable types and maps automatically between DBNull and
/// Nullable when the generic type is nullable.
/// </summary>
/// <param name="row">
/// The input DataRow
/// </param>
/// <param name="columnName">
/// The input column name specificy which row value to retrieve.
/// </param>
/// <returns>
/// The DataRow value for the column specified.
/// </returns>
public static T Field<T>(this DataRow row, string columnName) {
DataSetUtil.CheckArgumentNull(row, "row");
return UnboxT<T>.Unbox(row[columnName]);
}
/// <summary>
/// This method provides access to the values in each of the columns in a given row.
/// This method makes casts unnecessary when accessing columns.
/// Additionally, Field supports nullable types and maps automatically between DBNull and
/// Nullable when the generic type is nullable.
/// </summary>
/// <param name="row">
/// The input DataRow
/// </param>
/// <param name="column">
/// The input DataColumn specificy which row value to retrieve.
/// </param>
/// <returns>
/// The DataRow value for the column specified.
/// </returns>
public static T Field<T>(this DataRow row, DataColumn column) {
DataSetUtil.CheckArgumentNull(row, "row");
return UnboxT<T>.Unbox(row[column]);
}
/// <summary>
/// This method provides access to the values in each of the columns in a given row.
/// This method makes casts unnecessary when accessing columns.
/// Additionally, Field supports nullable types and maps automatically between DBNull and
/// Nullable when the generic type is nullable.
/// </summary>
/// <param name="row">
/// The input DataRow
/// </param>
/// <param name="columnIndex">
/// The input ordinal specificy which row value to retrieve.
/// </param>
/// <returns>
/// The DataRow value for the column specified.
/// </returns>
public static T Field<T>(this DataRow row, int columnIndex) {
DataSetUtil.CheckArgumentNull(row, "row");
return UnboxT<T>.Unbox(row[columnIndex]);
}
/// <summary>
/// This method provides access to the values in each of the columns in a given row.
/// This method makes casts unnecessary when accessing columns.
/// Additionally, Field supports nullable types and maps automatically between DBNull and
/// Nullable when the generic type is nullable.
/// </summary>
/// <param name="row">
/// The input DataRow
/// </param>
/// <param name="columnIndex">
/// The input ordinal specificy which row value to retrieve.
/// </param>
/// <param name="version">
/// The DataRow version for which row value to retrieve.
/// </param>
/// <returns>
/// The DataRow value for the column specified.
/// </returns>
public static T Field<T>(this DataRow row, int columnIndex, DataRowVersion version) {
DataSetUtil.CheckArgumentNull(row, "row");
return UnboxT<T>.Unbox(row[columnIndex, version]);
}
/// <summary>
/// This method provides access to the values in each of the columns in a given row.
/// This method makes casts unnecessary when accessing columns.
/// Additionally, Field supports nullable types and maps automatically between DBNull and
/// Nullable when the generic type is nullable.
/// </summary>
/// <param name="row">
/// The input DataRow
/// </param>
/// <param name="columnName">
/// The input column name specificy which row value to retrieve.
/// </param>
/// <param name="version">
/// The DataRow version for which row value to retrieve.
/// </param>
/// <returns>
/// The DataRow value for the column specified.
/// </returns>
public static T Field<T>(this DataRow row, string columnName, DataRowVersion version) {
DataSetUtil.CheckArgumentNull(row, "row");
return UnboxT<T>.Unbox(row[columnName, version]);
}
/// <summary>
/// This method provides access to the values in each of the columns in a given row.
/// This method makes casts unnecessary when accessing columns.
/// Additionally, Field supports nullable types and maps automatically between DBNull and
/// Nullable when the generic type is nullable.
/// </summary>
/// <param name="row">
/// The input DataRow
/// </param>
/// <param name="column">
/// The input DataColumn specificy which row value to retrieve.
/// </param>
/// <param name="version">
/// The DataRow version for which row value to retrieve.
/// </param>
/// <returns>
/// The DataRow value for the column specified.
/// </returns>
public static T Field<T>(this DataRow row, DataColumn column, DataRowVersion version) {
DataSetUtil.CheckArgumentNull(row, "row");
return UnboxT<T>.Unbox(row[column, version]);
}
/// <summary>
/// This method sets a new value for the specified column for the DataRow it<69>s called on.
/// </summary>
/// <param name="row">
/// The input DataRow.
/// </param>
/// <param name="columnIndex">
/// The input ordinal specifying which row value to set.
/// </param>
/// <param name="value">
/// The new row value for the specified column.
/// </param>
public static void SetField<T>(this DataRow row, int columnIndex, T value) {
DataSetUtil.CheckArgumentNull(row, "row");
row[columnIndex] = (object)value ?? DBNull.Value;
}
/// <summary>
/// This method sets a new value for the specified column for the DataRow it<69>s called on.
/// </summary>
/// <param name="row">
/// The input DataRow.
/// </param>
/// <param name="columnName">
/// The input column name specificy which row value to retrieve.
/// </param>
/// <param name="value">
/// The new row value for the specified column.
/// </param>
public static void SetField<T>(this DataRow row, string columnName, T value) {
DataSetUtil.CheckArgumentNull(row, "row");
row[columnName] = (object)value ?? DBNull.Value;
}
/// <summary>
/// This method sets a new value for the specified column for the DataRow it<69>s called on.
/// </summary>
/// <param name="row">
/// The input DataRow.
/// </param>
/// <param name="column">
/// The input DataColumn specificy which row value to retrieve.
/// </param>
/// <param name="value">
/// The new row value for the specified column.
/// </param>
public static void SetField<T>(this DataRow row, DataColumn column, T value) {
DataSetUtil.CheckArgumentNull(row, "row");
row[column] = (object)value ?? DBNull.Value;
}
private static class UnboxT<T>
{
internal static readonly Converter<object, T> Unbox = Create(typeof(T));
private static Converter<object, T> Create(Type type)
{
if (type.IsValueType)
{
if (type.IsGenericType && !type.IsGenericTypeDefinition && (typeof(Nullable<>) == type.GetGenericTypeDefinition()))
{
return (Converter<object, T>)Delegate.CreateDelegate(
typeof(Converter<object, T>),
typeof(UnboxT<T>)
.GetMethod("NullableField", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)
.MakeGenericMethod(type.GetGenericArguments()[0]));
}
return ValueField;
}
return ReferenceField;
}
private static T ReferenceField(object value)
{
return ((DBNull.Value == value) ? default(T) : (T)value);
}
private static T ValueField(object value)
{
if (DBNull.Value == value)
{
throw DataSetUtil.InvalidCast(Strings.DataSetLinq_NonNullableCast(typeof(T).ToString()));
}
return (T)value;
}
private static Nullable<TElem> NullableField<TElem>(object value) where TElem : struct
{
if (DBNull.Value == value)
{
return default(Nullable<TElem>);
}
return new Nullable<TElem>((TElem)value);
}
}
}
}

View File

@@ -0,0 +1,134 @@
//------------------------------------------------------------------------------
// <copyright file="DataSetUtil.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
// <owner current="true" primary="false">[....]</owner>
//------------------------------------------------------------------------------
using System;
using System.Data;
using System.Data.DataSetExtensions;
using System.Diagnostics;
internal static class DataSetUtil
{
#region CheckArgument
internal static void CheckArgumentNull<T>(T argumentValue, string argumentName) where T : class
{
if (null == argumentValue)
{
throw ArgumentNull(argumentName);
}
}
#endregion
#region Trace
private static T TraceException<T>(string trace, T e)
{
Debug.Assert(null != e, "TraceException: null Exception");
if (null != e)
{
//Bid.Trace(trace, e.ToString()); // will include callstack if permission is available
}
return e;
}
private static T TraceExceptionAsReturnValue<T>(T e)
{
return TraceException("<comm.ADP.TraceException|ERR|THROW> '%ls'\n", e);
}
#endregion
#region new Exception
internal static ArgumentException Argument(string message)
{
return TraceExceptionAsReturnValue(new ArgumentException(message));
}
internal static ArgumentNullException ArgumentNull(string message)
{
return TraceExceptionAsReturnValue(new ArgumentNullException(message));
}
internal static ArgumentOutOfRangeException ArgumentOutOfRange(string message, string parameterName)
{
return TraceExceptionAsReturnValue(new ArgumentOutOfRangeException(parameterName, message));
}
internal static InvalidCastException InvalidCast(string message)
{
return TraceExceptionAsReturnValue(new InvalidCastException(message));
}
internal static InvalidOperationException InvalidOperation(string message)
{
return TraceExceptionAsReturnValue(new InvalidOperationException(message));
}
internal static NotSupportedException NotSupported(string message)
{
return TraceExceptionAsReturnValue(new NotSupportedException(message));
}
#endregion
#region new EnumerationValueNotValid
static internal ArgumentOutOfRangeException InvalidEnumerationValue(Type type, int value)
{
return ArgumentOutOfRange(Strings.DataSetLinq_InvalidEnumerationValue(type.Name, value.ToString(System.Globalization.CultureInfo.InvariantCulture)), type.Name);
}
static internal ArgumentOutOfRangeException InvalidDataRowState(DataRowState value)
{
#if DEBUG
switch (value)
{
case DataRowState.Detached:
case DataRowState.Unchanged:
case DataRowState.Added:
case DataRowState.Deleted:
case DataRowState.Modified:
Debug.Assert(false, "valid DataRowState " + value.ToString());
break;
}
#endif
return InvalidEnumerationValue(typeof(DataRowState), (int)value);
}
static internal ArgumentOutOfRangeException InvalidLoadOption(LoadOption value)
{
#if DEBUG
switch (value)
{
case LoadOption.OverwriteChanges:
case LoadOption.PreserveChanges:
case LoadOption.Upsert:
Debug.Assert(false, "valid LoadOption " + value.ToString());
break;
}
#endif
return InvalidEnumerationValue(typeof(LoadOption), (int)value);
}
#endregion
// only StackOverflowException & ThreadAbortException are sealed classes
static private readonly Type StackOverflowType = typeof(System.StackOverflowException);
static private readonly Type OutOfMemoryType = typeof(System.OutOfMemoryException);
static private readonly Type ThreadAbortType = typeof(System.Threading.ThreadAbortException);
static private readonly Type NullReferenceType = typeof(System.NullReferenceException);
static private readonly Type AccessViolationType = typeof(System.AccessViolationException);
static private readonly Type SecurityType = typeof(System.Security.SecurityException);
static internal bool IsCatchableExceptionType(Exception e)
{
// a 'catchable' exception is defined by what it is not.
Type type = e.GetType();
return ((type != StackOverflowType) &&
(type != OutOfMemoryType) &&
(type != ThreadAbortType) &&
(type != NullReferenceType) &&
(type != AccessViolationType) &&
!SecurityType.IsAssignableFrom(type));
}
}

View File

@@ -0,0 +1,280 @@
//------------------------------------------------------------------------------
// <copyright file="DataTableExtenstions.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
// <owner current="true" primary="false">[....]</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
}
}

View File

@@ -0,0 +1,356 @@
//------------------------------------------------------------------------------
// <copyright file="GenericEnumRowCollection.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
// <owner current="true" primary="false">[....]</owner>
//------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.Data;
using System.Linq;
using System.Diagnostics;
using System.Linq.Expressions;
using System.Collections.ObjectModel;
using System.Data.DataSetExtensions;
namespace System.Data
{
/// <summary>
/// Provides an entry point so that Cast operator call can be intercepted within an extension method.
/// </summary>
public abstract class EnumerableRowCollection : IEnumerable
{
internal abstract Type ElementType { get; }
internal abstract DataTable Table { get; }
internal EnumerableRowCollection()
{
}
IEnumerator IEnumerable.GetEnumerator()
{
return null;
}
}
/// <summary>
/// This class provides a wrapper for DataTables to allow for querying via LINQ.
/// </summary>
public class EnumerableRowCollection<TRow> : EnumerableRowCollection, IEnumerable<TRow>
{
private readonly DataTable _table;
private readonly IEnumerable<TRow> _enumerableRows;
private readonly List<Func<TRow, bool>> _listOfPredicates;
// Stores list of sort expression in the order provided by user. E.g. order by, thenby, thenby descending..
private readonly SortExpressionBuilder<TRow> _sortExpression;
private readonly Func<TRow, TRow> _selector;
#region Properties
internal override Type ElementType
{
get
{
return typeof(TRow);
}
}
internal IEnumerable<TRow> EnumerableRows
{
get
{
return _enumerableRows;
}
}
internal override DataTable Table
{
get
{
return _table;
}
}
#endregion Properties
#region Constructors
/// <summary>
/// This constructor is used when Select operator is called with output Type other than input row Type.
/// Basically fail on GetLDV(), but other LINQ operators must work.
/// </summary>
internal EnumerableRowCollection(IEnumerable<TRow> enumerableRows, bool isDataViewable, DataTable table)
{
Debug.Assert(!isDataViewable || table != null, "isDataViewable bug table is null");
_enumerableRows = enumerableRows;
if (isDataViewable)
{
_table = table;
}
_listOfPredicates = new List<Func<TRow, bool>>();
_sortExpression = new SortExpressionBuilder<TRow>();
}
/// <summary>
/// Basic Constructor
/// </summary>
internal EnumerableRowCollection(DataTable table)
{
_table = table;
_enumerableRows = table.Rows.Cast<TRow>();
_listOfPredicates = new List<Func<TRow, bool>>();
_sortExpression = new SortExpressionBuilder<TRow>();
}
/// <summary>
/// Copy Constructor that sets the input IEnumerable as enumerableRows
/// Used to maintain IEnumerable that has linq operators executed in the same order as the user
/// </summary>
internal EnumerableRowCollection(EnumerableRowCollection<TRow> source, IEnumerable<TRow> enumerableRows, Func<TRow, TRow> selector)
{
Debug.Assert(null != enumerableRows, "null enumerableRows");
_enumerableRows = enumerableRows;
_selector = selector;
if (null != source)
{
if (null == source._selector)
{
_table = source._table;
}
_listOfPredicates = new List<Func<TRow, bool>>(source._listOfPredicates);
_sortExpression = source._sortExpression.Clone(); //deep copy the List
}
else
{
_listOfPredicates = new List<Func<TRow, bool>>();
_sortExpression = new SortExpressionBuilder<TRow>();
}
}
#endregion Constructors
#region PublicInterface
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
/// <summary>
/// This method returns an strongly typed iterator
/// for the underlying DataRow collection.
/// </summary>
/// <returns>
/// A strongly typed iterator.
/// </returns>
public IEnumerator<TRow> GetEnumerator()
{
return _enumerableRows.GetEnumerator();
}
#endregion PublicInterface
/// <summary>
/// Evaluates filter and sort if necessary and returns
/// a LinqDataView representing the LINQ query this class has collected.
/// </summary>
/// <returns>LinqDataView repesenting the LINQ query</returns>
internal LinqDataView GetLinqDataView() //Called by AsLinqDataView
{
if ((null == _table) || !typeof(DataRow).IsAssignableFrom(typeof(TRow)))
{
throw DataSetUtil.NotSupported(Strings.ToLDVUnsupported);
}
LinqDataView view = null;
#region BuildSinglePredicate
Func<DataRow, bool> finalPredicate = null; //Conjunction of all .Where(..) predicates
if ((null != _selector) && (0 < _listOfPredicates.Count))
{
// Hook up all individual predicates into one predicate
// This delegate is a conjunction of multiple predicates set by the user
// Note: This is a Short-Circuit Conjunction
finalPredicate =
delegate(DataRow row)
{
if (!Object.ReferenceEquals(row, _selector((TRow)(object)row)))
{
throw DataSetUtil.NotSupported(Strings.ToLDVUnsupported);
}
foreach (Func<TRow, bool> pred in _listOfPredicates)
{
if (!pred((TRow)(object)row))
{
return false;
}
}
return true;
};
}
else if (null != _selector)
{
finalPredicate =
delegate(DataRow row)
{
if (!Object.ReferenceEquals(row, _selector((TRow)(object)row)))
{
throw DataSetUtil.NotSupported(Strings.ToLDVUnsupported);
}
return true;
};
}
else if (0 < _listOfPredicates.Count)
{
finalPredicate =
delegate(DataRow row)
{
foreach (Func<TRow, bool> pred in _listOfPredicates)
{
if (!pred((TRow)(object)row))
{
return false;
}
}
return true;
};
}
#endregion BuildSinglePredicate
#region Evaluate Filter/Sort
// All of this mess below is because we want to create index only once.
//
// If we only have filter, we set _view.Predicate - 1 index creation
// If we only have sort, we set _view.SortExpression() - 1 index creation
// If we have BOTH, we set them through the constructor - 1 index creation
//
// Filter AND Sort
if ((null != finalPredicate) && (0 < _sortExpression.Count))
{
// A lot more work here because constructor does not know type K,
// so the responsibility to create appropriate delegate comparers
// is outside of the constructor.
view = new LinqDataView(
_table,
finalPredicate, //Func() Predicate
delegate(DataRow row) //System.Predicate
{
return finalPredicate(row);
},
delegate(DataRow a, DataRow b) //Comparison for DV for Index creation
{
return _sortExpression.Compare(
_sortExpression.Select((TRow)(object)a),
_sortExpression.Select((TRow)(object)b)
);
},
delegate(object key, DataRow row) //Comparison_K_T for DV's Find()
{
return _sortExpression.Compare(
(List<object>)key,
_sortExpression.Select((TRow)(object)row)
);
},
_sortExpression.CloneCast<DataRow>());
}
else if (null != finalPredicate)
{
//Only Filtering
view = new LinqDataView(
_table,
finalPredicate,
delegate(DataRow row) //System.Predicate
{
return finalPredicate(row);
},
null,
null,
_sortExpression.CloneCast<DataRow>());
}
else if (0 < _sortExpression.Count)
{
//Only Sorting
view = new LinqDataView(
_table,
null,
null,
delegate(DataRow a, DataRow b)
{
return _sortExpression.Compare(_sortExpression.Select((TRow)(object)a), _sortExpression.Select((TRow)(object)b));
},
delegate(object key, DataRow row)
{
return _sortExpression.Compare((List<object>)key, _sortExpression.Select((TRow)(object)row));
},
_sortExpression.CloneCast<DataRow>());
}
else
{
view = new LinqDataView(_table, _sortExpression.CloneCast<DataRow>());
}
#endregion Evaluate Filter and Sort
return view;
}
#region Add Single Filter/Sort Expression
/// <summary>
/// Used to add a filter predicate.
/// A conjunction of all predicates are evaluated in LinqDataView
/// </summary>
internal void AddPredicate(Func<TRow, bool> pred)
{
Debug.Assert(pred != null);
_listOfPredicates.Add(pred);
}
/// <summary>
/// Adds a sort expression when Keyselector is provided but not Comparer
/// </summary>
internal void AddSortExpression<TKey>(Func<TRow, TKey> keySelector, bool isDescending, bool isOrderBy)
{
AddSortExpression<TKey>(keySelector, Comparer<TKey>.Default, isDescending, isOrderBy);
}
/// <summary>
/// Adds a sort expression when Keyselector and Comparer are provided.
/// </summary>
internal void AddSortExpression<TKey>(
Func<TRow, TKey> keySelector,
IComparer<TKey> comparer,
bool isDescending,
bool isOrderBy)
{
DataSetUtil.CheckArgumentNull(keySelector, "keySelector");
DataSetUtil.CheckArgumentNull(comparer, "comparer");
_sortExpression.Add(
delegate(TRow input)
{
return (object)keySelector(input);
},
delegate(object val1, object val2)
{
return (isDescending ? -1 : 1) * comparer.Compare((TKey)val1, (TKey)val2);
},
isOrderBy);
}
#endregion Add Single Filter/Sort Expression
}
}

View File

@@ -0,0 +1,212 @@
//------------------------------------------------------------------------------
// <copyright file="EnumRowCollectionExtensions.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
// <owner current="true" primary="false">[....]</owner>
//------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Globalization;
using System.Diagnostics;
namespace System.Data
{
/// <summary>
/// This static class defines the extension methods that add LINQ operator functionality
/// within IEnumerableDT and IOrderedEnumerableDT.
/// </summary>
public static class EnumerableRowCollectionExtensions
{
/// <summary>
/// LINQ's Where operator for generic EnumerableRowCollection.
/// </summary>
public static EnumerableRowCollection<TRow> Where<TRow>(
this EnumerableRowCollection<TRow> source,
Func<TRow, bool> predicate)
{
EnumerableRowCollection<TRow> edt =
new EnumerableRowCollection<TRow>(source, Enumerable.Where<TRow>(source, predicate), null); //copy constructor
edt.AddPredicate(predicate);
return edt;
}
/// <summary>
/// LINQ's OrderBy operator for generic EnumerableRowCollection.
/// </summary>
public static OrderedEnumerableRowCollection<TRow> OrderBy<TRow, TKey>(
this EnumerableRowCollection<TRow> source,
Func<TRow, TKey> keySelector)
{
IEnumerable<TRow> ie = Enumerable.OrderBy<TRow, TKey>(source, keySelector);
OrderedEnumerableRowCollection<TRow> edt = new OrderedEnumerableRowCollection<TRow>(source, ie);
edt.AddSortExpression(keySelector, false, true);
return edt;
}
/// <summary>
/// LINQ's OrderBy operator for generic EnumerableRowCollection.
/// </summary>
public static OrderedEnumerableRowCollection<TRow> OrderBy<TRow, TKey>(
this EnumerableRowCollection<TRow> source,
Func<TRow, TKey> keySelector,
IComparer<TKey> comparer)
{
IEnumerable<TRow> ie = Enumerable.OrderBy<TRow, TKey>(source, keySelector, comparer);
OrderedEnumerableRowCollection<TRow> edt = new OrderedEnumerableRowCollection<TRow>(source, ie);
edt.AddSortExpression(keySelector, comparer, false, true);
return edt;
}
/// <summary>
/// LINQ's OrderByDescending operator for generic EnumerableRowCollection.
/// </summary>
public static OrderedEnumerableRowCollection<TRow> OrderByDescending<TRow, TKey>(
this EnumerableRowCollection<TRow> source,
Func<TRow, TKey> keySelector)
{
IEnumerable<TRow> ie = Enumerable.OrderByDescending<TRow, TKey>(source, keySelector);
OrderedEnumerableRowCollection<TRow> edt = new OrderedEnumerableRowCollection<TRow>(source, ie);
edt.AddSortExpression(keySelector, true, true);
return edt;
}
/// <summary>
/// LINQ's OrderByDescending operator for generic EnumerableRowCollection.
/// </summary>
public static OrderedEnumerableRowCollection<TRow> OrderByDescending<TRow, TKey>(
this EnumerableRowCollection<TRow> source,
Func<TRow, TKey> keySelector,
IComparer<TKey> comparer)
{
IEnumerable<TRow> ie = Enumerable.OrderByDescending<TRow, TKey>(source, keySelector, comparer);
OrderedEnumerableRowCollection<TRow> edt = new OrderedEnumerableRowCollection<TRow>(source, ie);
edt.AddSortExpression(keySelector, comparer, true, true);
return edt;
}
/// <summary>
/// LINQ's ThenBy operator for generic EnumerableRowCollection.
/// </summary>
public static OrderedEnumerableRowCollection<TRow> ThenBy<TRow, TKey>(
this OrderedEnumerableRowCollection<TRow> source,
Func<TRow, TKey> keySelector)
{
IEnumerable<TRow> ie =
Enumerable.ThenBy<TRow, TKey>((IOrderedEnumerable<TRow>)source.EnumerableRows, keySelector);
OrderedEnumerableRowCollection<TRow> edt =
new OrderedEnumerableRowCollection<TRow>((EnumerableRowCollection<TRow>)source, ie);
edt.AddSortExpression(keySelector, /*isDesc*/ false, /*isOrderBy*/ false);
return edt;
}
/// <summary>
/// LINQ's ThenBy operator for generic EnumerableRowCollection.
/// </summary>
public static OrderedEnumerableRowCollection<TRow> ThenBy<TRow, TKey>(
this OrderedEnumerableRowCollection<TRow> source,
Func<TRow, TKey> keySelector,
IComparer<TKey> comparer)
{
IEnumerable<TRow> ie =
Enumerable.ThenBy<TRow, TKey>((IOrderedEnumerable<TRow>)source.EnumerableRows, keySelector, comparer);
OrderedEnumerableRowCollection<TRow> edt =
new OrderedEnumerableRowCollection<TRow>((EnumerableRowCollection<TRow>)source, ie);
edt.AddSortExpression(keySelector, comparer, false, false);
return edt;
}
/// <summary>
/// LINQ's ThenByDescending operator for generic EnumerableRowCollection.
/// </summary>
public static OrderedEnumerableRowCollection<TRow> ThenByDescending<TRow, TKey>(
this OrderedEnumerableRowCollection<TRow> source,
Func<TRow, TKey> keySelector)
{
IEnumerable<TRow> ie =
Enumerable.ThenByDescending<TRow, TKey>((IOrderedEnumerable<TRow>)source.EnumerableRows, keySelector);
OrderedEnumerableRowCollection<TRow> edt =
new OrderedEnumerableRowCollection<TRow>((EnumerableRowCollection<TRow>)source, ie);
edt.AddSortExpression(keySelector, /*desc*/ true, false);
return edt;
}
/// <summary>
/// LINQ's ThenByDescending operator for generic EnumerableRowCollection.
/// </summary>
public static OrderedEnumerableRowCollection<TRow> ThenByDescending<TRow, TKey>(
this OrderedEnumerableRowCollection<TRow> source,
Func<TRow, TKey> keySelector,
IComparer<TKey> comparer)
{
IEnumerable<TRow> ie =
Enumerable.ThenByDescending<TRow, TKey>((IOrderedEnumerable<TRow>)source.EnumerableRows, keySelector, comparer);
OrderedEnumerableRowCollection<TRow> edt =
new OrderedEnumerableRowCollection<TRow>((EnumerableRowCollection<TRow>)source, ie);
edt.AddSortExpression(keySelector, comparer, true, false);
return edt;
}
/// <summary>
/// Executes a Select (Projection) on EnumerableDataTable. If the selector returns a different
/// type than the type of rows, then AsLinqDataView is disabled, and the returning EnumerableDataTable
/// represents an enumerable over the LINQ Query.
/// </summary>
public static EnumerableRowCollection<S> Select<TRow, S>(
this EnumerableRowCollection<TRow> source,
Func<TRow, S> selector)
{
//Anonymous type or some other type
//The only thing that matters from this point on is _enumerableRows
IEnumerable<S> typedEnumerable = Enumerable.Select<TRow, S>(source, selector);
// Dont need predicates or sort expression from this point on since we know
// AsLinqDataView is disabled.
return new EnumerableRowCollection<S>(((object)source) as EnumerableRowCollection<S>,
typedEnumerable,
((object)selector) as Func<S,S>);
}
/// <summary>
/// Casts an EnumerableDataTable_TSource into EnumerableDataTable_TResult
/// </summary>
public static EnumerableRowCollection<TResult> Cast<TResult>(this EnumerableRowCollection source)
{
// Since Cast does not have the signature Cast_T_R(..) this call is routed
// through the non-generic base class EnumerableDataTable
if ((null != source) && source.ElementType.Equals(typeof(TResult)))
{
return (EnumerableRowCollection<TResult>)(object)source;
}
else
{ //Anonymous type or some other type
//The only thing that matters from this point on is _enumerableRows
IEnumerable<TResult> typedEnumerable = Enumerable.Cast<TResult>(source);
EnumerableRowCollection<TResult> newEdt = new EnumerableRowCollection<TResult>(
typedEnumerable,
typeof(TResult).IsAssignableFrom(source.ElementType) && typeof(DataRow).IsAssignableFrom(typeof(TResult)),
source.Table);
return newEdt;
}
}
} //end class
}

View File

@@ -0,0 +1,305 @@
//------------------------------------------------------------------------------
// <copyright file="LinqDataView.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
// <owner current="true" primary="false">[....]</owner>
//------------------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;
using System.ComponentModel;
using System.Data;
using System.Text;
using System.Data.DataSetExtensions;
namespace System.Data
{
/// <summary>
/// Represents a bindable, queryable DataView of DataRow, that can be created from from LINQ queries over DataTable
/// and from DataTable.
/// </summary>
internal class LinqDataView : DataView, IBindingList, IBindingListView
{
/// <summary>
/// A Comparer that compares a Key and a Row.
/// </summary>
internal Func<object, DataRow, int> comparerKeyRow; // comparer for DataView.Find(..
/// <summary>
/// Builds the sort expression in case multiple selector/comparers are added
/// </summary>
internal readonly SortExpressionBuilder<DataRow> sortExpressionBuilder;
/// <summary>
/// Constructs a LinkDataView and its parent DataView.
/// Does not create index on the DataView since filter and sort expressions are not yet provided.
/// </summary>
/// <param name="table">The input table from which LinkDataView is to be created.</param>
internal LinqDataView(DataTable table, SortExpressionBuilder<DataRow> sortExpressionBuilder)
: base(table)
{
Debug.Assert(table != null, "null DataTable");
this.sortExpressionBuilder = sortExpressionBuilder ?? new SortExpressionBuilder<DataRow>();
}
//I have two forms of predicate because I need to pass in null if predicate is null. Otherwise I need to convert it into delegate and pass it into
// data view's constructor. That logic for checking null can't be embedded in the base constructor call.
/// <summary>
///
/// </summary>
/// <param name="table">Table from which to create the view</param>
/// <param name="predicate_func">User-provided filter-predicate as a Func<DataRow>, bool>"/></param>
/// <param name="predicate_system">User-provided predicate but in the form of System.Predicate<DataRow>
/// Predicates are being replicated in different forms so that nulls can be passed in.
/// For e.g. when user provides null predicate, base.Predicate should be set to null. I cant do that in the constructor initialization
/// if I will have to create System.Predicate delegate from Func.
/// </param>
/// <param name="comparison">The comparer function of DataRow to be used for sorting. </param>
/// <param name="comparerKeyRow">A comparer function that compares a Key value to DataRow.</param>
/// <param name="isDescending">Whether sorting is ascending or descending.</param>
/// <param name="rowState">Row state filter. For the purpose of LinkDataView it should always be CurrentRows.</param>
internal LinqDataView(
DataTable table,
Func<DataRow, bool> predicate_func,
Predicate<DataRow> predicate_system,
Comparison<DataRow> comparison,
Func<object, DataRow, int> comparerKeyRow,
SortExpressionBuilder<DataRow> sortExpressionBuilder)
//Parent constructor
: base(table,
predicate_system,
comparison,
DataViewRowState.CurrentRows)
{
this.sortExpressionBuilder = (sortExpressionBuilder == null) ? this.sortExpressionBuilder : sortExpressionBuilder;
this.comparerKeyRow = comparerKeyRow;
}
/// <summary>
/// Gets or sets the expression used to filter which rows are viewed in the LinqDataView
/// </summary>
public override string RowFilter
{
get
{
if (base.RowPredicate == null)//using string based filter or no filter
{
return base.RowFilter;
}
else //using expression based filter
{
return null;
}
}
set
{
if (value == null)
{
base.RowPredicate = null;
base.RowFilter = String.Empty; //INDEX rebuild twice
}
else
{
base.RowFilter = value;
base.RowPredicate = null;
}
}
}
#region Find
/// <summary>
/// Searches the index and finds a single row where the sort-key matches the input key
/// </summary>
/// <param name="key">Value of the key to find</param>
/// <returns>Index of the first match of input key</returns>
internal override int FindByKey(object key)
{
/////////////// Preconditions ////////////////
//check that both string and expression based sort are never simultaneously set
Debug.Assert(base.Sort != null);
Debug.Assert(!(!String.IsNullOrEmpty(base.Sort) && base.SortComparison != null));
/////////////////////////////////////////////
if (!String.IsNullOrEmpty(base.Sort)) //use find for DV's sort string
{
return base.FindByKey(key);
}
else if (base.SortComparison == null) //neither string or expr set
{
//This is the exception message from DataView that we want to use
throw ExceptionBuilder.IndexKeyLength(0, 0);
}
else //find for expression based sort
{
if (sortExpressionBuilder.Count !=1)
throw DataSetUtil.InvalidOperation(Strings.LDV_InvalidNumOfKeys(sortExpressionBuilder.Count));
Index.ComparisonBySelector<object, DataRow> compareDelg =
new Index.ComparisonBySelector<object, DataRow>(comparerKeyRow);
List<object> keyList = new List<object>();
keyList.Add(key);
Range range = FindRecords<object, DataRow>(compareDelg, keyList);
return (range.Count == 0) ? -1 : range.Min;
}
}
/// <summary>
/// Since LinkDataView does not support multiple selectors/comparers, it does not make sense for
/// them to Find using multiple keys.
/// This overriden method prevents users calling multi-key find on dataview.
/// </summary>
internal override int FindByKey(object[] key)
{
//---------Checks ----------------
//must have string or expression based sort specified
if (base.SortComparison == null && String.IsNullOrEmpty(base.Sort))
{
//This is the exception message from DataView that we want to use
throw ExceptionBuilder.IndexKeyLength(0, 0);
}
else if (base.SortComparison != null && key.Length != sortExpressionBuilder.Count)
{
throw DataSetUtil.InvalidOperation(Strings.LDV_InvalidNumOfKeys(sortExpressionBuilder.Count));
}
//--------------------------------
if (base.SortComparison == null)//using string to sort
return base.FindByKey(key);
else
{
Index.ComparisonBySelector<object, DataRow> compareDelg =
new Index.ComparisonBySelector<object, DataRow>(comparerKeyRow);
List<object> keyList = new List<object>();
foreach (object singleKey in key)
{
keyList.Add(singleKey);
}
Range range = FindRecords<object, DataRow>(compareDelg, keyList);
return (range.Count == 0) ? -1 : range.Min;
}
}
/// <summary>
/// Searches the index and finds rows where the sort-key matches the input key.
/// Since LinkDataView does not support multiple selectors/comparers, it does not make sense for
/// them to Find using multiple keys. This overriden method prevents users calling multi-key find on dataview.
/// </summary>
internal override DataRowView[] FindRowsByKey(object[] key)
{
//---------Checks ----------------
//must have string or expression based sort specified
if (base.SortComparison == null && String.IsNullOrEmpty(base.Sort))
{
//This is the exception message from DataView that we want to use
throw ExceptionBuilder.IndexKeyLength(0, 0);
}
else if (base.SortComparison != null && key.Length != sortExpressionBuilder.Count)
{
throw DataSetUtil.InvalidOperation(Strings.LDV_InvalidNumOfKeys(sortExpressionBuilder.Count));
}
//--------------------------------
if (base.SortComparison == null)//using string to sort
{
return base.FindRowsByKey(key);
}
else
{
Range range = FindRecords<object, DataRow>(
new Index.ComparisonBySelector<object, DataRow>(comparerKeyRow),
new List<object>(key));
return base.GetDataRowViewFromRange(range);
}
}
#endregion
#region Misc Overrides
/// <summary>
/// Overriding DataView's SetIndex to prevent users from setting RowState filter to anything other
/// than CurrentRows.
/// </summary>
internal override void SetIndex(string newSort, DataViewRowState newRowStates, IFilter newRowFilter)
{
//Throw only if expressions (filter or sort) are used and rowstate is not current rows
if ( (base.SortComparison != null || base.RowPredicate != null)
&& newRowStates != DataViewRowState.CurrentRows)
{
throw DataSetUtil.Argument(Strings.LDVRowStateError);
}
else
{
base.SetIndex(newSort, newRowStates, newRowFilter);
}
}
#endregion
#region IBindingList
/// <summary>
/// Clears both expression-based and DataView's string-based sorting.
/// </summary>
void IBindingList.RemoveSort()
{
base.Sort = String.Empty;
base.SortComparison = null;
}
/// <summary>
/// Overrides IBindingList's SortProperty so that it returns null if expression based sort
/// is used in the LinkDataView, otherwise it defers the result to DataView
/// </summary>
PropertyDescriptor IBindingList.SortProperty
{
get
{
return (base.SortComparison == null) ? base.GetSortProperty() : null;
}
}
/// <summary>
/// Overrides IBindingList's SortDescriptions so that it returns null if expression based sort
/// is used in the LinkDataView, otherwise it defers the result to DataView
/// </summary>
ListSortDescriptionCollection IBindingListView.SortDescriptions
{
get
{
if (base.SortComparison == null)
{
return base.GetSortDescriptions();
}
else
{
return new ListSortDescriptionCollection();
}
}
}
/// <summary>
/// Tells whether the LinqDataView is sorted or not
/// </summary>
bool IBindingList.IsSorted
{
get
{ //Sorted if either expression based sort or string based sort is set
return !(base.SortComparison == null && base.Sort.Length == 0);
}
}
#endregion
}
}

View File

@@ -0,0 +1,34 @@
//------------------------------------------------------------------------------
// <copyright file="OrderedEnumerableRowCollection.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
// <owner current="true" primary="false">[....]</owner>
//------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.Data;
using System.Linq;
using System.Diagnostics;
namespace System.Data
{
/// <summary>
/// This class provides a wrapper for DataTables representing an ordered sequence.
/// </summary>
public sealed class OrderedEnumerableRowCollection<TRow> : EnumerableRowCollection<TRow>
{
/// <summary>
/// Copy Constructor that sets enumerableRows to the one given in the input
/// </summary>
internal OrderedEnumerableRowCollection(EnumerableRowCollection<TRow> enumerableTable, IEnumerable<TRow> enumerableRows)
: base(enumerableTable, enumerableRows, null)
{
}
}
}

View File

@@ -0,0 +1,9 @@
//------------------------------------------------------------------------------
// <copyright file="ShippingAssemblyAttribute.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
// <owner current="true" primary="false">[....]</owner>
//------------------------------------------------------------------------------
[assembly: System.Security.SecurityCritical]

View File

@@ -0,0 +1,209 @@
//------------------------------------------------------------------------------
// <copyright file="EnumerableDataTable.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
// <owner current="true" primary="false">[....]</owner>
//------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.Data;
using System.Linq;
using System.Diagnostics;
namespace System.Data
{
/// <summary>
/// This class represents a combined sort expression build using mutiple sort expressions.
/// </summary>
/// <typeparam name="T"></typeparam>
internal class SortExpressionBuilder<T> : IComparer<List<object>>
{
/**
* This class ensures multiple orderby/thenbys are handled correctly. Its semantics is as follows:
*
* Query 1:
* orderby a
* thenby b
* orderby c
* orderby d
* thenby e
*
* is equivalent to:
*
* Query 2:
* orderby d
* thenby e
* thenby c
* thenby a
* thenby b
*
**/
//Selectors and comparers are mapped using the index in the list.
//E.g: _comparers[i] is used with _selectors[i]
LinkedList<Func<T, object>> _selectors = new LinkedList<Func<T, object>>();
LinkedList<Comparison<object>> _comparers = new LinkedList<Comparison<object>>();
LinkedListNode<Func<T, object>> _currentSelector = null;
LinkedListNode<Comparison<object>> _currentComparer = null;
/// <summary>
/// Adds a sorting selector/comparer in the correct order
/// </summary>
internal void Add(Func<T, object> keySelector, Comparison<object> compare, bool isOrderBy)
{
Debug.Assert(keySelector != null);
Debug.Assert(compare != null);
//Inputs are assumed to be valid. The burden for ensuring it is on the caller.
if (isOrderBy)
{
_currentSelector = _selectors.AddFirst(keySelector);
_currentComparer = _comparers.AddFirst(compare);
}
else
{
//ThenBy can only be called after OrderBy
Debug.Assert(_currentSelector != null);
Debug.Assert(_currentComparer != null);
_currentSelector = _selectors.AddAfter(_currentSelector, keySelector);
_currentComparer = _comparers.AddAfter(_currentComparer, compare);
}
}
/// <summary>
/// Represents a Combined selector of all selectors added thusfar.
/// </summary>
/// <returns>List of 'objects returned by each selector'. This list is the combined-selector</returns>
public List<object> Select(T row)
{
List<object> result = new List<object>();
foreach (Func<T, object> selector in _selectors)
{
result.Add(selector(row));
}
return result;
}
/// <summary>
/// Represents a Comparer (of IComparer) that compares two combined-selectors using
/// provided comparers for each individual selector.
/// Note: Comparison is done in the order it was Added.
/// </summary>
/// <returns>Comparison result of the combined Sort comparer expression</returns>
public int Compare(List<object> a, List<object> b)
{
Debug.Assert(a.Count == Count);
int i = 0;
foreach (Comparison<object> compare in _comparers)
{
int result = compare(a[i], b[i]);
if (result != 0)
{
return result;
}
i++;
}
return 0;
}
internal int Count
{
get
{
Debug.Assert(_selectors.Count == _comparers.Count); //weak now that we have two dimensions
return _selectors.Count;
}
}
/// <summary>
/// Clones the SortexpressionBuilder and returns a new object
/// that points to same comparer and selectors (in the same order).
/// </summary>
/// <returns></returns>
internal SortExpressionBuilder<T> Clone()
{
SortExpressionBuilder<T> builder = new SortExpressionBuilder<T>();
foreach (Func<T, object> selector in _selectors)
{
if (selector == _currentSelector.Value)
{
builder._currentSelector = builder._selectors.AddLast(selector);
}
else
{
builder._selectors.AddLast(selector);
}
}
foreach (Comparison<object> comparer in _comparers)
{
if (comparer == _currentComparer.Value)
{
builder._currentComparer = builder._comparers.AddLast(comparer);
}
else
{
builder._comparers.AddLast(comparer);
}
}
return builder;
}
/// <summary>
/// Clones the SortExpressinBuilder and casts to type TResult.
/// </summary>
internal SortExpressionBuilder<TResult> CloneCast<TResult>()
{
SortExpressionBuilder<TResult> builder = new SortExpressionBuilder<TResult>();
foreach (Func<T, object> selector in _selectors)
{
if (selector == _currentSelector.Value)
{
builder._currentSelector = builder._selectors.AddLast(r => selector((T)(object)r));
}
else
{
builder._selectors.AddLast(r => selector((T)(object)r));
}
}
foreach (Comparison<object> comparer in _comparers)
{
if (comparer == _currentComparer.Value)
{
builder._currentComparer = builder._comparers.AddLast(comparer);
}
else
{
builder._comparers.AddLast(comparer);
}
}
return builder;
}
} //end SortExpressionBuilder<T>
}

View File

@@ -0,0 +1,70 @@
//------------------------------------------------------------------------------
// <copyright file="TypedTableBase.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
// <owner current="true" primary="false">spather</owner>
//------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Linq.Expressions;
using System.Runtime.Serialization;
namespace System.Data {
/// <summary>
/// This is the generic base class for TypedDataSet
/// </summary>
[Serializable]
public abstract class TypedTableBase<T> : DataTable, IEnumerable<T> where T : DataRow {
/// <summary>
/// Default constructor for generic TypedTableBase.
/// Will be called by generated Typed DataSet classes and is not for public use.
/// </summary>
protected TypedTableBase() : base() {}
/// <summary>
/// Constructor for the generic TypedTableBase with takes SerializationInfo and StreamingContext.
/// Will be called by generated Typed DataSet classes and
/// is not for public use.
/// </summary>
/// <param name="info">
/// SerializationInfo containing data to construct the object.
/// </param>
/// <param name="context">
/// The streaming context for the object being deserializad.
/// </param>
protected TypedTableBase(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
: base(info, context) {}
/// <summary>
/// This property returns a enumerator of T for the TypedTable. Note, this could
/// execute the underlying Linq expression.
/// </summary>
/// <returns>
/// IEnumerable of T.
/// </returns>
public IEnumerator<T> GetEnumerator() {
return this.Rows.Cast<T>().GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
/// <summary>
/// Casts an EnumerableDataTable_TSource into EnumerableDataTable_TResult
/// </summary>
public EnumerableRowCollection<TResult> Cast<TResult>()
{
EnumerableRowCollection<T> erc = new EnumerableRowCollection<T>((DataTable)this);
return erc.Cast<TResult>();
}
}
}

View File

@@ -0,0 +1,133 @@
//------------------------------------------------------------------------------
// <copyright file="EnumRowCollectionExtensions.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
// <owner current="true" primary="false">[....]</owner>
//------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Globalization;
using System.Diagnostics;
namespace System.Data
{
/// <summary>
/// This static class defines the extension methods that add LINQ operator functionality
/// within IEnumerableDT and IOrderedEnumerableDT.
/// </summary>
public static class TypedTableBaseExtensions
{
/// <summary>
/// LINQ's Where operator for generic EnumerableRowCollection.
/// </summary>
public static EnumerableRowCollection<TRow> Where<TRow>(
this TypedTableBase<TRow> source,
Func<TRow, bool> predicate) where TRow : DataRow
{
DataSetUtil.CheckArgumentNull(source, "source");
EnumerableRowCollection<TRow> erc = new EnumerableRowCollection<TRow>((DataTable)source);
return erc.Where<TRow>(predicate);
}
/// <summary>
/// LINQ's OrderBy operator for generic EnumerableRowCollection.
/// </summary>
public static OrderedEnumerableRowCollection<TRow> OrderBy<TRow, TKey>(
this TypedTableBase<TRow> source,
Func<TRow, TKey> keySelector) where TRow : DataRow
{
DataSetUtil.CheckArgumentNull(source, "source");
EnumerableRowCollection<TRow> erc = new EnumerableRowCollection<TRow>((DataTable)source);
return erc.OrderBy<TRow, TKey>(keySelector);
}
/// <summary>
/// LINQ's OrderBy operator for generic EnumerableRowCollection.
/// </summary>
public static OrderedEnumerableRowCollection<TRow> OrderBy<TRow, TKey>(
this TypedTableBase<TRow> source,
Func<TRow, TKey> keySelector,
IComparer<TKey> comparer) where TRow : DataRow
{
DataSetUtil.CheckArgumentNull(source, "source");
EnumerableRowCollection<TRow> erc = new EnumerableRowCollection<TRow>((DataTable)source);
return erc.OrderBy<TRow, TKey>(keySelector, comparer);
}
/// <summary>
/// LINQ's OrderByDescending operator for generic EnumerableRowCollection.
/// </summary>
public static OrderedEnumerableRowCollection<TRow> OrderByDescending<TRow, TKey>(
this TypedTableBase<TRow> source,
Func<TRow, TKey> keySelector) where TRow : DataRow
{
DataSetUtil.CheckArgumentNull(source, "source");
EnumerableRowCollection<TRow> erc = new EnumerableRowCollection<TRow>((DataTable)source);
return erc.OrderByDescending<TRow, TKey>(keySelector);
}
/// <summary>
/// LINQ's OrderByDescending operator for generic EnumerableRowCollection.
/// </summary>
public static OrderedEnumerableRowCollection<TRow> OrderByDescending<TRow, TKey>(
this TypedTableBase<TRow> source,
Func<TRow, TKey> keySelector,
IComparer<TKey> comparer) where TRow : DataRow
{
DataSetUtil.CheckArgumentNull(source, "source");
EnumerableRowCollection<TRow> erc = new EnumerableRowCollection<TRow>((DataTable)source);
return erc.OrderByDescending<TRow, TKey>(keySelector, comparer);
}
/// <summary>
/// Executes a Select (Projection) on EnumerableDataTable. If the selector returns a different
/// type than the type of rows, then AsLinqDataView is disabled, and the returning EnumerableDataTable
/// represents an enumerable over the LINQ Query.
/// </summary>
public static EnumerableRowCollection<S> Select<TRow, S>(
this TypedTableBase<TRow> source,
Func<TRow, S> selector) where TRow : DataRow
{
DataSetUtil.CheckArgumentNull(source, "source");
EnumerableRowCollection<TRow> erc = new EnumerableRowCollection<TRow>((DataTable)source);
return erc.Select<TRow, S>(selector);
}
/// <summary>
/// This method returns a IEnumerable of TRow.
/// </summary>
/// <param name="source">
/// The source DataTable to make enumerable.
/// </param>
/// <returns>
/// IEnumerable of datarows.
/// </returns>
public static EnumerableRowCollection<TRow> AsEnumerable<TRow>(this TypedTableBase<TRow> source) where TRow : DataRow
{
DataSetUtil.CheckArgumentNull(source, "source");
return new EnumerableRowCollection<TRow>(source as DataTable);
}
public static TRow ElementAtOrDefault<TRow>(this TypedTableBase<TRow> source, int index) where TRow : DataRow
{
if ((index >= 0) && (index < source.Rows.Count))
{
return (TRow)source.Rows[index];
}
else
{
return default(TRow);
}
}
} //end class
}