I'll start off by saying that I'm pretty inept at coding (have yet to learn how to utilize classes and how they work in depth), and that I've never worked with sql before doing this project.
The idea here is that you connect to an sql database, after which a datagridview element gets filled with data from a table called TABLE_1 by default. The user should then be able to input, delete and save data.
The first two work operations work perfectly, but the saving is the problem. I've banged my head against a wall for about 4 days trying to get the saving to work, but I just cant get it to do so. The saving is done with the method Button3_click.
Any insight as to what I should do?
Is the main chunk of the code where you connect the part where I'm
messing up?
//initialize the classes I guess, on some youtube guides they did so
SqlConnection con;
DataSet ds;
SqlDataAdapter a;
DataTable t;
SqlCommandBuilder scb;
static string tablename = "TABLE_1";
string constring;
//connect button
private void button1_Click(object sender, EventArgs e)
{
try
{
//connect using info from textboxes, connection is ok
constring = "server=" + Server.Text + ";database=" + DB.Text + ";UID=" + UID.Text + ";password=" + Password.Text;
SqlConnection con = new SqlConnection(constring);
con.Open();
DataSet ds = new DataSet();
Save.Enabled = true;
//check if table name is empty, if so default to TABLE_1
if (textBox1.Text != "")
{
tablename = textBox1.Text;
}
else
{
tablename = "TABLE_1";
}
a = new SqlDataAdapter("SELECT * FROM " + tablename, con);
DataTable t = new DataTable();
a.Fill(t);
datagrid.DataSource = t;
//
label5.Text = Server.Text;
con.Close();
}
catch(Exception es)
{
MessageBox.Show(es.Message);
}
}
//save button
private void button3_Click(object sender, EventArgs e)
{
SqlConnection con = new SqlConnection(constring);
con.Open();
DataSet ds = new DataSet();
DataTable t = new DataTable();
a.TableMappings.Add(tablename, "t");
scb = new SqlCommandBuilder(a);
a.Update(t);
con.Close();
}
Use the following example which sets up the adapter, dataset and table name as private variables.
Also, I recommend to never use one letter variable names as a) it's difficult to debug b) when getting into more lines of code it's going to be difficult to know what a variable is for.
Next up, using SELECT *, it' unknown if you have setup a auto-incrementing primary key which is needed to perform updates. In the example below, Id is auto-incrementing.
using System;
using System.Data;
using System.Data.SqlClient;
using System.Windows.Forms;
namespace WindowsFormsDataAdapterExample
{
public partial class Form1 : Form
{
private SqlDataAdapter _companiesSqlDataAdapter;
private DataSet _companiesDataSet;
private string _tableName = "Companies";
public Form1()
{
InitializeComponent();
Shown += OnShown;
}
private void OnShown(object sender, EventArgs e)
{
_companiesDataSet = new DataSet();
_companiesSqlDataAdapter = new SqlDataAdapter(
"SELECT Id, FirstName, LastName, PhoneNumber, City FROM dbo.Companies;",
"Data Source=.\\sqlexpress;Initial Catalog=ForumExample;" +
"Integrated Security=True");
var builder = new SqlCommandBuilder(_companiesSqlDataAdapter);
_companiesSqlDataAdapter.Fill(_companiesDataSet, _tableName);
dataGridView1.DataSource = _companiesDataSet.Tables[_tableName];
}
private void SaveButton_Click(object sender, EventArgs e)
{
_companiesSqlDataAdapter.Update(_companiesDataSet, _tableName);
}
}
}
Related
I share with you a piece of code that works except the part where I'm trying to loop in the items of my listbox. That's why I'm here asking you for some help.
Lately, I switched from VBA to C# so I'm still new on this and don't undertsand everything yet.
So, the below code connect to my SQL server DB and fetch data both within my listbox and a DataGridView. I can filter with two textboxes also.
So now I have items within my listbox and my db's view within the DataGridview. I'd like to filter my DataGridview (which is filled by a datatable ) with my Listbox's item. I miss only a silly part I guess. Why Do I get this CS0246 "ListItem could not be found"
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Configuration;
using System.Data.SqlClient;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsAppTest
{
public partial class Form1 : Form
{
//Initialize the component and display the items within my listbox CS_Bonds_listBox
public Form1()
{
InitializeComponent();
string connetionString = #"Data Source=my_server;Initial Catalog=my_db;Integrated Security=SSPI";
SqlConnection conn = new SqlConnection(connetionString);
conn.Open();
DataSet ds = new DataSet();
SqlDataAdapter adapter = new SqlDataAdapter(
"SELECT DISTINCT RatingProvider FROM Bonds", conn);
adapter.Fill(ds);
this.CS_Bonds_listBox.DataSource = ds.Tables[0];
this.CS_Bonds_listBox.DisplayMember = "RatingProvider";
}
private void Form1_Load(object sender, EventArgs e)
{
}
DataTable dtTEST = new DataTable();
// Next, when clicking on my button Connect, I retrieve my db into a Datatable that is displayed within //the Datagridview1
private void buttonConnect_Click(object sender, EventArgs e)
{
string connetionString = #"Data Source=my_server;Initial Catalog=my_db;Integrated Security=SSPI";
SqlConnection cnn= new SqlConnection(connetionString);
cnn.Open();
MessageBox.Show("Connection Open !");
String sql = "Select * from Bonds";
SqlCommand command = new SqlCommand(sql, cnn);
SqlDataAdapter sqlDA = new SqlDataAdapter();
sqlDA.SelectCommand = command;
sqlDA.Fill(dtTEST);
dataGridView1.DataSource = dtTEST;
cnn.Close();
}
private void ISIN_Bonds_textBox_TextChanged(object sender, EventArgs e)
{
DataView dv = dtTEST.DefaultView;
dv.RowFilter = "ISIN LIKE '" + ISIN_Bonds_textBox.Text + "%'";
dataGridView1.DataSource = dv;
}
private void Ticker_Bonds_textBox_TextChanged(object sender, EventArgs e)
{
DataView dv1 = dtTEST.DefaultView;
dv1.RowFilter = "Ticker LIKE '" + Ticker_Bonds_textBox.Text + "%'";
dataGridView1.DataSource = dv1;
}
private void CS_Bonds_listBox_SelectedIndexChanged(object sender, EventArgs e)
{
string conString = #"Data Source=my_server;Initial Catalog=my_db;Integrated Security=SSPI";
string query = "SELECT ISIN, Ticker, CrediSight, FROM Bonds";
string condition = string.Empty;
foreach (ListItem item in CS_Bonds_listBox.Items)
{
condition += item.Selected ? string.Format("'{0}',", item.Value) : "";
}
if (!string.IsNullOrEmpty(condition))
{
condition = string.Format(" WHERE Country IN ({0})", condition.Substring(0, condition.Length - 1));
}
using (SqlConnection con = new SqlConnection(conString))
{
using (SqlCommand cmd = new SqlCommand(query + condition))
{
using (SqlDataAdapter sda = new SqlDataAdapter(cmd))
{
cmd.Connection = con;
using (DataTable dt = new DataTable())
{
sda.Fill(dt);
dataGridView1.DataSource = dt;
//dataGridView1.DataBind();
}
}
}
}
}
}
}
This line has a problem:
foreach (ListItem item in CS_Bonds_listBox.Items)
A ListItem is a WebForms thing, and your application is a WinForms thing; your listbox doesn't contain a list of ListItem objects so this line of code wouldn't work out anyway, even if the relevant web namespace was imported.
Because you've bound your listbox to a datatable the list it is showing is full of DataRowView objects, so that's what you need to process. A DataRowView has a Row property that gives you the underlying row, which in turn can be accessed by a column name.
Additionally, to make your life easier a listbox has a SelectedItems property so you don't need to check every item for being selected:
foreach (DataRowView drv in CS_Bonds_listBox.SelectedItems)
{
var dr = drv.Row as DataRow;
var rp = dr["RatingProvider"];
condition += $"'{rp}',"
}
Your condition will end up with a trailing comma as a result of this, so trim it off before you build an IN clause with it:
condition = condition.TrimEnd(',');
This technique could be susceptible to SQL Injection hacking if the user manages to change the text showing in the list items.
A better way to handle the problem is via parameterization. You'd do it like this:
var cmd = new SqlCommand("SELECT * FROM table WHERE Country IN(", connStr);
int i = 0;
foreach (DataRowView drv in CS_Bonds_listBox.SelectedItems)
{
var dr = drv.Row as DataRow;
var rp = dr["RatingProvider"];
cmd.CommandText += $"#p{i},";
cmd.Parameters.Add($"#p{i}", SqlDbType.VarChar).Value = rp;
i++;
}
cmd.CommandText = cmd.CommandText.TrimEnd(',') + ")";
using(var da = new SqlDataAdapter(cmd))
{
var dt = new DataTable();
da.Fill(dt);
someGridView.DataSource = dt;
}
This builds an sql that looks like SELECT * FROM table WHERE Country IN(#p0,#p1,#p2.... i.e. we have concatenated parameter placeholders in rather than concatenating values in. At the same time we have filled the parameters collection with the parameter values
It also means that our database can't be hacked via our program, and our app doesn't die in a heap when the user selects a country with a name like Cote d'Ivoire
Some other things to note to tidy your code up:
SqlDataAdapter can take a string SQL and a string connection-string. You don't need to make a SqlCommand for it. You don't need to open and close conenctions for it; it knows how to do all this itself. I only used a SqlCommand because I was building the parameters collection as I went. Ordinarily I'd do using(var da = SqlDataAdapter("SELECT...", "Server=..") because it makes things nice and tidy.
This means e.g. your constructor can be simply:
//put this here once
private string _connStr = #"Data Source=my_server;Initial Catalog=my_db;Integrated Security=SSPI";
public Form1()
{
InitializeComponent();
var dt = new DataTable();
using(var da = new SqlDataAdapter("SELECT DISTINCT RatingProvider FROM Bonds", _connStr))
adapter.Fill(dt);
this.CS_Bonds_listBox.DataSource = dt;
this.CS_Bonds_listBox.DisplayMember = "RatingProvider";
}
I want to search some input in a data table and if exact data is found then I want to put those data into another table. If not, I will simply clear the corresponding TextBox. I have done theses so far.
private void btn_InputConfirm_Click(object sender, EventArgs e) {
string strConnection = #"Data Source=F_NOOB-PC\;Initial Catalog=ComShopDB;Integrated Security=True";
SqlConnection objcon = new SqlConnection(strConnection);
try {
string strcmd1 = "SELECT partID,partAvailable FROM Parts WHERE partID LIKE '" + txtbox_ProductSerial.Text + "'AND partAvailable ='yes'";
SqlCommand objcmd1 = new SqlCommand(strcmd1, objcon);
objcon.Open();
objcmd1.ExecuteNonQuery();
objcon.Close();
}
catch (Exception ex) {
MessageBox.Show(ex.Message);
}
}
Some help will be very much appreciated. Thanks in advance.
You can use a DataTable, use ExecuteReader method and load all records into DataTable, then use AsEnumerable and some LINQ you can get your results as a List.
DataTable dt = new DataTable();
var reader = objcmd1.ExecuteReader();
if(reader.HasRows)
{
dt.Load(reader);
var myValues = dt.AsEnumerable()
.Select(d => new {
Id = d["partID"],
Available = d["partAvailable"]
}).ToList();
}
Also you should consider using parameterized queries instead to prevent SQL Injection Attacks.
The easiest is to use DataAdapter and then use its Fill() function on a DataTable or DataSet. You do not need to open and close the connection as the Fill() function will do that for you:
private void btn_InputConfirm_Click(object sender, EventArgs e)
{
string strConnection = #"Data Source=F_NOOB-PC\;Initial Catalog=ComShopDB;Integrated Security=True";
SqlConnection objcon = new SqlConnection (strConnection);
try
{
//Writing command//
string strcmd1 = "SELECT partID,partAvailable FROM Parts WHERE partID LIKE '" + txtbox_ProductSerial.Text + "'AND partAvailable ='yes'";
System.Data.SqlClient.SqlDataAdapter da = new System.Data.SqlClient.SqlDataAdapter(strcmd1, objcon);
System.Data.DataSet ds = new System.Data.DataSet();
aa.Fill(ds);
}
catch ( Exception ex )
{
MessageBox.Show (ex.Message);
}
Windows Form Application,
VS 2010 C#,
Access 2003
I am referencing from this website, here. My aim is to show updated records (insert/delete update) without closing and restarting the application to show new reflection. I have quite a few textboxes, comboxes etc, so I am not using datagridview.
From that website there are two pieces of code...
bindingNavigator1.BindingSource = bdSource;
this.dataGridView1.DataSource = bdSource;
So I did this...
this.table1BindingSource = dbSource;
and gave a error of "Object reference not set to an instance of an object." With a warning that dbSource is never assigned and will aways have a default value null.
my insert command parameter structure..
OleDbCommand cmd = new OleDbCommand(#"INSERT INTO Table1 (ID, Name) VALUES(#ID, #Name)", myCon);
cmd.CommandType = CommandType.Text;
cmd.Parameters.AddWithValue("#ID", txtID.Text);
cmd.Parameters.AddWithValue("#Name", txtName.Text);
myCon.Open();
cmd.ExecuteNonQuery();
myCon.Close();
The following method I am hoping data would be refreshed...
private void btnReload_Click(object sender, EventArgs e)
{
OleDbDataAdapter.Update(dtSource);
}
and...
public partial class Form1 : Form
{
private OleDbConnection myCon;
private string connectionString;
private string commandText;
private OleDbCommandBuilder OleDbCommandBuilder;
private OleDbDataAdapter OleDbDataAdapter;
private DataTable dtSource;
private BindingSource dbSource;
public Form1()
{
connectionString = #"Provider=Microsoft.Jet.OLEDB.4.0;Data.. ";
commandText = "SELECT * FROM TABLE1";
myCon = new OleDbConnection(connectionString);
OleDbDataAdapter = new OleDbDataAdapter(commandText, myCon);
OleDbCommandBuilder = new OleDbCommandBuilder(OleDbDataAdapter);
dtSource = new DataTable();
OleDbDataAdapter.Fill(dtSource);
dbSource = new BindingSource();
dbSource.DataSource = dtSource;
Thanks in advance for anyone who could help me
EDIT
I also have navigation buttons..
private void fnDisplayPosition()
{
this.label2.Text = this.table1BindingSource.Position + 1 + " of " +
this.table1BindingSource.Count;
table1BindingSource.ResetBindings(false);
}
then in the Form load...
private void Form1_Load(object sender, EventArgs e)
{
this.fnDisplayPosition();
}
Example of navigation button...
private void btnFirst_Click(object sender, EventArgs e)
{
this.table1BindingSource.MoveFirst();
this.fnDisplayPosition();
}
UPDATE 2
I did some error in coding so warning signs and errors are gone
As I understand, here you initalize bindingSource called bdSource
bdSource = new BindingSource();
But then you try to set the dataSource
dbSource.DataSource = dtSource;
for bindingSource called dbSource.You haven't initalized it yet. So, which one of them you are going to use?
Also, what is table1BindingSource in your code?
The issue was with tableadapters and bindingsource tied to texboxes and others alike. I have explained it here in full.
I have a datagrid to display some information from a SQL table, and then a simple textbox and button to allow users to add records to the database. Problem is, when the user clicks Add, the datagrid SHOULD update, but it doesn't, any ideas? The code in question is as follows:
protected void Page_Load(object sender, EventArgs e)
{
username.Text = Session["username"].ToString();
datetime.Text = DateTime.Now.ToString();
BindData();
}
protected void BindData()
{
string SQLQuery = "SELECT * From Filters";
OleDbConnection MyConn = new OleDbConnection(ConfigurationManager.ConnectionStrings["ConnStr"].ConnectionString);
DataSet resultsDataSet = new DataSet();
MyConn.Open();
OleDbDataAdapter DataAdapter = new OleDbDataAdapter(SQLQuery, MyConn);
DataAdapter.Fill(resultsDataSet);
DGFilters.DataSource = resultsDataSet;
DGFilters.DataBind();
if (resultsDataSet.Tables[0].Rows.Count == 0)
{
no_records.Visible = true;
DGFilters.Visible = false;
}
else
{
DGFilters.Visible = true;
no_records.Visible = false;
}
MyConn.Close();
}
protected void AddFilter_Click(object sender, EventArgs e)
{
OleDbConnection MyConn = new OleDbConnection(ConfigurationManager.ConnectionStrings["ConnStr"].ConnectionString);
MyConn.Open();
string SQLInsert = "INSERT INTO Filters (FilterString) VALUES ( '" + FilterToAdd.Text + "')";
OleDbCommand MyCmd = new OleDbCommand(SQLInsert, MyConn);
MyCmd.ExecuteNonQuery();
MyConn.Close();
DataBind();
}
Any ideas?
At the bottom of your AddFilter_Click method you need to call your own BindData() so the grid can be refreshed with the new record. Right now you're calling DataBind(), which is a method on the base class Control, which is being applied to your entire web form. I'm guessing this isn't doing much of anything.
Also, in your Page_Load method, you can probably change this:
BindData();
to
if (!Page.IsPostBack)
BindData();
so that you don't bind your grid twice when the user clicks on the 'add' button.
I create table called Department with 2 columns Department ID which is auto increment and Department Name
I create Navigate_Department() in order to walk through the department rows
System.Data.SqlClient.SqlConnection con;
DataSet Dep_ds;
System.Data.SqlClient.SqlDataAdapter Dep_da;
int Dep_MaxRows = 0;
int Dep_inc = 0;
private void ILS_Load(object sender, EventArgs e)
{
con = new System.Data.SqlClient.SqlConnection();
con.ConnectionString = "Data Source=.\\SQLEXPRESS;AttachDbFilename=|DataDirectory|\\ILS_DB.mdf;Integrated Security=True;Connect Timeout=30;User Instance=True";
con.Open();
Dep_ds = new DataSet();
string sql2 = "select * from Department order by DepartmentID";
Dep_da = new System.Data.SqlClient.SqlDataAdapter(sql2, con);
Dep_da.Fill(Dep_ds, "Department");
Navigate_Department();
Dep_MaxRows = Dep_ds.Tables["Department"].Rows.Count;
}
private void Navigate_Department()
{
DataRow dRow = Dep_ds.Tables["Department"].Rows[Dep_inc];
Dep_ID.Text =dRow.ItemArray.GetValue(0).ToString();
Dep_Name.Text = dRow.ItemArray.GetValue(1).ToString();
}
private void Move_Next_Click(object sender, EventArgs e)
{
if (Dep_inc != Dep_MaxRows-1)
{
Dep_inc++;
Navigate_Department();
}
else
{
MessageBox.Show("No More Records");
}
}
private void Move_back_Click(object sender, EventArgs e)
{
if (Dep_inc > 0)
{
Dep_inc--;
Navigate_Department();
}
else
{
MessageBox.Show("First Record");
}
}
private void Dep_Clear_Click(object sender, EventArgs e)
{
Dep_ID.Clear();
Dep_Name.Clear();
}
private void Dep_Add_Click(object sender, EventArgs e)
{
try
{
SqlCommand insCmd = new SqlCommand("insert into dbo.Department (DepartmentName) values ('" + Dep_Name.Text + "')", con);
Dep_da.InsertCommand = insCmd;
Dep_MaxRows = Dep_MaxRows + 1;
Dep_inc = Dep_MaxRows - 1;
Dep_Max.Text = Dep_MaxRows.ToString();
Dep_Current.Text = (Dep_MaxRows).ToString();
}
catch (Exception exceptionObject)
{
MessageBox.Show(exceptionObject.Message);
}
The problem is:
After I click clear button, I insert the department name into Dep_Name textbox then click add button. The name that I inserted didn’t get saved in the database, and if I click move back then move next in order to see what I inserted, I get a Index out of range exception in the Navigate_Department() method.
So did I make any mistake?
The reason the name you're inserting isn't saved in the database is because you never execute the insert command. Add:
int ret = Dep_da.Update(Dep_ds);
after the
Dep_da.InsertCommand = insCmd;
The variable ret will contain the number of rows successfully updated (inserted) - which in your case should be 1.
To add to what #N. Warfield wrote, if you simply append raw data into your SQL string that a user (or another system, for that matter) provides, you leave yourself wide-open to SQL injection attacks.
Rather than create the insert statement like this you should use the data adapter with an insert command, add a new DataRow instance to the table then use the data adapter to execute the update.
Alternatively you could execute the insert command within Dep_Add_Click by replacing the call to "Dep_da.InsertCommand = insCmd" with "insCmd.ExecuteNonQuery", however this would mean that you would need to re-run the select statement and repopulate the dataset from the database to get the data from the database into the dataset.