Sending DataTable to a stored procedure from C# - c#

Need some help with DatagridView & DataTable.
Basically I have a DatagridView which OnLoad populates with the data from a table in SQL Server
When I click a button on UI, this DataGridView adds a new column to the front of grid "Update" which is a checkbox column
Now when the user ticks all those rows which needs updating and clicks Update...
I want to update all the rows which are ticked (for example: I wish to set the owner of these rows from Person A to Person B)
I've looked at DataTable but I'm confused
My logic is to add all the selected columns to a DataTable and send this to a stored procedure in SQL Server which would update the values.
If I'm not mistaken, I shall be sending a DataTable with just an ID column followed by From & To (owners) to the stored procedure.
Please guide me if I'm wrong, any help would be immensely appreciated.

private DataTable getDataGridID()
{
DataTable dt = new DataTable();
dt.Columns.Add("ID");
foreach (DataGridViewRow row in dgTeamDashboard.Rows)
{
if (Convert.ToBoolean(row.Cells["Update"].Value) == true)
dt.Rows.Add(row.Cells["ID"].Value);
}
return dt;
}
I've now progressed upto this point where I have a DataTable with all those ID's whose update column is ticked.
I'm hopeful, I'm heading in the right direction. Comment if I'm not
Further Update:
I've now create a stored procedure which accepts UserDefinedTableType and a destinationOwnerID as parameter and updates the actual table with the supplied OwnerID for all those leads whose ID matches the records from DataTable.
Create Procedure [activity].[udpUpdateActivityLead]
#ActivityLeadTable ActivityLeadType READONLY,
#OwnerTo int
AS
BEGIN
UPDATE [activity].[tblActivity]
set [activity].[tblActivity].[IDOwner]= #OwnerTo
from #ActivityLeadTable
where [activity].[tblActivity].[ID]=[#ActivityLeadTable].[ID];
END
Finally I got this function in my UI which works like a GEM. Happy ending...I can go to sleep now...
public void updateActivityLead()
{
SqlConnection con = new SqlConnection(OpSupLib.MyConnectionString);
SqlCommand cmd = new SqlCommand();
if (con.State == System.Data.ConnectionState.Closed)
con.Open();
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.CommandText = "[activity].[udpUpdateActivityLead]";
SqlParameter p1 = new SqlParameter();
p1.ParameterName = "#ActivityLeadTable";
p1.Value = getDataGridID();
cmd.Parameters.Add(p1);
SqlParameter p2 = new SqlParameter();
p2.ParameterName = "#OwnerTo";
p2.Value = ((ComboBoxItem)cmbUpdateTo.SelectedItem).HiddenValue;
cmd.Parameters.Add(p2);
cmd.Connection = con;
cmd.ExecuteNonQuery();
if (con.State == System.Data.ConnectionState.Open)
con.Close();
}

Related

My gridcontrol doesn't show datatable connected SQL Server

In this form, I have just one gridcontrol and this datasource is datatable connected to SQL Server.
Bottom of the gridcontrol there are four buttons named select, insert, delete, modify gridcontrol.
This is my code, and it has click event when insert button is clicked.
private void InputButton_Click(object sender, EventArgs e)
{
try
{
using (SqlConnection conn = new SqlConnection(mssql))
{
conn.Open();
SqlDataAdapter sqlDataAdapter = new SqlDataAdapter();
SqlCommand cmd = new SqlCommand("Insert into [Department] (부서코드, 부서명) values (#부서코드, #부서명)", conn);
cmd.Parameters.AddWithValue("#부서코드", "2101");
cmd.Parameters.AddWithValue("#부서명", "영업부");
sqlDataAdapter.InsertCommand = cmd;
int result = cmd.ExecuteNonQuery();
if (result < 0)
{
MessageBox.Show("There are errors when inserting data");
}
else
{
sqlDataAdapter.Update(dataTable);
gridControl1.RefreshDataSource();
conn.Close();
}
}
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
}
When I click insert button, there are not any event in gridcontrol so I don't see any change of insert query. However, close form and reopen form, then I can see my insert query in gridcontrol.
Even I use gridcontrol.refreshdatasource but I don't know what I can't see any changes soon after I click
insert button. Is there any answers?
Even I tried use try catch, but I don't know my issue. I want to show gridcontrol.refreshdatasource when I click insert query.
To insert SQL Server data using a GridControl and DataTable in C# Winform, you can follow these steps:
Create a connection string to connect to the SQL Server database. You can use the SqlConnection class to create the connection string.
string connectionString ="Server<server_name>;Database<database_name>;User Id=<username>;Password=<password>;";
Create a DataTable object to store the data that will be displayed in the GridControl. You can add columns to the DataTable object to match the columns in the SQL Server table.
DataTable dt = new DataTable();
dt.Columns.Add("ID", typeof(int));
dt.Columns.Add("Name", typeof(string));
dt.Columns.Add("Age", typeof(int));
Create a SqlDataAdapter object and use it to fill the DataTable object with data from the SQL Server table. You can use the SELECT statement to retrieve the data.
SqlDataAdapter adapter = new SqlDataAdapter("SELECT * FROM <table_name>", connectionString);
adapter.Fill(dt);
Set the DataSource property of the GridControl to the DataTable object to display the data in the GridControl.
gridControl1.DataSource = dt;
Add a new row to the DataTable object using the NewRow method, and set the values of the columns.
DataRow row = dt.NewRow();
row["ID"] = 1;
row["Name"] = "Bob Ross";
row["Age"] = 52;
dt.Rows.Add(row);
Create a SqlCommand object to insert the data into the SQL Server table. You can use the INSERT INTO statement to insert the data.
SqlCommand cmd = new SqlCommand("INSERT INTO <table_name> (ID, Name, Age) VALUES (#ID, #Name, #Age)", connection);
cmd.Parameters.AddWithValue("#ID", row["ID"]);
cmd.Parameters.AddWithValue("#Name", row["Name"]);
cmd.Parameters.AddWithValue("#Age", row["Age"]);
cmd.ExecuteNonQuery();
Refresh the DataTable object and the GridControl to reflect the changes.
dt.Clear();
adapter.Fill(dt);
gridControl1.RefreshDataSource();
Note that you should handle any exceptions that may occur while inserting data into the SQL Server table, and also make sure to dispose of any database connections, commands, and adapters after use.
Hope this helps.

DataGridView Datatable Copy-Keep-Add-Update rows

I am developing Windows application (environment: Visual Studio 2010 and C#)
I use a datagridview with records completed by datatable dt:
dataGridView1.DataSource = dt;
This datatable has 20 columns with 1 identity column - column[0] = autonumber, and column[1] called “RecordChecked” implemented as Boolean (checkbox).
I need to solve next problems:
Select rows filtered by column[2] (in code example: DrawingNo='DM-3012');
Keep these records
Add exactly the same records below existing but update column[2] with different value like DrawingNo='DM-3013' (so we’ll have twice more records)
I started from copying records from one datatable into another (see code below) – this code works ok, but then stacked how to add copied records below existing and then update them:
DataTable dtSource = ((DataTable)dataGridView1.DataSource);
DataTable dtTarget = new DataTable();
dtTarget = ((DataTable)dataGridView1.DataSource).Clone();
DataRow[] rowsToCopy;
rowsToCopy = ((DataTable)dataGridView1.DataSource).Select("DrawingNo='DM-3012'");
foreach (DataRow temp in rowsToCopy)
{
dtTarget.ImportRow(temp);
}
dt = dtTarget;
Thanks,
I think I found a good approach to this problem.
1. Create Stored Procedure (mySP).
This SP creates temp table where we located all records selected with clause 'WHERE DrawingNo = #CopyFrom'.
Then this SP updates temp table with statement like:
UPDATE #TempTbl1 SET RecChecked = 0, DrawingNo = #CopyTo WHERE DrawingNo = #CopyFrom.
Then SP inserts updated records from temp table into the main table.
Finally SP drops temp table and selects all needed records from the main table.
2. Now we can run this SP in app and bind data to datagridview like:
//Run SP
using (SqlCommand cmd = new SqlCommand("mySP", con))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#CopyFrom", SqlDbType.VarChar).Value = sValueFrom;
cmd.Parameters.Add("#CopyTo", SqlDbType.VarChar).Value = sValueTo;
con.Open();
cmd.ExecuteNonQuery();
}
//Create SELECT statement
string strSelect = "SELECT Recid, RecChecked, DrawingNo, ... FROM Tbl1 WHERE DrawingNo = '" + sValueTo + "'"
//fill dataadapter
sda = new SqlDataAdapter(#strSelect, con);
dt = new DataTable();
sda.Fill(dt);
That works!

update columns in table using C# for current row in gridview control

How do i updated the Modified and ModifiedBy column in my gridview1 control based on the current one thats showing or is selected.
Currently i have the following but it seems to update everything:
string ConnectionString = "Data Source=C;Initial Catalog=tickets;Integrated Security=True";
SqlConnection Conn = new SqlConnection(ConnectionString);
Conn.Open();
SqlDataAdapter DA = new SqlDataAdapter("UPDATE [tickets].[dbo].[UpdatedStat] SET MODIFIED = GETDATE(), MODIFEDBY = 'John' WHERE Person = 'John' AND ClosedDate IS NULL", Conn);
DataTable dt = new DataTable();
DA.Fill(dt);
if (Conn.State == ConnectionState.Open)
{
Conn.Close();
}
The problem is, how do update only the record/row in focus in my grid view? I basically click a button to move to the next row, so i need to update EACH record with my ModifiedDate and Modified name.
OH and the update code doesnt even work at all? I think i need an update command??

Combobox add rows dynamically and dropdown from SQL Server stored procedure

This is basically a search tool. When I type some thing in a combobox, the combobox drops down and will show me suggestions (something like Google search bar)
I created a procedure which does some complex calculations, which take one parameter and returns some rows. Then I created a combobox event (On Update Text).
And in the event handler I wrote this code:
private void combobox_TextUpdate(object sender, EventArgs e)
{
this.combobox.Items.Clear();
DataTable List = new DataTable();
if (this.combobox.Text.Length > 0)
{
List = searchIt(combobox.text);
foreach (DataRow Row in List.Rows)
{
this.combobox.Items.Add(Row.ItemArray.GetValue(0).ToString());
}
this.combobox.DroppedDown = true;
}
}
static public DataTable searchIt(string STR)
{
string connectionString = McFarlaneIndustriesPOSnamespace.Properties.Settings.Default.McFarlane_IndustriesConnectionString;
SqlConnection con = new SqlConnection(connectionString);
DataTable DT = new DataTable();
con.Open();
SqlDataAdapter DA = new SqlDataAdapter("USE [McFarlane Industries] " +
"EXEC search " +
STR, connectionString);
DA.Fill(DT);
con.Close();
return DT;
}
The function searchIt executes the stored procedure and it returns a DataTable. The stored procedure is working fine in SQL Server Management Studio.
But in the application it is not working correctly in some cases.
When I type [space], then it throws an exception and it says stored procedure needs parameter which is not provided.
There are many other characters when I type them it throws exception that invalid character at end of string "my string".
Any suggestion how could I achieve my goal.
Call your stored procedure with sqlcommand to fill your datatable
using (SqlConnection scn = new SqlConnection(connect)
{
SqlCommand spcmd = new SqlCommand("search", scn);
spcmd.Parameters.Add("#blah", SqlDbType.VarChar, -1); //or SqlDbType.NVarChar
spcmd.CommandType = System.Data.CommandType.StoredProcedure;
using (SqlDataAdapter da = new SqlDataAdapter(spcmd))
{
da.Fill(dt);
}
}
static public DataTable searchIt(string STR)
{
string connectionString = McFarlaneIndustriesPOSnamespace.Properties.Settings.Default.McFarlane_IndustriesConnectionString;
SqlConnection con = new SqlConnection(connectionString);
DataTable DT = new DataTable();
con.Open();
SqlCommand command = new SqlCommand("Name_of_Your_Stored_Procedure",con);
command.CommandType=CommandType.StoredProcedure;
command.Parameters.Add(new SqlParameter("#parameter_name",SqlDbType.NVarChar));
command.Parameters[0].Value="Your Value in this case STR";
SqlDataAdapter DA = new SqlDataAdapter(command);
DA.Fill(DT);
con.Close();
return DT;
}
Important :
'parameter_Name' and 'Name_of_Your_Stored_Procedure' should be replaced by yours which you have in database. And value of parameter could be like "abc" (combox.Text)
Command and its type, its text are necessary.
Adding parameters depends upon your stored procedure. They can be 0,1 or more but once they are added their values must be given. conn(connection) can be passed to new SqlCmmand() or new SqlDataAdapter()
No need of things like 'use' and 'exec'
Following me and this link might be helpful in future for stored procedures
http://www.codeproject.com/Articles/15403/Calling-Stored-procedures-in-ADO-NET
Two optional Suggestions for you
use variable name 'list' instead of 'List' (you used) however you will not get problem with this name until you add a namespace using System.Collections.Generic; but you may need to use this namespace in future.
Use only list.Rows[0].ToString(); no need to get itemarray then get value when you are working with data in strings;

add row to a BindingSource gives different autoincrement value from whats saved into DB

I have a DataGridView that shows list of records and when I hit a insert button, a form should add a new record, edit its values and save it.
I have a BindingSource bound to a DataGridView. I pass is as a parameter to a NEW RECORD form so
// When the form opens it add a new row and de DataGridView display this new record at this time
DataRowView currentRow;
currentRow = (DataRowView) myBindindSource.AddNew();
when user confirm to save it I do a
myBindindSource.EndEdit(); // inside the form
and after the form is disposed the new row is saved and the bindingsorce position is updated to the new row
DataRowView drv = myForm.CurrentRow;
avaliadoTableAdapter.Update(drv.Row);
avaliadoBindingSource.Position = avaliadoBindingSource.Find("ID", drv.Row.ItemArray[0]);
The problem is that this table has a AUTOINCREMENT field and the value saved may not correspond the the value the bindingSource gives in EDIT TIME.
So, when I close and open the DataGridView again the new rowd give its ID based on the available slot in the undelying DB at the momment is was saved and it just ignores the value the BindingSource generated ad EDIT TIME,
Since the value given by the binding source should be used by another table as a foreingKey it make the reference insconsistent.
There's a way to get the real ID was saved to the database?
I come up with this solution
First added a GetNextID() method directly to the table model:
SELECT autoinc_next
FROM information_schema.columns
WHERE (table_name = 'Estagio') AND (column_name = 'ID')
and whener I need a new row to be added I do
EstagioTableAdapter ta = new EstagioTableAdapter ();
nextID = ta.GetNextID();
row = (DataRowView)source.AddNew();
row.Row["ID"] = nextID;
(...)
source.EndEdit();
The same thing happens with Access databases. There is a great article (with solution) here. Basically, the TableAdapter normally sends 2 queries in a batch when you save the data. The first one saves the data and the second one asks for the new ID. Unfortunately, neither Access nor SQL CE support batch statements.
The solution is to add an event handler for RowUpdated that queries the DB for the new ID.
based on my answer on concurrency violation, use da.InsertCommand.UpdatedRowSource = UpdateRowSource.FirstReturnedRecord.
note: just change SQLiteConnection and SQLiteDataAdapter to MSSQL ones, and change the LAST_INSERT_ROWID() to SCOPE_IDENTITY()
const string devMachine = #"Data Source=C:\_DEVELOPMENT\__.NET\dotNetSnippets\Mine\TestSqlite\test.s3db";
SQLiteConnection c = new SQLiteConnection(devMachine);
SQLiteDataAdapter da = new SQLiteDataAdapter();
DataTable dt = new DataTable();
public Form1()
{
InitializeComponent();
da = new SQLiteDataAdapter("select product_id, product_name, abbrev from product", c);
var b = new SQLiteCommandBuilder(da);
da.InsertCommand = new SQLiteCommand(
#"insert into product(product_id, product_name, abbrev) values(:_product_id, :_product_name, :_abbrev);
select product_id /* include rowversion field here if you need */
from product where product_id = LAST_INSERT_ROWID();", c);
da.InsertCommand.Parameters.Add("_product_id", DbType.Int32,0,"product_id");
da.InsertCommand.Parameters.Add("_product_name", DbType.String, 0, "product_name");
da.InsertCommand.Parameters.Add("_abbrev", DbType.String, 0, "abbrev");
da.InsertCommand.UpdatedRowSource = UpdateRowSource.FirstReturnedRecord;
da.UpdateCommand = b.GetUpdateCommand();
da.DeleteCommand = b.GetDeleteCommand();
da.Fill(dt);
bds.DataSource = dt;
grd.DataSource = bds;
}
private void uxUpdate_Click(object sender, EventArgs e)
{
da.Update(dt);
}
here's the sample table on SQLite:
CREATE TABLE [product] (
[product_id] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
[product_name] TEXT NOT NULL,
[abbrev] TEXT NOT NULL
)
[EDIT Nov 19, 2009 12:58 PM CN] Hmm... I guess my answer cannot be used, SQLCE does not allow multiple statements.
anyway, just use my answer when you use server-based MSSQL or if you use SQLite. or perhaps, encapsulate the two statements to a function that returns scope_identity(integer):
da.InsertCommand = new SQLiteCommand(
#"select insert_to_product(:_product_id, :_product_name, :_abbrev) as product_id", c);
da.InsertCommand.Parameters.Add("_product_id", DbType.Int32,0,"product_id");
da.InsertCommand.Parameters.Add("_product_name", DbType.String, 0, "product_name");
da.InsertCommand.Parameters.Add("_abbrev", DbType.String, 0, "abbrev");
da.InsertCommand.UpdatedRowSource = UpdateRowSource.FirstReturnedRecord;
note: just change SQLiteConnection and SQLiteDataAdapter to MSSQL ones, and change the LAST_INSERT_ROWID() to SCOPE_IDENTITY()
use RowUpdated (shall work on SQLCE and RDBMS that doesn't support multi-statements):
const string devMachine = #"Data Source=C:\_DEVELOPMENT\__.NET\dotNetSnippets\Mine\TestSqlite\test.s3db";
SQLiteConnection c = new SQLiteConnection(devMachine);
SQLiteDataAdapter da = new SQLiteDataAdapter();
DataTable dt = new DataTable();
public Form1()
{
InitializeComponent();
da = new SQLiteDataAdapter("select product_id, product_name, abbrev from product", c);
var b = new SQLiteCommandBuilder(da);
da.InsertCommand = b.GetInsertCommand();
da.UpdateCommand = b.GetUpdateCommand();
da.DeleteCommand = b.GetDeleteCommand();
da.Fill(dt);
da.RowUpdated += da_RowUpdated;
bds.DataSource = dt;
grd.DataSource = bds;
}
void da_RowUpdated(object sender, System.Data.Common.RowUpdatedEventArgs e)
{
if (e.StatementType == StatementType.Insert)
{
int ident = (int)(long) new SQLiteCommand("select last_insert_rowid()", c).ExecuteScalar();
e.Row["product_id"] = ident;
}
}
private void uxUpdate_Click(object sender, EventArgs e)
{
da.Update(dt);
}
I haven't had a chance to use SQLiteConnection class but I do used SQLConnection and SQLCommand class. SqlCommand has a method ExecuteScalar that return the value of the first row and first column of your t-sql statement. You can use it to return the Auto-Identity column. Also, in SQL Server 2005 there is a keyword named OUTPUT you may also check it too.
I've come across this: all you need to do is set your autoincrement seed to -1 and have it "increment" by -1 too. This way all your datarows will have unique ids that DON'T map to anything in the real database. If you're saving your data with a DataAdapter, then after the save your datarow and any other rows with a datarelation pointing to that id will be updated

Categories