0

I have the following code, which does work:

var dataSource = (from p in dv.ToTable().AsEnumerable() where filter(p) select p).AsDataView();

filter is a Func<DataRow, bool>
dv is a DataView
dataSource is being used as a DataSource for a DataGrid.

Anyhow, it strikes me as a bit ugly that I am calling ToTable, AsEnumerable, and AsDataView, so I was wondering if there is a way to reduce the number of calls.

Is this as simple as I can make it?

Edit: The DataGrid has pagination, and I make use of the dataSource to determine the total number of entries. I'm not especially worried about the efficiency of this; dv only has a few thousand items and the table is being maintained in memory.

Brian
  • 25,523
  • 18
  • 82
  • 173

2 Answers2

6

Well for one thing, I'd say that using a query expression is somewhat clumsy here. Your code is equivalent to:

var dataSource = dv.ToTable()
                   .AsEnumerable()
                   .Where(filter)
                   .AsDataView();

which I would say is clearer.

Another alternative would be:

var dataSource = dv.Cast<DataRowView>()
                   .Select(rowView => rowView.Row)
                   .Where(filter)
                   .ToList();

This avoids building a DataTable, so may well be more efficient too (it'll just stream the DataRowViews from the view and select their underlying DataRows) but builds a List<DataRow> at the end. On the other hand, it's now not acting on the view itself, really - because it's just selecting the underlying rows. This may or may not do what you need, depending on what your view is doing.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Your second method gives a compiler error, "`Error 2 'System.Collections.Generic.IEnumerable' does not contain a definition for 'AsDataView'"`. I'm using the dataSource to get a total count of items, so in terms of efficiency I'll need to read through all the data regardless. Further, my DataGrid is paginated and thus isn't happy with anything that doesn't implement `ICollection`. – Brian Mar 08 '11 at 19:31
  • 1
    @Brian: If the second approach gives a compiler error, so should your original source code... You'll need to read through all the data regardless, but you don't need to *copy* it all. How about just calling `ToList()` instead of `AsDataView`? – Jon Skeet Mar 08 '11 at 19:37
  • @Brian: Ah, I see how the first code was working. I didn't realise there were a bunch of extension methods on `EnumerableRowCollection`. That's horrible :( I've changed the second example to just call `ToList` instead, as I think that's all you'd need. – Jon Skeet Mar 08 '11 at 19:39
  • Well, now that you changed it to `ToList()`, the compiler error is avoided. Though now the second alternative gives a runtime error, `Unable to cast object of type 'System.Data.DataRowView' to type 'System.Data.DataRow'.` The first version has worked all along, which isn't surprising, since, as you pointed out, it is equivalent to my original code. – Brian Mar 08 '11 at 19:43
  • @Brian: Right. Will edit again... I'd hoped that each row in a DataView would be a DataRow, but it's not. Hang on... – Jon Skeet Mar 08 '11 at 20:01
  • @Brian: Okay, the latter version will *work*, but it may not do what you want. It depends on what your view is doing. Basically this is giving you the raw rows underlying the view, instead of the projected rows. – Jon Skeet Mar 08 '11 at 20:02
  • @Jon: Yup, it "works." Though not in a useful way, since `List` doesn't have the headings that the DataGrid is looking for. At this point, it's reached the point where it's a bit more complicated than the first alternative, even if it *did* work. Ah well, accepting. Your/Lee's point about not using a query expression to make it cleaner is definitely a good one. – Brian Mar 08 '11 at 20:07
  • @Brian: Ultimately, you might find you'd be better off using plain old .NET objects and collections instead of DataView/DataTable. Either that, or use strongly-typed data sets which play better with LINQ anyway. – Jon Skeet Mar 08 '11 at 20:09
3

It looks like you want:

var dataSource = dv.ToTable().AsEnumerable().Where(filter);

AsEnumerable is required for DataTable and returns an IEnumerable<DataRow> which DataTable does not implement.

Lee
  • 142,018
  • 20
  • 234
  • 287