/mcs/class/referencesource/System.Data.DataSetExtensions/System/Data/EnumerableRowCollection.cs
C# | 356 lines | 254 code | 40 blank | 62 comment | 32 complexity | 6061d1929197e76eb621bf274165abef MD5 | raw file
Possible License(s): LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0
- //------------------------------------------------------------------------------
- // <copyright file="GenericEnumRowCollection.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.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
- }
- }