I have the code below:
string SQL = "select * from " + TableName;
using (DS = new DataSet())
using (SqlDataAdapter adapter = new SqlDataAdapter())
using (SqlConnection sqlconn = new SqlConnection(connectionStringBuilder.ToString()))
using (SqlCommand objCommand = new SqlCommand(SQL, sqlconn))
{
sqlconn.Open();
adapter.SelectCommand = objCommand;
adapter.Fill(DS);
}
System.Windows.Forms.MessageBox.Show(DS.Tables[0].TableName);
return DS;
However, every time I run this code, the dataset (DS) is filled with one table called "Table". It does not represent the table name I pass in as the parameter TableName and this parameter does not get mutated so I don't know where the name Table comes from. I'd expect the table to be the same as the tableName parameter I pass in?
Any idea why this is not so?
EDIT: Important fact: This code needs to return a dataset because I use the dataRelation object in another method, which is dependent on this, and without using a dataset, that method throws an exception. The code for that method is:
DataRelation PartToIntersection = new DataRelation("XYZ",
this.LoadDataToTable(tableName).Tables[tableName].Columns[0],
// Treating the PartStat table as the parent - .N
this.LoadDataToTable("PartProducts").Tables["PartProducts"].Columns[0]);
// 1
// PartsProducts (intersection) to ProductMaterial
DataRelation ProductMaterialToIntersection =
new DataRelation("", ds.Tables["ProductMaterial"].Columns[0],
ds.Tables["PartsProducts"].Columns[1]);
Thanks
The default name for the first table in the DataSet is Table, the second one will be called Table1 and the third Table2 and so forth. The DataSet will not read the table name from the underlying store - and there's no option to make it do so.
This is documented behavior - see the MSDN documentation:
If the DataAdapter encounters multiple
result sets, it creates multiple
tables in the DataSet. The tables are
given an incremental default name of
TableN, starting with "Table" for
Table0. If a table name is passed as
an argument to the Fill method, the
tables are given an incremental
default name of TableNameN, starting
with "TableName" for TableName0.
If you want other names, you have to supply those - use this statement:
adapter.Fill(DS, Tablename);
This will name the newly filled table Tablename inside the DataSet.
The DataTable.TableName is just the name associated with the runtime structure and is independent of the data source used to create the DataTable.
Related
I have been trying to use OleDbDataAdapter to update a DataTable but got confused about the commands.
Since I sometimes get info from diffrent tables I can't use a CommandBuilder.
So I have tried to create the commands on my on but found it hard with the parameters.
DataTable.GetChanges returns rows that needs to use an INSERT or an UPDATE command - I guess I can't distinct between them.
I need you to complete the following:
DataTable dt = new DataTable();
OleDbDataAdapter da = new OleDbDataAdapter();
// Here I create the SELECT command and pass the connection.
da.Fill(dt);
// Here I make changes (INSERT/UPDATE) to the DataTable (by a DataGridView).
da.UpdateCommand = new OleDbCommand("UPDATE TABLE_NAME SET (COL1, COL2, ...) VALUES (#newVal1, #newVal2, ...) WHERE id=#id"); // How can I use the values of the current row (that the da is updating) as the parameters (#newVal1, #newVal2, id....)?
Thank you very much!
The data adapter can work in conjunction with the datatable. As such, I've actually wrapped mine together into a class and works quite well. Aside from the complexities of my stuff, here's a snippet that might help you along. When adding a parameter, you can identify the column source that the data is coming from FROM the DataTable. This way, when a record is internally identified as "Added" or "Updated" (or "Deleted"), when you build your SQL Insert/Update/Delete commands, it will pull the data from the columns from the respective rows.
For example. Say I have a DataTable, primary Key is "MyID" and has columns "ColX, ColY, ColZ". I create my DataAdapter and build out my select, update, delete commands something like... (? is a place-holder for the parameters)
DataAdapter myAdapter = new DataAdapter()
myAdapter.SelectCommand = new OleDbCommand();
myAdapter.InsertCommand = new OleDbCommand();
myAdapter.UpdateCommand = new OleDbCommand();
myAdapter.DeleteCommand = new OleDbCommand();
myAdapter.SelectCommand.CommandText = "select * from MyTable where MyID = ?";
myAdapter.InsertCommand.CommandText = "insert into MyTable ( ColX, ColY, ColZ ) values ( ?, ?, ? )";
myAdapter.UpdateCommand.CommandText = "update MyTable set ColX = ?, ColY = ?, ColZ = ? where MyID = ?";
myAdapter.DeleteCommand.CommandText = "delete from MyTable where MyID = ?";
Now, each has to have their respective "Parameters". The parameters have to be addded in the same sequence as their corresponding "?" place-holders.
// Although I'm putting in bogus values for preparing the parameters, its just for
// data type purposes. It does get changed through the data adapter when it applies the changes
OleDbParameter oParm = new OleDbParameter( "myID", -1 );
oParm.DbType = DbType.Int32;
oParm.SourceColumn = "myID"; // <- this is where it looks back to source table's column
oParm.ParameterName = "myID"; // just for consistency / readability reference
myAdapter.SelectCommand.Parameters.Add( oParm );
do similar for rest of parameters based on their types... char, int, double, whatever
Again, I have like a wrapper class that handles managment on a per-table basis... in brief
public myClassWrapper
{
protected DataTable myTable;
protected DataAdapter myAdapter;
... more ...
protected void SaveChanges()
{
}
}
Its more complex than just this, but during the "SaveChanges", The datatable and dataAdapter are in synch for their own purposes. Now, flushing the data. I check for the status of the table and then you can pass the entire table to the dataAdapter for update and it will cycle through all changed records and push respective changes. You'll have to trap for whatever possible data errors though.
myAdapter.Update( this.MyTable );
As it finds each "changed" record, it pulls the values from the Column Source as identified by the parameter that is found in the table being passed to the adapter for processing.
Hopefully this has given you a huge jump on what you are running into.
---- COMMENT PER FEEDBACK ----
I would put your update within a try/catch, and step into the program to see what the exception is. The message adn/or inner exception of the error might give more info. However, try to simplify your UPDATE to only include a FEW fields with the WHERE "Key" element.
Additionally, and I oopsed, missed this from first part answer. You might have to identify the datatable's "PrimaryKey" column. To do so, its a property of the DataTable that expects and array of columns that represent the primary key for the table. What I did was...
// set the primary key column of the table
DataColumn[] oCols = { myDataTbl.Columns["myID"] };
myDataTbl.PrimaryKey = oCols;
I would comment out your full update string and all its parameters for your UPDATE. Then, build it with just as simple as my sample of only setting 2-3 columns and the where clause
myAdapter.UpdateCommand.CommandText = "update MyTable set ColX = ?, ColY = ? where MyID=?";
Add Parameter object for "X"
Add Parameter object for "Y"
Add Parameter object for "MyID"
Pick fields like int or char so they have the least probability of problems for data type conversions, then, once that works, try adding all your "int" and "character" columns... then add any others. Also, which database are you going against. SOME databases don't use "?" as placeholder in the command but use "named" parameters, some using
"actualColumn = #namedCol"
or even
"actualColumn = :namedCol"
Hope this gets you over the hump...
You could use the String.Format Method to replace the #newVal1, #newVal2, ... in your code, like this da.UpdateCommand = new OleDbCommand(String.Format("UPDATE TABLE_NAME SET (COL1, COL2, ...) VALUES ({0}, {1}, ...) WHERE id=#id",OBJECT_ARRAY_CONTAINING_VALUES_FROM_THEDG));
[Eidt per comment]
To handle the row[0], row[1] you need a loop like:
for(i=0; i<rows.Count; i++)
{
da.UpdateCommand = new OleDbCommand(String.Format("UPDATE...",row[i]);
da.Update(dt);
}
Below is a snippet of the code. As you can see, that method returns a table from SQLite database, and adds that table to a DataSet if it doesn't exist yet.
SQLiteConnection connection;
DataSet Set = new DataSet();
DataTable GetTable(string tableName, string command)
{
if (!Set.Tables.Contains(tableName))
{
var adapter = new SQLiteDataAdapter(command, connection);
SQLiteCommandBuilder builder = new SQLiteCommandBuilder(adapter);
adapter.FillSchema(Set, SchemaType.Source, tableName);
adapter.Fill(Set, tableName);
adapter.Dispose();
}
return Set.Tables[tableName];
}
To call it, for example
DataTable myTable = GetTable("MyTable", "select * from MyTable);
To access a field:
object emptyValue = myTable.Rows[0]["Some_Column"];
There are some cells in the SQLite file that are of type INT, and their values are empty (not null). However when I'm trying to populate myTable, they are conveniently converted to 0's which I DO NOT WANT. How do I go about fixing that? I would like to keep empty values (and null values) as null's when importing to C#.
You can retrieve the row I was talking about above by executing the following SQL statement:
select * from MyTable where some_column = ''
The SQLite file that I use is SQLite3. Just in case it helps.
Thanks in advance!
This code snippet is throwing an error:
Update unable to find TableMapping['Table'] or DataTable 'Table'.) on adapter.Update(ds); line
Why it is throwing this type of error?
SqlConnection con = new SqlConnection();
con.ConnectionString = connectionString();
DataSet ds = new DataSet();
string strQuery = "SELECT * FROM Cars";
SqlDataAdapter adapter = new SqlDataAdapter();
adapter.SelectCommand = new SqlCommand(strQuery, con);
SqlCommandBuilder builder = new SqlCommandBuilder(adapter);
adapter.Fill(ds, "Cars");
//Code to modify data in the DataSet
ds.Tables["Cars"].Rows[0]["Brand"] = "NewBrand";
adapter.UpdateCommand = builder.GetUpdateCommand();
adapter.Update(ds);
Use
adapter.Update(ds, "Cars");
instead.
I have tested it. I got the same error without and it works if i specify the tablename. However, i must admit that i yet don't know why the DataAdapter needs to know the table-name since it has all informations it needs. If i useds.GetChanges i get one row with the correct table-name.
Update I have found nothing on MSDN but finally found it in the source(ILSpy). Here is the implementation of DBDataAdapter.Update(DataSet):
public override int Update(DataSet dataSet)
{
return this.Update(dataSet, "Table");
}
So if you don't specify a table, the table-name "Table" is used and if you've specified a table-name other than that you'll get this error, that's really strange!
I assume that the reason for this is that the DataAdapter cannot call GetChanges to determine the table to update for two reasons:
It would be inefficient since it needs to loop all tables and all of their rows to find rows with a RowState != Unchanged
It's possible that multiple tables needs to be updated since they contain changed rows. That is not supported via DataAdapter. Hence DataAdapter.Update(DataSet) assumes the default name "Table" as table-name.
Edit: However, maybe someone can explain me why the DataAdapter doesn't use DataSet.Tables[0].TableName instead.
So in general it seems to be best practise to specify the name of the table you want to update.
It's because .NET cannot assume that the table name in the DataSet/DataTable is identical to the database table. Thus .NET warns you about it.
To solve it you need to add a mapping to the DataAdapter:
da.TableMappings.Add("TableNameInDb", "TableNameInTheDataSet");
However, even if you have specified a name in the DataSet or the DataSource it still doesn't work, since the adapter seems to forget the name.
I only got it working by using:
da.TableMappings.Add("Table", "TableNameInDb");
I agree with jgauffin and I will only explain it with more text.
First, I must explain how TableMapping works. If we don't specify TableMappings with SqlDataAdapter (SqlDataAdapter that will fill our DataSet) then by default the first table will be named "Table", the second will be named "Table1", the third will be named "Table2" etc.
So, when we want to name DataTable in DataSet, we use it like this:
System.Data.DataSet myDataSet = new System.Data.DataSet();
using (System.Data.SqlClient.SqlDataAdapter dbAdapter = new System.Data.SqlClient.SqlDataAdapter(dbCommand))
{
dbAdapter.TableMappings.Add("Table", "Cars");
dbAdapter.TableMappings.Add("Table1", "Trucks");
//...
dbAdapter.Fill(myDataSet);
}
And only then we can modify it like this:
myDataSet.Tables["Cars"].Rows[0]["Brand"] = "Toyota";
myDataSet.Tables["Trucks"].Rows[0]["Brand"] = "MAN";
I am attempting to update a simple ms access database. I get an Exception on certain tables that, after searching, I found Microsoft Support - Syntax Error. I believe it means that one of the column names uses a reserved word. This seems to be the case, since all the tables update except the ones with "GUID" as one of the column names, a reserved word. This page also states that I should be using a OleDbAdapter and DataSet, which should solve the problem. Unfortunately I cannot change the name of the column. That is beyond my control, so I have to work with what is given me.
I haven't had to do work with databases much, and everything I know I've learned from examples from the internet (probably bad ones at that). So what is the proper way to update a database using OleDbAdapter and dataSet?
I don't think I should be using DataTable or OleDbCommandBuilder, and I believe the solution has something to do with parameters. But my googleing skills are weak.
OleDbConnection conn = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0; " +
Data Souce=" + source);
conn.Open();
OleDbAdapter adapter = new OleDbDataAdapter("SELECT * From " + table, conn);
OleDbCommandBuiler cmdBuiler = new OleDbCommandBuilder(adapter);
DataSet = new DatSet();
adapter.InsertCommand = cmdBuilder.GetInertCommand(true); // Is this necessary?
adapter.Fill( dataSet, table);
DataTable dataTable = dataSet.Tables[table]; // Do I need a DataTable?
DataRow row = dataTable.
row [ attribute ] = field; // Do this for all attributes/fields. I think this is wrong.
dataTable.rows.Add(row);
adapter.Update(dataTable); //<--"Syntax error in INSERT INTO statement." Exception
The problem may be that the column names (especially those whose name are reserved words) should be surrounded by square brackets. The OleDbCommandBuilder, when it creates its own InsertCommand, doesn't surround the names with brackets, so a solution is to manually define the OleDbDataAdapter's InsertCommand:
adapter.InsertCommand = new OleDbCommand(String.Format("INSERT INTO {0} ([GUID], [fieldName]) Values (#guid,#fieldName);", table), conn);
Defining parameters for each column and then manually adding the parameter's values;
adapter.InsertCommand.Parameters.Add(new OleDbParameter("#guid",row["GUID"]));
So summing up, for the tables which have a column named "GUID", you should try something like the following:
OleDbConnection conn = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;" +
"Data Souce=" + source);
conn.Open();
OleDbDataAdapter adapter = new OleDbDataAdapter("SELECT * From " + table, conn);
OleDbCommandBuilder cmdBuilder = new OleDbCommandBuilder(adapter);
adapter.InsertCommand = new OleDbCommand(String.Format("INSERT INTO {0} ([GUID], [fieldName]) Values (#guid,#fieldName);", table), conn);
DataTable dataTable = new DataTable(table);
adapter.Fill( dataTable);
DataRow row = dataTable.NewRow();
row [ fieldName ] = fieldValue;
// eg: row [ "GUID" ] = System.Guid.NewGuid().ToString(); // Do this for all attributes/fields.
dataTable.Rows.Add(row);
adapter.InsertCommand.Parameters.Add(new OleDbParameter("#fieldName",row[fieldName]));
// eg: adapter.InsertCommand.Parameters.Add(new OleDbParameter("#guid",row["GUID"]));
adapter.Update(dataTable);
As to problem #1. Try doing a full qualification of the column name i.e. table.columnName (that fixes the problem in MySQL so maybe it does in Access) also, try putting [ ] around the column name.
Select * is usually a poor option to specifying the column names and using aliases. For example use Select Column1 as 'Column1', Column2 as 'Column2' ....
this makes working with your dataset and datatable much easier as you can access the column by its alias instead of by column indexes.
I find that the DataAdapter is much more useful for filling datasets than for actually modifying a database. I recommend something like:
string updateQuery = "Update ..... Where ...."; //do your magic here
OldDbcommand command = new OleDbCommand(updateQuery);
command.Connection = conn;
conn.Open();
con.ExecuteNonQuery();
conn.Close();
You could fill your dataset with the adapter and then do as I just did to execute your update commands on the DB.
A good place to start would be using DataSetDesigner and Typed DataSets to start
try this walk through : http://msdn.microsoft.com/en-us/library/ms171893(v=vs.80).aspx
A good longterm approach is to use Sql Server Express instead, then you'll have a choice of using : Entity Framework, Linq To Sql or Still keep using the DataSetDesigner and Typed DataSets.
How to fill multiple tables in a dataset.
I m using a query that returns me four tables.
At the frontend I am trying to fill all the four resultant table into dataset.
Here is my Query. Query is not complete. But it is just a refrence for my Ques
Select * from tblxyz compute sum(col1)
suppose this query returns more than one table, I want to fill all the tables into my dataset
I am filling result like this
con.open();
adp.fill(dset);
con.close();
Now when i checks this dataset. It shows me that it has four tables but only first table data is being displayed into it. rest 3 dont even have schema also.
What i need to do to get desired output
Use DataAdapter.TableMappings.
e.g. :
DataSet ds = new DataSet();
// setup DataSet if required
SqlCommand cmd = new SqlCommand();
// setup command
SqlDataAdapter da = new SqlDataAdapter(cmd);
da.TableMappings.Add("Table", "goofy");
da.TableMappings.Add("Table1", "donald");
da.TableMappings.Add("Table2", "daffy");
da.TableMappings.Add("Table3", "foghorn");
da.MissingSchemaAction = MissingSchemaAction.AddWithKey;
da.Fill(ds);
where "goofy" is the name of the DataTable you want your first result set to go into,
and "donald" is the second etc.
Check this links.
http://www.developer.com/article.php/3311341
http://vb.net-informations.com/dataset/dataset-multiple-tables-sqlserver.htm
Maybe the problem is that you need to return all your tables separetly, as different queries, for example, fill-ing the same DataSet in a loop.
Good Luck!