Use data in datatable to call stored procedures - c#

I have a datatable fetching values from a stored procedure written as below:
SqlConnection sqlcon = new SqlConnection(ConfigurationManager.ConnectionStrings["db"].ConnectionString);
sqlcon.Open();
DataTable dt = new DataTable("tmp");
DataSet ds = new DataSet();
SqlDataAdapter da = new SqlDataAdapter();
SqlCommand cmd = new SqlCommand("usp_abc", sqlcon);
cmd.CommandType = CommandType.StoredProcedure;
da.SelectCommand = cmd;
da.Fill(dt);
Now, I need to loop through the datatable and get the values of this datatable and pass it is as a parameter to my stored proc.

I believe such operation is better done in the DB if possible instead of going to the DB for each row in the datatable row collection.
You can use foreach to loop through the datatable, each DataRow representing a row in the returned result.
foreach(DataRow row in dt.Rows)
{
var col1 = row[0]; //access using column index/position
var firstNameCol = row["FirstName"].ToString(); //access through column name
}
You can throw more light on what you want to do if it's possible to move it to DB (using stored procedure)
DataTable.Rows
Update: Passing value to stored proc using command.Parameters
cmd.Parameters.AddWithValue("#FirstName", firstNameCol);

You can use:
foreach (DataRow row in dt.Rows)
{
foreach(object o in row.ItemArray)
{
//do something with o
}
}

To set parameters in a sproc:
SqlCommand cmd = new SqlCommand("usp_abc", sqlcon);
cmd.CommandType = CommandType.StoredProcedure;
cmd.parameters.AddWithValue("#foo", "bar");
cmd.parameters.AddWithValue("#plugh", Integer.valueOf(17));
This will set the parameter "#foo" to the value "bar" and the parameter "#plugh" to the value 17. The value can be String, Integer, Boolean, probably others.
Read the documentation on the SqlParameterCollection object for other functions to set parameters. There are others that let you explicitly set the SQL data type, instead of making it guess a SQL datatype from your C# datatype. The only time I've ever needed this was when I wanted to set an Image value.
Side note that tripped me up early on: You cannot pass in NULL. If you want a value to be null, you have to pass in System.DBNull.Value. There is no doubt some good reason for this but I find it an annoying pain.

If you only need the data to pass as a parameter to a stored procedure consider DataReader.
DataTable has overhead.
DataReader is more efficient and simpler syntax.
SqlDataReader Class
If you need the DataTable for other stuff (like bind to a DataGrid) then stay with DataTable and the answer from codingbiz +1 seems correct to me.
Servy feels strongly the answer should include how to reference a values in a DataRow.
You can do it by ordinal position or column name.
row[0];
row["colx"];
But you already have that answer from condigbiz and should give the check to codingbiz if the is what you use.
SqlDataReader reader = command.ExecuteReader();
// Call Read before accessing data.
while (reader.Read())
{
if (!reader.IsDBnull(0))
{
cmd.Parameters.AddWithValue("#param1", Reader.GetString(0));
}
}
// Call Close when done reading.
reader.Close();
OP commented Reader would lock table.
You could use with (no lock) or read into a List<>;
But you could also just stay with DataTable and that seems like the best plan to me.

Related

How to save only the new rows AND modified rows in datagridview?

I'm trying to save my data from datagridview row to my sql database. My problem is with my current code, it reads and updates all the rows regardless if it has any changes or not. I know this is going to be a problem especially if my table has large amounts of data.
These are what I did with my code so far:
//retrieve data from dbase
public void loadToDGV()
{
DBConn.DBConnect();
SqlDataAdapter sqlDA = new SqlDataAdapter("SELECT * from TableName", DBConnection.conn);
sqlDA.Fill(dataTable);
gridView.DataSource = dataTable;
}
Below is what's in my Save button.
foreach (DataGridViewRow row in gridView.Rows)
{
DBConn.DBConnect();
SqlCommand comm = new SqlCommand();
comm.Connection = DBConnection.conn;
comm = new SqlCommand("SPName", DBConnection.conn);
comm.CommandType = CommandType.StoredProcedure;
comm.Parameters.AddWithValue("#ID", row.Cells["ID"].Value == DBNull.Value ? "" : row.Cells["ID"].Value);
comm.ExecuteNonQuery();
}
I have another sample code where I can only save modified rows
changeTable = dataTable.GetChanges(DataRowState.Modified);
foreach (DataRow row in changeTable.Rows)
{
DBConn.DBConnect();
SqlCommand comm = new SqlCommand();
comm.Connection = DBConnection.conn;
comm = new SqlCommand("SPName", DBConnection.conn);
comm.CommandType = CommandType.StoredProcedure;
comm.Parameters.AddWithValue("#ID", row["ID"].ToString());
comm.ExecuteNonQuery();
}
What I wanted to do is save only BOTH the "New Rows" AND "Modified Rows".
Is there any way to only get newly added/edited rows? and not include all the rows from the gridview?
My Stored Procedure only checks if the ID is existing or not.
IF NOT EXISTS (SELECT 1 FROM TABLE WHERE ID = #ID)
-- INSERT QUERY
ELSE
-- UPDATE QUERY
You should not be using a loop at all, nor even referring to the grid. You obviously have a DataTable already. If you're not already doing so, populate it with a data adapter, bind it to a BindingSource and bind that to the grid. When it's time to save, call EndEdit on the BindingSource call Update on the data adapter and pass the DataTable. That's it, that's all. There's no need to call GetChanges.
Obviously you will have to configure the data adapter appropriately, with an InsertCommand to insert new records and an UpdateCommand to save modified records. I won't go into specifics as you haven't gone into specifics but there are plenty of examples around. You can find my own here.
EDIT:
The short answer to your question is that, if you want both modified and added rows, then specify both Modified and Added rows. The DataRowState enumeration has the Flags attribute, which means that you can create compound values. That would mean that this:
changeTable = dataTable.GetChanges(DataRowState.Modified);
would become this:
changeTable = dataTable.GetChanges(DataRowState.ModifiedData Or RowState.Added);
If you were going to then loop through those rows, you wouldn't create a new command object every iteration. You'd create one command and add the parameters once, then simply set the Value of each parameter in the loop. You would also need to call AcceptChanges on the original DataTable afterwards.
You should do any of that though. As I said, you should use the same data adapter as you used to retrieve the data to save the changes. Normally you'd use different SQL for the InsertCommand and UpdateCommand but, in your case, you can use the same. Just create a single command object with the appropriate parameters and assign it to both the InsertCommand and UpdateCommand properties, then call Update.

C# + SQL Select + dataset + dataadapter = out of memory

I'm currently trying to use C# to read through an SQL DB. To do so, I use OleDB with a select statement. This goes into a dataset, which then populates a data adapter. I then iterate through each row and calculate stuff.
First of all, I feel like there's a better/more efficient way of doing this because I NEVER actually write back to the SQL DB. I just calculate based on what I'm selecting.
Anyways, past a certain point I get out of memory errors and/or an error from Ssms.exe saying "a new guard page for the stack cannot be created."
From the other questions I've seen, I need to use DataReader but I can't seem to get it to work the same way as the data adapter (which I suppose isn't that surprising).
The code I have now:
OleDbConnection myConn = new OleDbConnection(#"SQLDB connection string here");
OleDbCommand cmd = new OleDbCommand();
cmd.CommandText = <selectstatement here>
cmd.Connection = myConn;
cmd.CommandTimeout = 0;
OleDbDataAdapter da = new OleDbDataAdapter(cmd);
DataSet ds = new DataSet();
da.Fill(ds);
myConn.Close();
foreach (DataTable table in ds.Tables)
{
foreach (DataRow dr in table.Rows)
{
//do stuff
I guess my question is twofold, like I said above. One would DataReader solve my problem and allow me to iterate through the data, and two how do I adapt the first code snippet above to support that?
Also, since I've seen it elsewhere, I'm using x64 on the application.

Use DataTable.Load() without creating all column properties

I'm loading data from a SQL server database like this:
SqlCommand cmd = new SqlCommand("select * from x", conn);
cmd.CommandType = CommandType.Text;
DataTable dt = new DataTable();
SqlDataReader rd = cmd.ExecuteReader();
dt.Load(rd, LoadOption.PreserveChanges);
Issue:
The Load() function initializes the table columns with ColumnName and DataType, but it also looks deeper into the database, and it adds some constraints like AllowDBNull, AutoIncrement, MaxLength, etc.
However, this leads to problems in my application, because I want to further process the data internally.
So, is it possible to do a Load() just with setting the most basic properties (which come directly from the select statement), without setting AllowDBNull, MaxLength, and so on? Or do I need to clean these values after the Load() ? Or is there another alternative to calling Load() ?
If you don't want that behaviour don't use DataTable.Load but DbDataAdapter.Fill(DataTable):
DataTable dt = new DataTable();
using(var da = new SqlDataAdapter("select * from x", conn))
da.Fill(dt);
The Fill operation then adds the rows to destination DataTable objects
in the DataSet, creating the DataTable objects if they do not already
exist. When creating DataTable objects, the Fill operation normally
creates only column name metadata. However, if the MissingSchemaAction
property is set to AddWithKey, appropriate primary keys and
constraints are also created.
I have tested it, it just loads the column-names and types, all other properties like AllowDbNull or MaxLength have their default values which might be right or wrong.

Should I use datatable to store one column of select query in asp.net

My sql select query will return one column of company names (many rows of names). Now I want to store it. I used:
try
{
connection.Open();
sqlCmd = new SqlCommand(sqlCmd.CommandText, connection);
SqlDataReader sqlReader = sqlCmd.ExecuteReader();
DataTable dt = new DataTable();
dt.Clear();
sqlReader.Read();
dt.Load(sqlReader);
Then I tried to access the name by
dt.Rows[0][0].ToString()
dt.Rows[1][0].ToString()
dt.Rows[2][0].ToString()
etc.
But I recognize that the value in dt.Rows[0][0].ToString() is different from time to time, event if I use the same query, and it looks like that the old values still stored in the datatable event if I use new query value.
How is the right way to store and retrieve values here?
You can use,
string compnay_name = dt.Rows[0]["column_name"].ToString();
Try using your compnay name column in double quote.
You can use it in a for or foreach loop.
as,
for(int i=0;i<count;i++)
{
string compnay_name = dt.Rows[i]["column_name"].ToString();
}
Hope it helps...
Aki

Trying to load a DataSet, but it is always null. Regular sqlReader.GetValue() is not

I don't use DataSets much. Usually find myself using an ORM or just a basic sqlReader.Read() followed by some GetValues(). I'm working on some legacy code that has DataSets all over the place, and while fixing a bug was trying to DRY some of it up.
However, I can't seem to actually get the data loaded into a non-typed DataSet.
public static DataSet ExecuteStoredProcedure(string storedProcedure, DBEnum db, IEnumerable<SqlParameter> parameters)
{
DataSet result = new DataSet();
using (SqlConnection connection = SqlHelper.GetSqlConnection(db))
{
SqlCommand command = connection.CreateCommand();
command.CommandType = CommandType.StoredProcedure;
command.CommandText = storedProcedure;
if (parameters != null)
foreach (SqlParameter parameter in parameters)
command.Parameters.Add(parameter);
connection.Open();
DataTable table = new DataTable();
using (SqlDataReader reader = command.ExecuteReader())
{
table.Load(reader);
}
result = table.DataSet; // table.DataSet is always empty!
}
return result;
}
I assumed table.Load(reader) does all the necessary reader.Read() calls ... but I went ahead and tried it both with and without reader.Read() before the table.Load(), to no avail.
I know that the stored procedure being called is actually returning data. If I do something like this, I see the data just fine:
using(SqlDataReader reader = command.ExecuteReader())
{
reader.Read();
object test = reader.GetValue(0); // returns the expected value
}
Seems like I'm missing something simple here, but I've been scratching my head over this one for a while now.
This is in .NET 3.5.
If you can, I would suggest using a SqlDataAdapter to populate the DataTable
using(SqlDataAdapter sqlDA = new SqlDataAdapter(command))
{
sqlDA.Fill(table);
}
You logic shows the DataTable being loaded with data from the reader but DataTable is never added to a dataset.
I believe the dataset should be created first. In fact, you could use DataSet.Load instead of the DataTable.Load. The DataSet.Load should create data tables, but it won't work the other way around.
A DataSet contains DataTables not the other way round. So if you want to create a DataSet to return then you'll probably want to create a new dataset and then add your DataTable into it before returning it. If a DataTable is not in a DataSet then the reference to its parent dataset will always be null.
That having been said I do also recommend Jason Evans' suggestion of using teh SqlDataAdapter.
Think of a dataset as a collection of tables and optionally information about the relationships between them.
In your example code you are creating an independent Table that does not belong to a DataSet. To pragmatically create a table that is part of a dataset you could do the following:
DataSet ds = new DataSet();
DataTable dt = ds.Tables.Add();
//or
ds.Tables.Add(MyAlreadyCreatedTable);
Jason Evans above is also correct, populating DataTables and DataSets is much simpler using SqlDataAdaptors as he demonstrated.
Finally, the Method as you have it written is meant to return a DataSet. But it only captures a single result set from the stored procedure it is calling. It's possible that a procedure could return any number of separate results.
All you should need to do is change the following:
DataTable table = new DataTable();
using (SqlDataReader reader = command.ExecuteReader())
{
table.Load(reader);
}
to
//you can skip creating a new DataTable object
using (SqlDataAdapter da = new SqlDataAdapter(command))
{
da.Fill(result); // the result set you created at the top
}

Categories

Resources