1 //------------------------------------------------------------------------------
2 // <copyright file="LinqDataView.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">Microsoft</owner>
7 //------------------------------------------------------------------------------
9 using System
.Collections
;
10 using System
.Collections
.Generic
;
12 using System
.Diagnostics
;
13 using System
.ComponentModel
;
16 using System
.Data
.DataSetExtensions
;
21 /// Represents a bindable, queryable DataView of DataRow, that can be created from from LINQ queries over DataTable
22 /// and from DataTable.
24 internal class LinqDataView
: DataView
, IBindingList
, IBindingListView
27 /// A Comparer that compares a Key and a Row.
29 internal Func
<object, DataRow
, int> comparerKeyRow
; // comparer for DataView.Find(..
32 /// Builds the sort expression in case multiple selector/comparers are added
34 internal readonly SortExpressionBuilder
<DataRow
> sortExpressionBuilder
;
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.
40 /// <param name="table">The input table from which LinkDataView is to be created.</param>
41 internal LinqDataView(DataTable table
, SortExpressionBuilder
<DataRow
> sortExpressionBuilder
)
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.
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.
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(
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
)
77 DataViewRowState
.CurrentRows
)
79 this.sortExpressionBuilder
= (sortExpressionBuilder
== null) ? this.sortExpressionBuilder
: sortExpressionBuilder
;
80 this.comparerKeyRow
= comparerKeyRow
;
84 /// Gets or sets the expression used to filter which rows are viewed in the LinqDataView
86 public override string RowFilter
90 if (base.RowPredicate
== null)//using string based filter or no filter
92 return base.RowFilter
;
94 else //using expression based filter
104 base.RowPredicate
= null;
105 base.RowFilter
= String
.Empty
; //INDEX rebuild twice
109 base.RowFilter
= value;
110 base.RowPredicate
= null;
118 /// Searches the index and finds a single row where the sort-key matches the input key
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>();
149 Range range
= FindRecords
<object, DataRow
>(compareDelg
, keyList
);
151 return (range
.Count
== 0) ? -1 : range
.Min
;
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.
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
);
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
;
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.
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
);
219 Range range
= FindRecords
<object, DataRow
>(
220 new Index
.ComparisonBySelector
<object, DataRow
>(comparerKeyRow
),
221 new List
<object>(key
));
222 return base.GetDataRowViewFromRange(range
);
228 #region Misc Overrides
230 /// Overriding DataView's SetIndex to prevent users from setting RowState filter to anything other
231 /// than CurrentRows.
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
);
243 base.SetIndex(newSort
, newRowStates
, newRowFilter
);
252 /// Clears both expression-based and DataView's string-based sorting.
254 void IBindingList
.RemoveSort()
256 base.Sort
= String
.Empty
;
257 base.SortComparison
= null;
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
264 PropertyDescriptor IBindingList
.SortProperty
268 return (base.SortComparison
== null) ? base.GetSortProperty() : null;
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
276 ListSortDescriptionCollection IBindingListView
.SortDescriptions
280 if (base.SortComparison
== null)
282 return base.GetSortDescriptions();
286 return new ListSortDescriptionCollection();
292 /// Tells whether the LinqDataView is sorted or not
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);