//------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// Microsoft
// Microsoft
//------------------------------------------------------------------------------
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
{
///
/// Provides an entry point so that Cast operator call can be intercepted within an extension method.
///
public abstract class EnumerableRowCollection : IEnumerable
{
internal abstract Type ElementType { get; }
internal abstract DataTable Table { get; }
internal EnumerableRowCollection()
{
}
IEnumerator IEnumerable.GetEnumerator()
{
return null;
}
}
///
/// This class provides a wrapper for DataTables to allow for querying via LINQ.
///
public class EnumerableRowCollection : EnumerableRowCollection, IEnumerable
{
private readonly DataTable _table;
private readonly IEnumerable _enumerableRows;
private readonly List> _listOfPredicates;
// Stores list of sort expression in the order provided by user. E.g. order by, thenby, thenby descending..
private readonly SortExpressionBuilder _sortExpression;
private readonly Func _selector;
#region Properties
internal override Type ElementType
{
get
{
return typeof(TRow);
}
}
internal IEnumerable EnumerableRows
{
get
{
return _enumerableRows;
}
}
internal override DataTable Table
{
get
{
return _table;
}
}
#endregion Properties
#region Constructors
///
/// 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.
///
internal EnumerableRowCollection(IEnumerable enumerableRows, bool isDataViewable, DataTable table)
{
Debug.Assert(!isDataViewable || table != null, "isDataViewable bug table is null");
_enumerableRows = enumerableRows;
if (isDataViewable)
{
_table = table;
}
_listOfPredicates = new List>();
_sortExpression = new SortExpressionBuilder();
}
///
/// Basic Constructor
///
internal EnumerableRowCollection(DataTable table)
{
_table = table;
_enumerableRows = table.Rows.Cast();
_listOfPredicates = new List>();
_sortExpression = new SortExpressionBuilder();
}
///
/// 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
///
internal EnumerableRowCollection(EnumerableRowCollection source, IEnumerable enumerableRows, Func selector)
{
Debug.Assert(null != enumerableRows, "null enumerableRows");
_enumerableRows = enumerableRows;
_selector = selector;
if (null != source)
{
if (null == source._selector)
{
_table = source._table;
}
_listOfPredicates = new List>(source._listOfPredicates);
_sortExpression = source._sortExpression.Clone(); //deep copy the List
}
else
{
_listOfPredicates = new List>();
_sortExpression = new SortExpressionBuilder();
}
}
#endregion Constructors
#region PublicInterface
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
///
/// This method returns an strongly typed iterator
/// for the underlying DataRow collection.
///
///
/// A strongly typed iterator.
///
public IEnumerator GetEnumerator()
{
return _enumerableRows.GetEnumerator();
}
#endregion PublicInterface
///
/// Evaluates filter and sort if necessary and returns
/// a LinqDataView representing the LINQ query this class has collected.
///
/// LinqDataView repesenting the LINQ query
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 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 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 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