0

I have bounded datagridview from datatable from sql server database

I need to know how I can make update button that on click update edited data within this datagridview

if there is new directly add rows to datagridview insert them to the database if there is an update for previously bounded data then update it

I searched but I do not really understand how to do it

  1. why do I have to use DataAdpterReader?

  2. why do I have to use Dataset which is a set of DataTable and I already have one DataTable?

Thanks

sam
  • 2,493
  • 6
  • 38
  • 73
  • [Here is a full example of *one way* to do it](https://github.com/crowcoder/CSharpCrudGrid). It uses `SqlDataAdapter` which, in conjunction with data tables can track and perform CRUD on your database. Where you see `SQLite...` you can use the same with `Sql...` – Crowcoder Sep 21 '18 at 12:58
  • @Crowcoder it written with `.net4.5.2` I am working on `.net4.0` – sam Sep 21 '18 at 13:15
  • it may still work if you remove the reference to SQLite and add a previous version. Other than that it should work all the way back to .net 2.0 or maybe even earlier. At worst you can reference the code, it was kept simple and commented. – Crowcoder Sep 21 '18 at 13:18

3 Answers3

3

if you want to have the rows in your DataGirdView to be reflected in the database, you simply have to use the methods of the DataAdapter object.

The DataAdapter and DataReader objects, simply put, provide you with an easy and efficient way of reading from and writing to your database. Apart from that, they also do all of this without affecting the actual database table so that means everything is untouched until you say so.

For this example, imagine we have an SQL table named contacts with three columns namely fname, mname and lname.

To get things started, we'll need a function we could use to get the data from the "contacts" table.

    protected DataSet GetData()
    {
        // our select query to obtain all the rows from the contacts table
        string selectQuery = "SELECT * FROM contacts";

        // Where the data from the underlying table will be stored
        DataSet ds = new DataSet();

        // Connect to the database and get the data from the "contacts" table
        using (SqlConnection conn = new SqlConnection(connString))
        {
            using (SqlDataAdapter da = new SqlDataAdapter(selectQuery, conn))
            {
                da.Fill(ds, "contacts"); // Add the rows from the "contacts" table to our dataset
            }
        }

        return ds;
    }

You can then bind the DGV to the table stored in the returned dataset object by doing

DGV_Items.DataSource = GetData();

In your form_load event.

Now we have finished setting up our method for getting the data from the database, we now set up a method for manipulating all that data we now have.

    protected void UpdateTable(DataSet ds)
    {
        SqlConnection conn = new SqlConnection(connString);

        // Insert, update and delete queries
        string updateQuery = "UPDATE contacts SET fname=@first,mname=@middle,lname=@last WHERE ID=@id";
        string deleteQuery = "DELETE FROM contacts WHERE ID=@id";
        string insertQuery = "INSERT INTO contacts VALUES(@first,@middle,@last)";

        // Create the parameters for the queries above
        SqlParameter[] insertParams = new SqlParameter[]
        {
            // the first parameter (e.g. @first) has to match with the declaration in the query

            // the second parameter (e.g.SqlDbType.NVarChar) is the data type of the actual column in the source table

            // the third paramter (e.g. 100) is the length of the data in the database table's column

            // the last parameter (e.g. "fname") is the DataPropertyName of the source column which is
            // basically the name of the database table column that the DGV column represents

            new SqlParameter("@first", SqlDbType.NVarChar, 100, "fname"),
            new SqlParameter("@middle", SqlDbType.NVarChar, 100, "mname"),
            new SqlParameter("@last", SqlDbType.NVarChar, 100, "lname")
        };

        SqlParameter[] updateParams = new SqlParameter[]
        {
            new SqlParameter("@first", SqlDbType.NVarChar, 100, "fname"),
            new SqlParameter("@middle", SqlDbType.NVarChar, 100, "mname"),
            new SqlParameter("@last", SqlDbType.NVarChar, 100, "lname"),
            new SqlParameter("@id", SqlDbType.Int, 100, "id")
        };

        SqlParameter[] DeleteParams = new SqlParameter[]
        {
            new SqlParameter("@id", SqlDbType.Int, 100, "id")
        };

        // Create the SqlCommand objects that will be used by the DataAdapter to modify the source table
        SqlCommand insertComm = new SqlCommand(insertQuery, conn);
        SqlCommand updateComm = new SqlCommand(updateQuery, conn);
        SqlCommand deleteComm = new SqlCommand(deleteQuery, conn);

        // Associate the parameters with the proper SqlCommand object
        insertComm.Parameters.AddRange(insertParams);
        updateComm.Parameters.AddRange(updateParams);
        deleteComm.Parameters.AddRange(DeleteParams);

        // Give the DataAdapter the commands it needs to be able to properly update your database table
        SqlDataAdapter dataAdapter = new SqlDataAdapter()
        {
            InsertCommand = insertComm,
            UpdateCommand = updateComm,
            DeleteCommand = deleteComm
        };

        // A DataTable and a DataSet are basically the same. Except the DataSet is a collection of DataTables
        // Here, you can see that we've accessed a specific DataTable in the DataSet.

        // Calling the Update method executes the proper command based on the modifications to the specified
        // DataTable object then commits these changes to the database
        dataAdapter.Update(ds.Tables["contacts"]);
    }

The above method will handle all the data manipulation. It will be acting based on the changes done to the DataTable object bound to your DGV. Finally, you can call all the methods we've made in your update button's event handler.

    private void Btn_Update_Click(object sender, EventArgs e)
    {
        // Grab the DGV's data source which contains the information shown in the DGV
        DataSet ds = (DataSet)dgv_items.DataSource;
        // Have any updates to the said dataset committed to the database
        UpdateTable(ds);
        // rebind the DGV
        dgv_items.DataSource = GetData();
    }

EDIT

Along with the suggestions from Crowcoder, here is a better way to write all of the stuff I wrote above:

/// <summary>
/// A collection of methods for easy manipulation of the data in a given SQL table
/// </summary>
class DBOps
{
    // The connection string contains parameters that dictate how we connect to the database
    private string connString = ConfigurationManager.ConnectionStrings["contactsConnectionString"].ConnectionString;

    // The table the instance of the class will be interacting with
    private string srcTable;

    // The SqlConnection Object that we will be using to connect to the database
    SqlConnection conn;

    // The DataAdapter object that we will be using to interact with our database
    SqlDataAdapter da;

    // The DataSet that we will be storing the data retrieved from the database
    DataSet ds;

    // The queries we would be using to manipulate and interact with the data in the database
    private string selectQuery;
    private string updateQuery;
    private string deleteQuery;
    private string insertQuery;

    // The collection of parameters for the queries above
    private SqlParameter[] insertParams;
    private SqlParameter[] updateParams;
    private SqlParameter[] DeleteParams;

    // The command objects that will be used by our data adapter when
    // interacting with the database
    private SqlCommand insertComm;
    private SqlCommand updateComm;
    private SqlCommand deleteComm;

    /// <summary>
    /// Initialize a new instance of the DBOps class
    /// </summary>
    /// <param name="tableName">The name of the table that the object will be interacting with</param>
    public DBOps(string tableName)
    {
        // Initialize the SqlConnection object
        conn = new SqlConnection(connString);

        // Initialize our collection of DataTables
        ds = new DataSet();

        srcTable = tableName;

        // initialize the query strings
        selectQuery = string.Format("SELECT * FROM {0}", srcTable);
        insertQuery = string.Format("INSERT INTO {0}(fname, mname, lnmae) VALUES(@first, @middle, @last", srcTable);
        updateQuery = string.Format("UPDATE {0} SET fname=@first, mname=@middle, lname=@last WHERE ID=@id", srcTable);
        deleteQuery = string.Format("DELETE FROM {0} WHERE ID=@id", srcTable);

        // Initialize the collection of parameters for each query above
        insertParams = new SqlParameter[]
        {
            // new SqlParameter(@paramName, paramDataType, paramValueLength, DGVDataPropertyName);
            new SqlParameter("@first", SqlDbType.NVarChar, 100, "fname"),
            new SqlParameter("@middle", SqlDbType.NVarChar, 100, "mname"),
            new SqlParameter("@last", SqlDbType.NVarChar, 100, "lname")
        };

        updateParams = new SqlParameter[]
        {
            new SqlParameter("@first", SqlDbType.NVarChar, 100, "fname"),
            new SqlParameter("@middle", SqlDbType.NVarChar, 100, "mname"),
            new SqlParameter("@last", SqlDbType.NVarChar, 100, "lname"),
            new SqlParameter("@id", SqlDbType.Int, 100, "id")

        };

        DeleteParams = new SqlParameter[]
        {
            new SqlParameter("@id", SqlDbType.Int, 100, "id")
        };

        // Initialize the SqlCommand objects that will be used by the DataAdapter to modify the source table
        insertComm = new SqlCommand(insertQuery, conn);
        updateComm = new SqlCommand(updateQuery, conn);
        deleteComm = new SqlCommand(deleteQuery, conn);

        // Associate the parameters with the proper SqlCommand object
        insertComm.Parameters.AddRange(insertParams);
        updateComm.Parameters.AddRange(updateParams);
        deleteComm.Parameters.AddRange(DeleteParams);

        // Give the DataAdapter the commands it needs to be able to properly update your database table
        da = new SqlDataAdapter()
        {
            InsertCommand = insertComm,
            UpdateCommand = updateComm,
            DeleteCommand = deleteComm
        };
    }

    /// <summary>
    /// Retrieve the data from the SQl table
    /// </summary>
    /// <returns></returns>
    public DataSet GetData()
    {
        DataSet ds = new DataSet();

        // Connect to the database and get the data from the "contacts" table
        using (conn)
        {
            conn.Open();
            using (SqlDataAdapter da = new SqlDataAdapter(selectQuery, conn))
            {
                da.Fill(ds); // Add the rows from the "contacts" table to our dataset
            }
        }

        return ds;
    }

    /// <summary>
    /// Commit the changes present in the object's DataSet to the Database
    /// </summary>
    public void UpdateData(DataSet ds)
    {
        // Calling the Update method executes the proper command based on the modifications to the specified
        // DataTable object
        da.Update(ds.Tables[srcTable]);
    }

To use this class, just create an instance of it by writing:

DBOps ops = new DBOps("contacts");

In your Update Button's click event handler, you can commit all the changes done to your DGV's underlying data source by calling the UpdateData method.

private void Btn_Update_Click(object sender, EventArgs e)
{
    // Grab the DGV's data source which contains the information shown in the DGV
    DataSet ds = (DataSet)dgv_items.DataSource;
    // Have any updates to the said dataset committed to the database
    ops.UpdateData(ds);
    // rebind the DGV
    dgv_items.DataSource = ops.GetData();
}

To sum it all up:

  • The DataAdapter and DataReader objects provide you methods that will allow you to interact with the database in safe, efficient and easy manner.
  • DataTable and DataSet are pretty much the same. Except the DataTable is only one table and the DataSet is a collection of DataTables. On the other hand, each of these also have specific methods that the other doesn't have.
C.RaysOfTheSun
  • 706
  • 1
  • 6
  • 9
  • 1
    I would not recommend newing up the adapter, dataset and commands/parameters every time and `AcceptChanges` is already called when you `Update()`. – Crowcoder Sep 21 '18 at 17:18
  • 1
    Ah, correct. I’ve totally forgotten about that. On the other hand, I used to breakdown stuff like this and put them in a class I called DatabaseOps which I also suggest the OP do as well. Should I edit? Thanks for the feedback too @Crowcoder :) – C.RaysOfTheSun Sep 21 '18 at 22:12
  • @Crowcoder please would you mind to post an answer with example and few explanation please I have seen some comments using acceptChanges but it was clear enough for me .. thank you in advance – sam Sep 22 '18 at 08:21
  • @C.RaysOfTheSun I suggest not to edit post another answer or update your original post with another way without changing the previous one please – sam Sep 22 '18 at 08:23
  • @sam I created that github repo so I could reference it when people ask this question (which is often). I would be duplicating effort and not explaining any better if I made an answer. Maybe create a github issue if something is unclear so I can improve it. Forget about `AcceptChanges` for now, you don't typically need to call it yourself. – Crowcoder Sep 22 '18 at 12:03
  • @Crowcoder github blocked from my iSP VPNs too I can hardly access it – sam Sep 22 '18 at 12:16
  • @C.RaysOfTheSun sorry my friend I was waiting for the holiday to work on it first I want to mention I had to use `@` instead of `$` at line selectQuery = $"SELECT * FROM {tableName}"; if you wish to update your code second I got an error on line `da.Fill(ds);` saying `{"Incorrect syntax near '}'."}` so I think using the brakets `{TableName}` is incorrect at that line `SelectQuery = @"SELECT * FROM {TableName}"; I tried to use `[TableName]` instead but I got error that says `Invalid object name 'TableName'.` so please what I hvae to do ? – sam Sep 27 '18 at 20:40
  • one last question what if I need to pass parameter to the select query? example '@"SELECT * FROM {TableName} WHERE ID=@ID"` I have made ` SelectParams = new SqlParameter[] {new SqlParameter("@SEANCE_ID", SqlDbType.Int,100,"SEANCE_ID")};` and `SelectComm = new SqlCommand(SelectQuery, conn);` and `SelectComm.Parameters.AddRange(SelectParams);` does it enough ? – sam Sep 27 '18 at 20:40
  • and can i use that without specifying `datatype` size ? which is 100 at this line `new SqlParameter("@ID", SqlDbType.Int,100,"ID"),` – sam Sep 27 '18 at 20:43
  • @C.RaysOfTheSun also the `ops.UpdateData(ds);` has error of `No overload for method UdpateData` – sam Sep 27 '18 at 21:52
  • 1
    @sam I've updated the code again. Sorry about that. String values prefixed with a $ indicates that it is a template string and the space in-between the curly braces are where you put variables. While strings prefixed with an @ symbol indicate that it is to be interpreted as is (Checkout [`verbatim string literals`](https://stackoverflow.com/questions/3311988/what-is-the-difference-between-a-regular-string-and-a-verbatim-string)). – C.RaysOfTheSun Sep 28 '18 at 00:49
  • 1
    On the other hand, if you want to pass a value to the select query, you'd have to do something like: `string selectQuery = $"SELECT * FROM {tableName} WHERE ID=@id;` `SqlCommand selectCommand = new SqlCommand(selectQuery);` `selectCommand.Parameters.AddWithValue("id", 1);` – C.RaysOfTheSun Sep 28 '18 at 00:50
  • 2
    If you want to pass a value to an SqlCommand object without having to specify a data type, the constructor for the SqlParameter object has an overload wherein only the name of the parameter "@name" and its supposed value is needed, The value that will be serving as the second argument to this can be anything from a variable to something taken from a textbox. – C.RaysOfTheSun Sep 28 '18 at 00:54
  • @C.RaysOfTheSun is template string available in `.net4` ms 2013 ? because I getting error on `$` that says `Unexpected character '$' ` – sam Sep 28 '18 at 07:31
  • @C.RaysOfTheSun OMG if that true https://stackoverflow.com/a/31514795/340277 here are some suggested solution https://stackoverflow.com/a/32010632/340277 but now I am afraid about in compatibility within my project any work around ? – sam Sep 28 '18 at 07:35
  • Just make use of `string.format()` instead of template strings. Answer's updated :) – C.RaysOfTheSun Sep 28 '18 at 08:44
0

If I understand right, you just need to execute the same query that will repopulate "refresh" the grid, i.e. OnClick - executes the code above. I do not really understand your if clause? If there is new? New data? You want to add it to the database? You can add that functionallity to this OnClick event to write to the DB whatever is present, doesn't matter changed or not, it really depends on how much of data you are presenting, so the solution may vary.

Kris
  • 322
  • 8
  • 19
0

I have come up with solution I made a method to get all data and add it to DataTable within a Dataset as you can see in below example

public DATEaSet GetDATEa()
{
    string connStr = ConfigurationManager.ConnectionStrings["connstr"].ToString();
    string cmdStr = @"SELECT  SEQ,
                                       ID,
                                       DATE,
                                       Started,
                                       END,
                                       TYPE,
                                       ENTRANCE,
                                       OUTGOING
                               FROM LOGING
                        WHERE SICK_ID=@ID;";

    SqlConnection conn = new SqlConnection(connStr);
    using (SqlCommand cmd = new SqlCommand(cmdStr, conn))
    {
        try
        {
            conn.Open();
            cmd.CommandText = cmdStr;
            cmd.CommandType = CommandType.Text;

            ds = new DATEaSet();
            cmd.Parameters.Add(new SqlParameter("@ID", SqlDbType.Int)).Value = Convert.ToInt32(TB_ID.Text);
            da = new SqlDATEaAdapter(cmd);

            da.Fill(ds, "DATEaTable1");
            MyDGV.Columns["MyDGV_RowNum"].DATEaPropertyName = ds.Tables["DATEaTable1"].Columns["SEQ"].ColumnName;
            MyDGV.Columns["MyDGV_SessionID"].DATEaPropertyName = ds.Tables["DATEaTable1"].Columns["ID"].ColumnName;
            MyDGV.Columns["MyDGV_DATEe"].DATEaPropertyName = ds.Tables["DATEaTable1"].Columns["DATE"].ColumnName;
            MyDGV.Columns["MyDGV_StartTime"].DATEaPropertyName = ds.Tables["DATEaTable1"].Columns["Started"].ColumnName;
            MyDGV.Columns["MyDGV_EndTime"].DATEaPropertyName = ds.Tables["DATEaTable1"].Columns["END"].ColumnName;
            MyDGV.Columns["MyDGV_Type"].DATEaPropertyName = ds.Tables["DATEaTable1"].Columns["TYPE"].ColumnName;
            MyDGV.Columns["MyDGV_CreatedBy"].DATEaPropertyName = ds.Tables["DATEaTable1"].Columns["ENTRANCE"].ColumnName;
            MyDGV.Columns["MyDGV_EntryDATEe"].DATEaPropertyName = ds.Tables["DATEaTable1"].Columns["OUTGOING"].ColumnName;

            return ds;
        }
        catch (Exception ex)
        {
            string ErrorMsg = ex.Message.Substring(0, Math.Min(ex.Message.Length, 1024));
            MessageBox.Show(ErrorMsg);
            return null;
        }
    }
}

On the Form_Load event I have set the DataSource of the Datagridview to the DS returned from the method above

Note that in some cases you have to set DataMember explicitly

MyDGV.DataSource = GetPatientSessions();
MyDGV.DataMember = "DATEaTable1";

now when user edit or add row to datagridview and then press the save button the method below will update edit it data and insert new row entered

private void BTN_Save_Click(object sender, EventArgs e)
{
    using (SqlCommandBuilder cmdbuilder = new SqlCommandBuilder(da))
    {
        try
        {
            da.Update(ds, "DATEaTable1");
        }
        catch (Exception ex)
        {
            string ErrorMsg = ex.Message.Substring(0, Math.Min(ex.Message.Length, 1024));
            MessageBox.Show(ErrorMsg);
        }
    }
}
sam
  • 2,493
  • 6
  • 38
  • 73