Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Data.DataSetExtensions / System / Data / LinqDataView.cs
blob13f891636e0f384961828fffc9219946a9a7aad0
1 //------------------------------------------------------------------------------
2 // <copyright file="LinqDataView.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">Microsoft</owner>
7 //------------------------------------------------------------------------------
8 using System;
9 using System.Collections;
10 using System.Collections.Generic;
11 using System.Linq;
12 using System.Diagnostics;
13 using System.ComponentModel;
14 using System.Data;
15 using System.Text;
16 using System.Data.DataSetExtensions;
18 namespace System.Data
20 /// <summary>
21 /// Represents a bindable, queryable DataView of DataRow, that can be created from from LINQ queries over DataTable
22 /// and from DataTable.
23 /// </summary>
24 internal class LinqDataView : DataView, IBindingList, IBindingListView
26 /// <summary>
27 /// A Comparer that compares a Key and a Row.
28 /// </summary>
29 internal Func<object, DataRow, int> comparerKeyRow; // comparer for DataView.Find(..
31 /// <summary>
32 /// Builds the sort expression in case multiple selector/comparers are added
33 /// </summary>
34 internal readonly SortExpressionBuilder<DataRow> sortExpressionBuilder;
36 /// <summary>
37 /// Constructs a LinkDataView and its parent DataView.
38 /// Does not create index on the DataView since filter and sort expressions are not yet provided.
39 /// </summary>
40 /// <param name="table">The input table from which LinkDataView is to be created.</param>
41 internal LinqDataView(DataTable table, SortExpressionBuilder<DataRow> sortExpressionBuilder)
42 : base(table)
44 Debug.Assert(table != null, "null DataTable");
45 this.sortExpressionBuilder = sortExpressionBuilder ?? new SortExpressionBuilder<DataRow>();
49 //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
50 // data view's constructor. That logic for checking null can't be embedded in the base constructor call.
51 /// <summary>
52 ///
53 /// </summary>
54 /// <param name="table">Table from which to create the view</param>
55 /// <param name="predicate_func">User-provided filter-predicate as a Func<DataRow>, bool>"/></param>
56 /// <param name="predicate_system">User-provided predicate but in the form of System.Predicate<DataRow>
57 /// Predicates are being replicated in different forms so that nulls can be passed in.
58 /// For e.g. when user provides null predicate, base.Predicate should be set to null. I cant do that in the constructor initialization
59 /// if I will have to create System.Predicate delegate from Func.
60 /// </param>
61 /// <param name="comparison">The comparer function of DataRow to be used for sorting. </param>
62 /// <param name="comparerKeyRow">A comparer function that compares a Key value to DataRow.</param>
63 /// <param name="isDescending">Whether sorting is ascending or descending.</param>
64 /// <param name="rowState">Row state filter. For the purpose of LinkDataView it should always be CurrentRows.</param>
65 internal LinqDataView(
66 DataTable table,
67 Func<DataRow, bool> predicate_func,
68 Predicate<DataRow> predicate_system,
69 Comparison<DataRow> comparison,
70 Func<object, DataRow, int> comparerKeyRow,
71 SortExpressionBuilder<DataRow> sortExpressionBuilder)
73 //Parent constructor
74 : base(table,
75 predicate_system,
76 comparison,
77 DataViewRowState.CurrentRows)
79 this.sortExpressionBuilder = (sortExpressionBuilder == null) ? this.sortExpressionBuilder : sortExpressionBuilder;
80 this.comparerKeyRow = comparerKeyRow;
83 /// <summary>
84 /// Gets or sets the expression used to filter which rows are viewed in the LinqDataView
85 /// </summary>
86 public override string RowFilter
88 get
90 if (base.RowPredicate == null)//using string based filter or no filter
92 return base.RowFilter;
94 else //using expression based filter
96 return null;
102 if (value == null)
104 base.RowPredicate = null;
105 base.RowFilter = String.Empty; //INDEX rebuild twice
107 else
109 base.RowFilter = value;
110 base.RowPredicate = null;
115 #region Find
117 /// <summary>
118 /// Searches the index and finds a single row where the sort-key matches the input key
119 /// </summary>
120 /// <param name="key">Value of the key to find</param>
121 /// <returns>Index of the first match of input key</returns>
122 internal override int FindByKey(object key)
124 /////////////// Preconditions ////////////////
125 //check that both string and expression based sort are never simultaneously set
126 Debug.Assert(base.Sort != null);
127 Debug.Assert(!(!String.IsNullOrEmpty(base.Sort) && base.SortComparison != null));
128 /////////////////////////////////////////////
130 if (!String.IsNullOrEmpty(base.Sort)) //use find for DV's sort string
132 return base.FindByKey(key);
134 else if (base.SortComparison == null) //neither string or expr set
136 //This is the exception message from DataView that we want to use
137 throw ExceptionBuilder.IndexKeyLength(0, 0);
139 else //find for expression based sort
141 if (sortExpressionBuilder.Count !=1)
142 throw DataSetUtil.InvalidOperation(Strings.LDV_InvalidNumOfKeys(sortExpressionBuilder.Count));
144 Index.ComparisonBySelector<object, DataRow> compareDelg =
145 new Index.ComparisonBySelector<object, DataRow>(comparerKeyRow);
147 List<object> keyList = new List<object>();
148 keyList.Add(key);
149 Range range = FindRecords<object, DataRow>(compareDelg, keyList);
151 return (range.Count == 0) ? -1 : range.Min;
155 /// <summary>
156 /// Since LinkDataView does not support multiple selectors/comparers, it does not make sense for
157 /// them to Find using multiple keys.
158 /// This overriden method prevents users calling multi-key find on dataview.
159 /// </summary>
160 internal override int FindByKey(object[] key)
162 //---------Checks ----------------
163 //must have string or expression based sort specified
164 if (base.SortComparison == null && String.IsNullOrEmpty(base.Sort))
166 //This is the exception message from DataView that we want to use
167 throw ExceptionBuilder.IndexKeyLength(0, 0);
169 else if (base.SortComparison != null && key.Length != sortExpressionBuilder.Count)
171 throw DataSetUtil.InvalidOperation(Strings.LDV_InvalidNumOfKeys(sortExpressionBuilder.Count));
173 //--------------------------------
175 if (base.SortComparison == null)//using string to sort
176 return base.FindByKey(key);
177 else
179 Index.ComparisonBySelector<object, DataRow> compareDelg =
180 new Index.ComparisonBySelector<object, DataRow>(comparerKeyRow);
182 List<object> keyList = new List<object>();
183 foreach (object singleKey in key)
185 keyList.Add(singleKey);
187 Range range = FindRecords<object, DataRow>(compareDelg, keyList);
188 return (range.Count == 0) ? -1 : range.Min;
193 /// <summary>
194 /// Searches the index and finds rows where the sort-key matches the input key.
195 /// Since LinkDataView does not support multiple selectors/comparers, it does not make sense for
196 /// them to Find using multiple keys. This overriden method prevents users calling multi-key find on dataview.
197 /// </summary>
198 internal override DataRowView[] FindRowsByKey(object[] key)
200 //---------Checks ----------------
201 //must have string or expression based sort specified
202 if (base.SortComparison == null && String.IsNullOrEmpty(base.Sort))
204 //This is the exception message from DataView that we want to use
205 throw ExceptionBuilder.IndexKeyLength(0, 0);
207 else if (base.SortComparison != null && key.Length != sortExpressionBuilder.Count)
209 throw DataSetUtil.InvalidOperation(Strings.LDV_InvalidNumOfKeys(sortExpressionBuilder.Count));
211 //--------------------------------
213 if (base.SortComparison == null)//using string to sort
215 return base.FindRowsByKey(key);
217 else
219 Range range = FindRecords<object, DataRow>(
220 new Index.ComparisonBySelector<object, DataRow>(comparerKeyRow),
221 new List<object>(key));
222 return base.GetDataRowViewFromRange(range);
225 #endregion
228 #region Misc Overrides
229 /// <summary>
230 /// Overriding DataView's SetIndex to prevent users from setting RowState filter to anything other
231 /// than CurrentRows.
232 /// </summary>
233 internal override void SetIndex(string newSort, DataViewRowState newRowStates, IFilter newRowFilter)
235 //Throw only if expressions (filter or sort) are used and rowstate is not current rows
236 if ( (base.SortComparison != null || base.RowPredicate != null)
237 && newRowStates != DataViewRowState.CurrentRows)
239 throw DataSetUtil.Argument(Strings.LDVRowStateError);
241 else
243 base.SetIndex(newSort, newRowStates, newRowFilter);
247 #endregion
249 #region IBindingList
251 /// <summary>
252 /// Clears both expression-based and DataView's string-based sorting.
253 /// </summary>
254 void IBindingList.RemoveSort()
256 base.Sort = String.Empty;
257 base.SortComparison = null;
260 /// <summary>
261 /// Overrides IBindingList's SortProperty so that it returns null if expression based sort
262 /// is used in the LinkDataView, otherwise it defers the result to DataView
263 /// </summary>
264 PropertyDescriptor IBindingList.SortProperty
268 return (base.SortComparison == null) ? base.GetSortProperty() : null;
272 /// <summary>
273 /// Overrides IBindingList's SortDescriptions so that it returns null if expression based sort
274 /// is used in the LinkDataView, otherwise it defers the result to DataView
275 /// </summary>
276 ListSortDescriptionCollection IBindingListView.SortDescriptions
280 if (base.SortComparison == null)
282 return base.GetSortDescriptions();
284 else
286 return new ListSortDescriptionCollection();
291 /// <summary>
292 /// Tells whether the LinqDataView is sorted or not
293 /// </summary>
294 bool IBindingList.IsSorted
297 { //Sorted if either expression based sort or string based sort is set
298 return !(base.SortComparison == null && base.Sort.Length == 0);
302 #endregion