Search code examples
.netdatatablearchitectureado.netdataview

What is the best way to use a filtered version of a DataTable?


I have a DataSet containing 4 DataTables, say dt1, dt2, dt3, dt4. I also have 3 DataRelations like this:

|Parent|Child|
|  d1  |  d2 |
|  d2  |  d3 |
|  d2  |  d4 |

My MasterClass passes this DataSet to several classes, each class needs to perform different actions using the DataSet. However, before passing the DataSet, MasterClass needs to perform a filter on dt1, as each class needs to work with the filtered version of dt1. Note that I can't update dt1, as I need to perform different filters on the same DataTable. In the picture below, I try to visualize the structure:

The Architecture

My question is: What is the best way to pass the filtered dt1 to Class A..B?

The first solution that I thought of, was to create a copy of dt1 in my DataSet, and store the filtered version in it, say FilteredDt1, however, I don't want to have redundant data / DataRows in memory.

From what I've read in docs from Microsoft, I think that I can do better with the help of a DataView, in a way that I pass the DataView as a second parameter to the classes, but I didn't get how I can do it, also from software architecture point of view.

Problems that I met:

  • Is it still possible to use the DataRelations, if I use a DataView?
  • Is it possible to run a join on the DataView and a DataTable?

I am rather new to ADO.NET, and I'd appretiate it very much, if you correct me, if there are any mistakes / incorrect assumptions in my question.


Solution

  • You could use linq:

    var filtered1 = MyDataSet.Tables["d1"].Rows.OfType<DataRow>()
                    .Where(r => r["Column"] = value);
    
    var filtered2 = MyDataSet.Tables["d1"].Rows.OfType<DataRow>()
                    .Where(r => r["OtherColumn"] = differentValue);
    

    Now filtered1 will return an IEnumerable<DataRow> where Column = value,
    And filtered2 will return an IEnumerable<DataRow> where OtherColumn = differentValue,

    And your original data table is left unchanged.

    Note that I had to use the OfType extension method before I could use the Where extension method, since the Rows property is of type DataRowCollection that does not implement the generic IEnumerable<T> interface, only the not-generic IEnumerable (and ICollection) interface.

    Update:

    Looking for a way to return a filtered data table without changing the source data table I came across the AsEnumerable extension method for the DataTable class, which returns an IEnumerable<DataRow>(), so my suggestion can be simplified to ...MyDataSet.Tables["d1"].AsEnumerable().Where....

    Also, I've found a way for you to create copies of the data table with only the filtered rows. What you do is use the DataView constructor that accepts DataTable, RowFilter, Sort, and DataViewRowState, and then call it's ToTable method to return a new data table.
    Please note that this will create new copies for the data rows in memory, since a DataRow can only belong to a single DataTable.

    However, Please note I would advise working with IEnumerable<T>, where T is specific types you creates as DTOs rather then working with directly DataTable. That will make your life much safer and easier.