I have a function that returns a typed DataRow object and now I would insert this DataRow in a given table. As far as I know I could do this using TableAdapter in two ways: TableAdapter.Update and TableAdapter.Insert.
Both ways seems pretty simple to use but, first way could even delete row from the table (essentially my DataSet will be "serialized" on my DB table) and since this table is really the application's critical part I would avoid any chance of data deletion, so I would prefear Insert method.
The problem seems to be that Insert method doesn't accept a DataRow object as it's parameter (unlike Update), so I need to manually insert each parameters. Alas I have at least 80 parameters so this is a really really headache operation. What can I do now?
The update method does allow you to insert,update or delete changes in the tableAdapter. MSDN: 'Use the TableAdapter.Update method when your application uses datasets to store data. The Update method sends all changes (updates, inserts, and deletes) to the database'. Although Insert is ment for usage if you want more control for you data inserts but does not allow you to pass a datarow instead Parameters must be used. See complete reference on MSDN: http://msdn.microsoft.com/en-us/library/ms233812(v=vs.110).aspx
Code example of Update method usage.
// Create a new row.
NorthwindDataSet.RegionRow newRegionRow;
newRegionRow = northwindDataSet.Region.NewRegionRow();
newRegionRow.RegionID = 5;
newRegionRow.RegionDescription = "NorthWestern";
// Add the row to the Region table
this.northwindDataSet.Region.Rows.Add(newRegionRow);
// Save the new row to the database
this.regionTableAdapter.Update(this.northwindDataSet.Region);
Code example Insert:
NorthwindDataSetTableAdapters.RegionTableAdapter regionTableAdapter =
new NorthwindDataSetTableAdapters.RegionTableAdapter();
regionTableAdapter.Insert(5, "NorthWestern");
Related
I have DataGridView where I am showing data read from database:
DataSet ds = new DataSet();
sqlDa.Fill(ds);
dgView.DataSource = ds.Tables[0]
After adding all of the rows in the UI, I need to to SQL UPDATE of rows that previously read from database, and do INSERT for new rows by clicking Save button (I don't save rows one by one when adding, just all of them when I click the Save button):
foreach (DataGridViewRow dgvRow in dgView.Rows)
{
// do insert for new rows, and update for existing ones from database
}
How can I know what rows are newly added and what are not? Can I add some type of attribute to every row that is read from database so that can I know that they need to be updated?
How can I know what rows are newly added and what are not?
You don't need to; the datatable the DGV is showing is already tracking this. If you make a SqlDataAdapter and plug a SqlCommandBuilder into it see the example code in the docs so that it gains queries in its InsertCommand/UpdateCommand/DeleteCommand properties (or you can put these commands in yourself, but there isn't much point given that a command builder can make them automatically) then you just say:
theDataAdapter.Update(theDataTable);
If you didn't save it anywhere else you can get it from the DataSource of the DGV:
theDataAdapter.Update(dgView.DataSource as DataTable);
Ny the way, the word "Update" here is nothing to do with an update query; Microsoft should have called it SaveChanges. It runs all kinds of modification query (I/U/D) not just UPDATE
If you really want to know, and have a burning desire to reinvent this wheel, you can check a DataRow's RowState property, and it will tell you if it's Added, Modified or Deleted, so you can fire the appropriate query (but genuinely you'd be reimplementing functionality that a SqlDataAdapter already has built in)
All this said, you might not be aware that you can make your life massively easier by:
Add a new DataSet type of file to your project (like you would add a class). Open it
Right-click in the surface of it, choose add TableAdapter
Design your connection string in (once)
Enter your query as a "select that produces rows" like SELECT * FROM SomeTable WHERE ID = Id (it's advisable to use a where clause that selects on the ID; you can add more queries later to do other things, like SELECT * FROM SomeTable WHERE SomeColumn LIKE #someValue but for now selecting on ID gives you a base query to use that is handy for loading related data). You can also use existing or new stored procs if you want
Give it a sensible name pair like FillById, GetDataById - FillBy fills an existing table, Get gets a new one
Finish
You'll now have objects available in your code that are wrappers data adapters and datatables - same functionality but more nicely strongly typed
e.g. you can fill your grid with:
var ta = new SomeTableAdapter();
dgView.DataSource = ta.GetDataByFirstName("John%"); //does select * from table where firstname like 'john%' into a datatable
The datatables are strongly typed, so you don't access them like this:
//no
foreach(DataRow row in someTable.Rows){
if((row["someColumn"] as string) == "hello" && row.IsNull("otherColumn"))
row["otherColumn"] = "goodbye";
}
You have named properties:
//yes
foreach(var row in someTable){
if((row.SomeColumn == "hello" && row.IsOtherColumnNull())
row.OtherColumn = "goodbye";
}
Much nicer. LINQ works on them too, without AsEnumerable or Cast and endless casting the values.
It's not magic; VS writes boatloads of code behind the scenes for you - check in the YourDataSet.Designer.cs file - hundreds of SqlCommands, fully parameterized, for all the table operations (Select/Insert/Update/Delete), all base don typing a SELECT command into a tool pane. It's quite nice to use really, even all these years later.
Oh, but the designer doesn't work very nicely in net core. They're really lagging behind on fixing up the bugs that netcore brings (other priorities)
Why is the DataTable not empty even after it has been cleared?
In the beginning I cleared the the "PasswoerterDataTable". But afte updating and filling it back it is not empty anymore. Did I misunderstood the usage of the .fill and .update commands? How do I correct my mistake so that the "PasswoerterDataTable" is empty in the end?
PasswoerterDataTable.Rows.Clear();
// DataTable is now empty
PasswoerterTableAdapter.Update(PasswoerterDataTable);
PasswoerterTableAdapter.Fill(PasswoerterDataTable);
// DataTable is not empty anymore. Why?
I know that it does not make sense to use .update and .fill after each other but I am currently working on a PasswordManager and I want to delete Passwords. These deleted Passwords should not appaer when the program is closed and started again but I need to use .fill after Startup to get the Passwords which haven't been deleted. But after I used .fill the Passwords reappear.
The Fill method exists to read data from a database table and put it inside an in memory DataTable object. I think you are misunderstanding the work of the Clear method and the Update method.
The Clear method removes all rows from the in memory datatable but doesn't change anything on the database table. The Update method looks at the rows present in your PasswoerterDataTable and checks their RowState property. If it finds something to update then a command is executed to reflect the changes back to the database.
But you have cleared the rows from PasswoerterDataTable so Update has nothing to update and your database table is left unchanged. The following Fill reloads everything.
If you want Update to execute delete commands on the database you need to set the RowState of each row to Deleted. And you can do this with
foreach(DataRow row in PasswoerterDataTable.Rows)
row.Delete(); // This doesn't remove the row, but sets its RowState to Deleted
// Now the adapter knows what to do...
PasswoerterTableAdapter.Update();
Of course this is totally unnecessary and you can simply write a single Command to act directly on your database to TRUNCATE your table
SqlCommand cmd = new SqlCommand("TRUNCATE TABLE PasswoerterTable", connection);
cmd.ExecuteNonQuery();
PasswoerterDataTable.Rows.Clear();
// No need to call Fill again.
I have a table within my program which stores information about messages and would like to detect when new information has been added to it. The reason I am doing this is beucause I would like to show the new data to the user only when there is new data instead of having to constantly get all the rows and display them.
The way I decided to do this was through the use of the dataSet.HasChanged() function which should essentially check the dataset for any new rows and a function called DataChanged returns dataSet.HasChanged() value.
However, the function I am using always returns false (even when there are changes)
Here is the function...
public bool DataChanged(string Table)
{
//This is the variable that will be returned
bool ChangesMade;
//Create the adapter
OleDbDataAdapter adapter = new OleDbDataAdapter(Table, connector);
//Clear the current data in the dataset
dataSet.Clear();
//Open the connection and fill the dataset
connector.Open();
adapter.Fill(dataSet, "TableData1");
connector.Close();
return ChangesMade = dataSet.HasChanges();
}
Changes for some reason are never detected and therefore this function always returns false even after I add a new record to the dataset.
An alternative method that provides the functionality explained in paragraph one would be very helpful and the fixing of my current method ever more so.
Here is an easy way to do this.
Make sure a Timestamp/Rowversion column exists for each table you wish to track changes to.
Return the current database Timestamp to your calling program as part of your data result set (DataSet in this case). For example, add the following query to your result set.
SELECT ##DBTS;
Use this returned value the next time you run your query with the following added to the existing WHERE clause(s) as appropriate.
...
AND (#LastRowversionValue IS NULL
OR TableName.RowversionColumn > #LastRowversionValue)
...
Pass NULL for the #LastRowversionValue the first time the query is ran to get the process started.
Any rows that are inserted/updated into the table(s) since the last time you retrieved data, will have an Rowversion greater than the one you stored from the last execution.
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.
I have two mirrored datatables (same structure with two primary keys) :
DataTable_A ---> bound to a datagridView
DataTable_B ---> filled from a database
Since DataTable_B is filled by a query into database every 2 seconds, I need to mirror the DataTable_A like DataTable_B avoiding filling directly DataTable_A. When a record disappears from DataTable_B i need to delete the record also from DataTable_A. What is the best way to do this ?
Right now I am doing a "for cycle" on each row of DataTable_B and if the row doesn't exist on DataTable_A, I delete it.
Is there a better way to do it ?
The best way may be not to have a TableA at all but use a DataView on TableB. That would solve all problems at once. Can you elaborate on why you need the copy?
But otherwise you would want to handle the RowChanged and TableNewRow RowDeleted event of TableB
A more general idea, after seeing your comments: If it is possible to add a Timestamp column to the table in the database you can run a much more efficient query. And the DataTable.Merge method would do the rest.