How to query a DataSet and iterate through the result? - c#

I have a DataSet which contains two tables, Publication and Owner, which are linked on Publication ID. How do I query the dataset? What I am trying to do is get all of the owners for a particular publication, and then I want to iterate over the resulting set, concatenate the owner names together and populate a label with the information...
But lets begin with, how do i query the dataset?
I also have a DataRelation, can I query that somehow to get the child rows for the current row?
Thanks.

ADO.NET supports two fundamental approaches for performing filtering and sorting of datasets:
The DataTable Select Method - This method is overloaded to accept arguments to filter and sort data rows returning an array of DataRow objects.
The DataView object sort, filter and find methods - This object uses the same filter arguments supported by the Select method, but the DataView exposes structures that can be bound to data-aware controls. See DataView.RowFilter
Iterating over filtered rows is as easy as:
DataTable dt;
...
foreach (DataRow dr in dt.Select(filter))
{
// ...
}
This article contains several examples: A Practical Guide to .NET DataTables, DataSets and DataGrids - Part 1

You could look into LINQ to Dataset, which allows you to perform queries against DataSets with multiple tables. You can perform joins between the tables on the appropriate columns amongst other things.

Related

How to avoid adding duplicate rows to a datatable

I'm using a SqlDataReader to add row by row into a datatable like follows:
while (reader.Read())
{
dataTable.LoadDataRow(reader.CurrentRow(), LoadOption.PreserveChanges);
}
This works, but I need to be able to avoid adding duplicate rows to the dataTable. I would love to be able to use the Contains or Find methods from the dataTable, but I can't find a way to turn the object[] from reader.CurrentRow() into a DataRow to compare to without adding it to a datatable.
I've looked into the option of making a hashset of the object[]s, and then adding them all at once to the datatable at the end, but I forgot that the default object IEqualityComparer only compares the reference.
Is there a feasible way of doing this without removing the duplicates at the end?
If removing the duplicates is the only way to go, what is the best way to do that?
EDIT:
I'm splitting distinct rows from the database into separate datatables in code. Each row from the query result is distinct, but sections of each row are not. Unfortunately I need to do exactly what my question is asking, as the results from the query are already distinct.
You didn't provide a ton of detail, but I hope this is comprehensive.
If you need a single column to be unique, then in your Columns collection in your datatable, specify the column like this:
DataTable appeals = new DataTable("Appeals");
appeals.Columns["PriorAppealNumber"].Unique = true;
DataColumn keyField = new DataColumn("AppealNumber", typeof(string));
appeals.Columns.Add(keyField);
If the uniqueness needs to span multiple rows, this is the method:
var myUniqueConstraint = new UniqueConstraint( new DataColumn[] {appeals.Columns[0], appeals.Columns[1], appeals.Columns[2]} );
appeals.Constraints.Add(myUniqueConstraint);
That will enforce the constraints BEFORE you try to commit back to the source database.
The easiest way is to actually make sure there are no duplicate rows at all - if you're querying relational database use DISTINCT - that will return only unique rows.

Can I access entire DataTable if all I have is a single DataRow?

DataRow contains a Table property, which seems to return the entire Table for which this row belongs.
I'd like to know if I can use that table safely, or if there are gotcha's.
In http://msdn.microsoft.com/en-us/library/system.data.datarow.table.aspx documentation, it says "A DataRow does not necessarily belong to any table's collection of rows. This behavior occurs when the DataRow has been created but not added to the DataRowCollection.", but I know for a fact my row belongs to a table.
In terms of pointers, if each Row from DataTable points to original DataTable, than I'm good to go. Is that all 'Table' property does?
Just to explain why I'm trying to get entire Table based on a single DataRow:
I'm using linq to join two (sometimes more) tables. I'd like to have a generic routine which takes the output of linq (var), and generate a single DataTable with all results.
I had opened another question at stackoverflow (Join in LINQ that avoids explicitly naming properties in "new {}"?), but so far there doesn't seem to be a generic solution, so I'm trying to write one.
if you know the row is part of table than yes you can access it without any problem. if the possibility exists where the row may not be associated to a table than check if the property is null.
if(row.Table == null)
{
}
else
{
}
As long as it's not null, you can use it freely.

sql query to fill dataset with multiple datatables based on specific column

I have a SQL Server table called tSongList that contains the following information:
colAlbumID, colSongName, colAlbumTrackNumber, colRequestedCount, colPlayPriority
The purpose of this table is to help a DJ keep a list of which songs the DJ should play and which album they are from. I have a C# class that will take a list of songs from a specific albumID and calculate the colPlayPriority based on the colRequestedCount. I have designed this class to take a DataTable containing the columns above and compute the necessary information.
So my question is if I want to use SQL to select all the rows from the tSongList, how do I get the SQL result into multiple DataTables grouped by colAlbumID? In other words, I want a DataTable for each Album that contains it's song information.
I know that I can use a SqlDataAdapter to fill a DataSet and since a DataSet can contain multiple DataTables, is there a way to construct a SQL query to return a DataSet containing the DataTables grouped by albumID?
Also if this can't be done, should I just select everything into one DataTable and use the Select function to get a DataRow array instead?
One option is to return a single DataTable and use LINQ to create your grouping.
var albumGroups = from a in dtAlbums.AsEnumerable()
group a by a.Field<int>("albumID") into g
select new { colSongName = g.Field<string>("colSongName"),
colAlbumTrackNumber = g.Field<int>("colAlbumTrackNumber"),
colRequestedCount = g.Field<int>("colRequestedCount"),
colPlayPriority = g.Field<int>("colPlayPriority") };
I think the example on MSDN will show you how to do this. You will make life much easier for yourself if you properly normalise your data first. You can create a hierarchical DataSet with the necessary relationships in place making it trivial to get all songs for an album.

How to share DataTable between multiple Datagridviews across Application and save precious memory

I have a list of companies in DataTable format which is static and accessible across my application.
Different Forms in the application just show filtered Data like Customer Companies ,Supplier Companies ,Misc etc in a DataGridView.
The DataGridViews are just for the sake of displaying the Data.
If I query my companies DataTable for only customer companies and assign the result to DatagridView.Datasource, it will just make a copy and assign it DataGridView.
Imagine if I have a list of 10,000 Customers and User open 10 forms for different purpose, I will be just duplicating 10,000 * 10 rows of data.
Also if my main Companies DataTable is Updated , I will have to update all the DataGridViews too.
Will using DataViews help me save the memory ?
Will BindingDatasource do good?
or is there any better method to share DataTable without making copies in memory for just Displaying Purpose.
Regards
Given:
I have a list of companies in
DataTable format which is static and
accessible across my application
I assume 1 DataTable. You should use RowFilter see MSDN: RowFilter:
DataGridView customerView;
DataGridView supplierView; // initialize in form
DataTable companiesTable; // initialized and filled
void SetCustomerCompanyView()
{
DataView cust = new DataView();
cust.Table = companiesTable;
cust.RowFilter = "Type = 'Customer'";
customerView.DataSource = cust;
}
// repeat for SetSupplierCompanyView()
If the concern is that one large DataTable is going to be copied throughout multiple active views, then you should really re-think the architecture. If a form is hidden or inactive, you can always dispose of the query results (in worst case) and re-query if/when the form becomes visible or active again (such as a tabbed form). For what it's worth, 10k records is easily handled in most systems and should accommodate several open views (filtered as above) of that DataTable.
An overtly simplistic comparison:
You can think of a DataView as a List<int> where int would be a row index. When you apply a row filter, the appropriate row indexes are appended to the List. You could easily create a view using LINQ to DataSets - extracting the row indexes you require based on a column or columns of data and append to the List. Now you use that list to reference only the rows (by Table.Rows[RowIndex]) in which you are interested from your primary DataTable.As stated, this is much simpler functionality than what the DataView is actually providing. You have not made a copy of the DataTable - you have simply created a shallow reference mechanism.

Join multiple DataRows into a single DataRow

I am writing this in C# using .NET 3.5. I have a System.Data.DataSet object with a single DataTable that uses the following schema:
Id : uint
AddressA: string
AddressB: string
Bytes : uint
When I run my application, let's say the DataTable gets filled with the following:
1 192.168.0.1 192.168.0.10 300
2 192.168.0.1 192.168.0.20 400
3 192.168.0.1 192.168.0.30 300
4 10.152.0.13 167.10.2.187 80
I'd like to be able to query this DataTable where AddressA is unique and the Bytes column is summed together (I'm not sure I'm saying that correctly). In essence, I'd like to get the following result:
1 192.168.0.1 1000
2 10.152.0.13 80
I ultimately want this result in a DataTable that can be bound to a DataGrid, and I need to update/regenerate this result every 5 seconds or so.
How do I do this? DataTable.Select() method? If so, what does the query look like? Is there an alternate/better way to achieve my goal?
EDIT: I do not have a database. I'm simply using an in-memory DataSet to store the data, so a pure SQL solution won't work here. I'm trying to figure out how to do it within the DataSet itself.
For readability (and because I love it) I would try to use LINQ:
var aggregatedAddresses = from DataRow row in dt.Rows
group row by row["AddressA"] into g
select new {
Address = g.Key,
Byte = g.Sum(row => (uint)row["Bytes"])
};
int i = 1;
foreach(var row in aggregatedAddresses)
{
result.Rows.Add(i++, row.Address, row.Byte);
}
If a performace issue is discovered with the LINQ solution I would go with a manual solution summing up the rows in a loop over the original table and inserting them into the result table.
You can also bind the aggregatedAddresses directly to the grid instead of putting it into a DataTable.
most efficient solution would be to do the sum in SQL directly
select AddressA, SUM(bytes) from ... group by AddressA
I agree with Steven as well that doing this on the server side is the best option. If you are using .NET 3.5 though, you don't have to go through what Rune suggests. Rather, use the extension methods for datasets to help query and sum the values.
Then, you can map it easily to an anonymous type which you can set as the data source for your grid (assuming you don't allow edits to this, which I don't see how you can, since you are aggregating the data).
I agree with Steven that the best way to do this is to do it in the database. But if that isn't an option you can try the following:
Make a new datatable and add the columns you need manually using DataTable.Columns.Add(name, datatype)
Step through the first datatables Rows collection and for each row create a new row in your new datatable using DataTable.NewRow()
Copy the values of the columns found in the first table into the new row
Find the matching row in the other data table using Select() and copy out the final value into the new data row
Add the row to your new data table using DataTable.Rows.Add(newRow)
This will give you a new data table containing the combined data from the two tables. It won't be very fast, but unless you have huge amounts of data it will probably be fast enough. But try to avoid doing a LIKE-query in the Select, for that one is slow.
One possible optimization would be possible if both tables contains rows with identical primary keys. You could then sort both tables and step through them fetching both data rows using their array index. This would rid you of the Select call.

Categories

Resources