1 //------------------------------------------------------------------------------
2 // <copyright file="GenericEnumRowCollection.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">[....]</owner>
6 // <owner current="true" primary="false">[....]</owner>
7 //------------------------------------------------------------------------------
10 using System.Collections.Generic;
11 using System.Collections;
15 using System.Diagnostics;
16 using System.Linq.Expressions;
17 using System.Collections.ObjectModel;
18 using System.Data.DataSetExtensions;
23 /// Provides an entry point so that Cast operator call can be intercepted within an extension method.
25 public abstract class EnumerableRowCollection : IEnumerable
27 internal abstract Type ElementType { get; }
28 internal abstract DataTable Table { get; }
30 internal EnumerableRowCollection()
34 IEnumerator IEnumerable.GetEnumerator()
41 /// This class provides a wrapper for DataTables to allow for querying via LINQ.
43 public class EnumerableRowCollection<TRow> : EnumerableRowCollection, IEnumerable<TRow>
45 private readonly DataTable _table;
46 private readonly IEnumerable<TRow> _enumerableRows;
47 private readonly List<Func<TRow, bool>> _listOfPredicates;
49 // Stores list of sort expression in the order provided by user. E.g. order by, thenby, thenby descending..
50 private readonly SortExpressionBuilder<TRow> _sortExpression;
52 private readonly Func<TRow, TRow> _selector;
56 internal override Type ElementType
65 internal IEnumerable<TRow> EnumerableRows
69 return _enumerableRows;
73 internal override DataTable Table
87 /// This constructor is used when Select operator is called with output Type other than input row Type.
88 /// Basically fail on GetLDV(), but other LINQ operators must work.
90 internal EnumerableRowCollection(IEnumerable<TRow> enumerableRows, bool isDataViewable, DataTable table)
92 Debug.Assert(!isDataViewable || table != null, "isDataViewable bug table is null");
94 _enumerableRows = enumerableRows;
99 _listOfPredicates = new List<Func<TRow, bool>>();
100 _sortExpression = new SortExpressionBuilder<TRow>();
104 /// Basic Constructor
106 internal EnumerableRowCollection(DataTable table)
109 _enumerableRows = table.Rows.Cast<TRow>();
110 _listOfPredicates = new List<Func<TRow, bool>>();
111 _sortExpression = new SortExpressionBuilder<TRow>();
115 /// Copy Constructor that sets the input IEnumerable as enumerableRows
116 /// Used to maintain IEnumerable that has linq operators executed in the same order as the user
118 internal EnumerableRowCollection(EnumerableRowCollection<TRow> source, IEnumerable<TRow> enumerableRows, Func<TRow, TRow> selector)
120 Debug.Assert(null != enumerableRows, "null enumerableRows");
122 _enumerableRows = enumerableRows;
123 _selector = selector;
126 if (null == source._selector)
128 _table = source._table;
130 _listOfPredicates = new List<Func<TRow, bool>>(source._listOfPredicates);
131 _sortExpression = source._sortExpression.Clone(); //deep copy the List
135 _listOfPredicates = new List<Func<TRow, bool>>();
136 _sortExpression = new SortExpressionBuilder<TRow>();
140 #endregion Constructors
142 #region PublicInterface
143 IEnumerator IEnumerable.GetEnumerator()
145 return GetEnumerator();
149 /// This method returns an strongly typed iterator
150 /// for the underlying DataRow collection.
153 /// A strongly typed iterator.
155 public IEnumerator<TRow> GetEnumerator()
157 return _enumerableRows.GetEnumerator();
159 #endregion PublicInterface
162 /// Evaluates filter and sort if necessary and returns
163 /// a LinqDataView representing the LINQ query this class has collected.
165 /// <returns>LinqDataView repesenting the LINQ query</returns>
166 internal LinqDataView GetLinqDataView() //Called by AsLinqDataView
168 if ((null == _table) || !typeof(DataRow).IsAssignableFrom(typeof(TRow)))
170 throw DataSetUtil.NotSupported(Strings.ToLDVUnsupported);
173 LinqDataView view = null;
175 #region BuildSinglePredicate
177 Func<DataRow, bool> finalPredicate = null; //Conjunction of all .Where(..) predicates
178 if ((null != _selector) && (0 < _listOfPredicates.Count))
180 // Hook up all individual predicates into one predicate
181 // This delegate is a conjunction of multiple predicates set by the user
182 // Note: This is a Short-Circuit Conjunction
184 delegate(DataRow row)
186 if (!Object.ReferenceEquals(row, _selector((TRow)(object)row)))
188 throw DataSetUtil.NotSupported(Strings.ToLDVUnsupported);
190 foreach (Func<TRow, bool> pred in _listOfPredicates)
192 if (!pred((TRow)(object)row))
200 else if (null != _selector)
203 delegate(DataRow row)
205 if (!Object.ReferenceEquals(row, _selector((TRow)(object)row)))
207 throw DataSetUtil.NotSupported(Strings.ToLDVUnsupported);
212 else if (0 < _listOfPredicates.Count)
215 delegate(DataRow row)
217 foreach (Func<TRow, bool> pred in _listOfPredicates)
219 if (!pred((TRow)(object)row))
227 #endregion BuildSinglePredicate
229 #region Evaluate Filter/Sort
230 // All of this mess below is because we want to create index only once.
232 // If we only have filter, we set _view.Predicate - 1 index creation
233 // If we only have sort, we set _view.SortExpression() - 1 index creation
234 // If we have BOTH, we set them through the constructor - 1 index creation
238 if ((null != finalPredicate) && (0 < _sortExpression.Count))
240 // A lot more work here because constructor does not know type K,
241 // so the responsibility to create appropriate delegate comparers
242 // is outside of the constructor.
244 view = new LinqDataView(
246 finalPredicate, //Func() Predicate
247 delegate(DataRow row) //System.Predicate
249 return finalPredicate(row);
251 delegate(DataRow a, DataRow b) //Comparison for DV for Index creation
253 return _sortExpression.Compare(
254 _sortExpression.Select((TRow)(object)a),
255 _sortExpression.Select((TRow)(object)b)
258 delegate(object key, DataRow row) //Comparison_K_T for DV's Find()
260 return _sortExpression.Compare(
262 _sortExpression.Select((TRow)(object)row)
265 _sortExpression.CloneCast<DataRow>());
267 else if (null != finalPredicate)
270 view = new LinqDataView(
273 delegate(DataRow row) //System.Predicate
275 return finalPredicate(row);
279 _sortExpression.CloneCast<DataRow>());
281 else if (0 < _sortExpression.Count)
284 view = new LinqDataView(
288 delegate(DataRow a, DataRow b)
290 return _sortExpression.Compare(_sortExpression.Select((TRow)(object)a), _sortExpression.Select((TRow)(object)b));
292 delegate(object key, DataRow row)
294 return _sortExpression.Compare((List<object>)key, _sortExpression.Select((TRow)(object)row));
296 _sortExpression.CloneCast<DataRow>());
300 view = new LinqDataView(_table, _sortExpression.CloneCast<DataRow>());
302 #endregion Evaluate Filter and Sort
308 #region Add Single Filter/Sort Expression
311 /// Used to add a filter predicate.
312 /// A conjunction of all predicates are evaluated in LinqDataView
314 internal void AddPredicate(Func<TRow, bool> pred)
316 Debug.Assert(pred != null);
317 _listOfPredicates.Add(pred);
321 /// Adds a sort expression when Keyselector is provided but not Comparer
323 internal void AddSortExpression<TKey>(Func<TRow, TKey> keySelector, bool isDescending, bool isOrderBy)
325 AddSortExpression<TKey>(keySelector, Comparer<TKey>.Default, isDescending, isOrderBy);
329 /// Adds a sort expression when Keyselector and Comparer are provided.
331 internal void AddSortExpression<TKey>(
332 Func<TRow, TKey> keySelector,
333 IComparer<TKey> comparer,
337 DataSetUtil.CheckArgumentNull(keySelector, "keySelector");
338 DataSetUtil.CheckArgumentNull(comparer, "comparer");
343 return (object)keySelector(input);
345 delegate(object val1, object val2)
347 return (isDescending ? -1 : 1) * comparer.Compare((TKey)val1, (TKey)val2);
352 #endregion Add Single Filter/Sort Expression