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