I have table called Inventory, and I want to remove the first row of it. For that purpose I have created a class named InventoryDAL. Here's code:
public class InventoryDAL
{
private string cnString = string.Empty;
private SqlDataAdapter da = null;
public InventoryDAL(string connectionString)
{
cnString = connectionString;
da = new SqlDataAdapter("Select CarID, Make, Color, PetName From Inventory",
connectionString);
SqlCommandBuilder builder = new SqlCommandBuilder(da);
da.DeleteCommand = builder.GetDeleteCommand();
da.InsertCommand = builder.GetInsertCommand();
da.UpdateCommand = builder.GetUpdateCommand();
}
public DataTable Inventory()
{
DataTable dt = new DataTable();
da.Fill(dt);
return dt;
}
public void UpdateInventory(DataTable modifiedTable)
{
da.Update(modifiedTable);
}
}
Also I created small program to try it:
class Program
{
static void Main(string[] args)
{
InventoryDAL inv = new InventoryDAL(#"Data Source=MYPC;Initial Catalog=AutoLot;Integrated Security=True;Pooling=False");
DataTable dt = inv.Inventory();
dt.Rows.RemoveAt(0);
inv.UpdateInventory(dt);
Console.ReadKey(true);
}}
But it's not working. After some attempts I realized that .Update() works only when I insert data.
Using DataTable.RemoveAt() completely removes the row from a DataTable object, so the SqlDataAdapter doesn't know to delete it at the data source. You have to use the DataTable.Rows[x].Delete() method. This marks the row for deletion so the adapter then knows to call the SQL delete statement on it.
So your code should become:
class Program
{
static void Main(string[] args)
{
InventoryDAL inv = new InventoryDAL(#"Data Source=MYPC;Initial Catalog=AutoLot;Integrated Security=True;Pooling=False");
DataTable dt = inv.Inventory();
dt.Rows[0].Delete();
inv.UpdateInventory(dt);
Console.ReadKey(true);
}
}
See here for more on how changes get pushed back to the data source.
Related
I want to create a class that I can get datatable of specific table given in the parameter of the constructor.
But how I can do it without it being vulnerable to SQL Injections?
I would've done something like this: (But you can't use those statements like that)
public class SqlClass
{
DataTable dt;
string table;
public SqlClass(String table)
{
this.table = table;
SqlConnection c = new SqlConnection();
c.conOpen();
MySqlCommand msc = new MySqlCommand("SELECT * FROM #tableName", c.Con); //Cant do that
msc.Parameters.AddWithValue("#tableName", table);
DataSet ds = c.getDataSet(msc, table);
this.dt = ds.Tables[table];
c.conClose();
}
public DataTable Dt { get => dt; set { dt = value; } }
}
Any other working alternative that won't harm me? Thanks.
Using the following code the first row in SQL is printed only, How can I print selected row from DataGridView in C# with SQL:
public partial class PrintScreen : Form
{
SqlConnection con = new SqlConnection(#"Server = TEST;DataBase=Registration;Integrated Security=true;");
SqlDataAdapter da;
DataTable dt = new DataTable();
public PrintScreen()
{
InitializeComponent();
da = new SqlDataAdapter("select * from data_graduation", con);
da.Fill(dt);
this.dataGridView1.DataSource = dt;
}
private void Print_ys_ar_cert_Click(object sender, EventArgs e)
{
Print_ys_ar_cert frm = new Print_ys_ar_cert();
da = new SqlDataAdapter("select * from data_graduation where ID_gra = '" + dataGridView1.CurrentRow.Cells[0].Value.ToString() + "'", con);
da.Fill(frm.RegistrationDataSet1.data_graduation);
frm.reportViewer2.RefreshReport();
frm.Show();
}
}
Currently you are passing value of Cells[0] of first row of the grid, you need to pass value of selected row:
if(dataGridView1.SelectedRows.Count>0)
{
var selectedValue = dataGridView1.SelectedRows[0].Cells[0].ToString();
//rest of code
}
Since you have loaded the row from database once, you don't need to load it again from database, you can simply add it to the data table which is data source of the report this way:
var row = ((DataRowView)(dataGridView1.SelectedRows[0].DataBoundItem)).Row;
frm.RegistrationDataSet1.data_graduation.Rows.Add(row.ItemArray);
//rest of code
I am a newbie in C# so I don't know if I will address my problem correctly so please bear with me. I have 3 DataGridViews (datagridview1, datagridview2, datagridview3). All are located in the same window but they are in a different tab (I have a tab control).
The purpose of each DataGridView is to display data for three tables from database. So every time I click a button, it retrieves data. But here is my problem, when datagridview1 displays the data after clicking the button, then I go to the next tab and click again the retrieve button, the datagridview2 shows the data that was displayed to datagridview1. Same thing to datagridview3.
I'm using DataTable as the data source for those DataGridViews. And somewhere in my script the query will change so I think there's no problem with the query. What I found is that the DataTable does not clear it's data even when the query already changed.
I'm using WinForms, please help me. Thanks.
Here is the code I used in binding the datagridview to a datasource:
currentdatagrid.DataSource = execute.InitConn2(query, CompleteTablename);
Note: "execute.InitConn2(query, CompleteTablename)" will return a datatable.
Try doing something like the following example and see if it works for you. The static method GetData returns a new data table each time. You need to update the SqlConnection with your own connection string.
public static void Main(string[] args)
{
DataGrid dg1 = new DataGrid();
DataGrid dg2 = new DataGrid();
DataGrid dg3 = new DataGrid();
dg1.DataSource = GetData("select * from table1");
dg1.DataBind();
dg2.DataSource = GetData("select * from table2");
dg2.DataBind();
dg3.DataSource = GetData("select * from table3");
dg3.DataBind();
}
public static DataTable GetData(string sqlQuery) {
try
{
DataTable dt = new DataTable();
// set your connection here
SqlConnection conn = new SqlConnection("");
// execute query with your connection
SqlDataAdapter adapt = new SqlDataAdapter(sqlQuery, conn);
// open connection, fill data and close
conn.Open();
adapt.Fill(dt);
conn.Close();
return dt;
}
catch (Exception ex) {
throw ex;
}
}
To use a dataset use the following:
public static void Main(string[] args)
{
DataGrid dg1 = new DataGrid();
DataGrid dg2 = new DataGrid();
DataGrid dg3 = new DataGrid();
DataSet ds = GetData(#"select * from table1;
select * from table2;
select * from table3");
dg1.DataSource = ds.Tables[0];
dg1.DataBind();
dg2.DataSource = ds.Tables[1];
dg2.DataBind();
dg3.DataSource = ds.Tables[2];
dg3.DataBind();
}
public static DataSet GetData(string sqlQuery) {
try
{
DataSet ds = new DataSet();
// set your connection here
SqlConnection conn = new SqlConnection("");
// execute query with your connection
SqlDataAdapter adapt = new SqlDataAdapter(sqlQuery, conn);
// open connection, fill data and close
conn.Open();
adapt.Fill(ds);
conn.Close();
return ds;
}
catch (Exception ex) {
throw ex;
}
}
I use a PostgreSQL view to display data in a DataGridView:
dataSource = new BindingSource();
dataSource.DataSource = Program.DB.GetView(dbView); // returns a DataTable
dgData.DataSource = dataSource;
Now, after I added a record using a PostgreSQL function, I refresh the data in the grid (I don't call Rows.Add() on the DataGridView:
protected void RefreshData() {
dataSource.DataSource = Program.DB.GetView(dbView);
}
The insertion function of PostgreSQL returns the ID of the inserted row, so I know the ID (which is primary key) and want to set Selected to true in the DataGridView. The row can be anywhere in the set, because the view is sorted by name, not ID. I reckon I could do that by cycling over all rows and set the Selected when I found it, but this could become slow on large datasets.
Is there a way to somehow bind the rows of the datasource to the datagrid?
Add an event handler to the ListChanged event:
dataSource.ListChanged += dataSource_ListChanged;
Here is the event handler definition:
void dataSource_ListChanged(object sender, ListChangedEventArgs e)
{
if (dgData.Rows.Count > 0)
dgData.CurrentCell = dgData.Rows[e.NewIndex].Cells[0];
}
[UPDATE]
As I also suggested in the comments, maybe you should not repopulate the data source on each insert (or update). To demonstrate my point, I'll post a code sample which uses a DataGridView, 2 TextBoxes and 2 Buttons (for insert and update) and a SqlDataAdapter. The SQL table has 2 columns (id and value).
Here's the code (I didn't cover the deletion):
public partial class Form1 : Form
{
static BindingSource dataSource;
static string dbView = "default";
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
dgData.MultiSelect = false;
dataSource = new BindingSource();
dataSource.ListChanged += dataSource_ListChanged;
RefreshData();
dgData.DataSource = dataSource;
dgData.Sort(dgData.Columns[1], ListSortDirection.Ascending);
}
void dataSource_ListChanged(object sender, ListChangedEventArgs e)
{
if (dgData.Rows.Count > 0)
dgData.CurrentCell = dgData.Rows[e.NewIndex].Cells[0];
}
protected void RefreshData()
{
dataSource.DataSource = DB.GetView(dbView);
}
private void insert_Click(object sender, EventArgs e)
{
DB.Insert(textBox1.Text);
RefreshData();
}
private void update_Click(object sender, EventArgs e)
{
DB.UpdateRandomRow(textBox2.Text);
RefreshData();
}
}
class DB
{
static DataTable dt;
static string conStr = "yourConnectionString";
static SqlDataAdapter _adapter;
static Random r = new Random(10);
public static SqlDataAdapter CreateSqlDataAdapter(SqlConnection connection)
{
_adapter = new SqlDataAdapter();
_adapter.MissingSchemaAction = MissingSchemaAction.AddWithKey;
_adapter.SelectCommand = new SqlCommand(
"SELECT * FROM test", connection);
_adapter.InsertCommand = new SqlCommand(
"INSERT INTO test (value) " +
"VALUES (#value)", connection);
_adapter.UpdateCommand = new SqlCommand(
"UPDATE test SET [value] = #value " +
"WHERE id = #id", connection);
_adapter.InsertCommand.Parameters.Add("#value",
SqlDbType.NVarChar, 50, "value");
_adapter.UpdateCommand.Parameters.Add("#id",
SqlDbType.Int, 4, "id").SourceVersion = DataRowVersion.Current;
_adapter.UpdateCommand.Parameters.Add("#value",
SqlDbType.NVarChar, 50, "value").SourceVersion = DataRowVersion.Current;
return _adapter;
}
// random update, to demonstrate dynamic
// repositioning
public static DataTable UpdateRandomRow(string value)
{
var currentRandom = r.Next(dt.Rows.Count);
dt.Rows[currentRandom].SetField<string>(1, value);
using (var con = new SqlConnection(conStr))
{
con.Open();
_adapter = CreateSqlDataAdapter(con);
_adapter.Update(dt);
}
return dt;
}
internal static DataTable GetView(string dbView)
{
if (dt == null)
{
dt = new DataTable();
using (var con = new SqlConnection(conStr))
{
con.Open();
_adapter = CreateSqlDataAdapter(con);
_adapter.Fill(dt);
}
}
return dt;
}
internal static void Insert(string value)
{
if (dt == null)
GetView("");
var dr = dt.NewRow();
dr[1] = value;
dt.Rows.Add(dr);
using (var con = new SqlConnection(conStr))
{
con.Open();
_adapter = CreateSqlDataAdapter(con);
_adapter.Update(dt);
}
}
}
If you test it, you'll see that the request from your question is satisfied for both insert and update. Also, please note the performance improvement, as the data table is not recreated on each operation.
I want to add search functionality to my program. There's a class which has this function:
public DataTable Search()
{
string SQL = "Select * from Customer where " + mField + " like '%" + mValue + "%'";
DataTable dt = new DataTable();
dt = dm.GetData(SQL);
return (dt);
}
There are setter and getter properties for mField and mValue. DM is the object of class DataManagement, which has a method GetData:
public DataTable GetData(string SQL)
{
SqlCommand command = new SqlCommand();
SqlDataAdapter dbAdapter = new SqlDataAdapter();
DataTable DataTable = new DataTable();
command.Connection = clsConnection.GetConnection();
command.CommandText = SQL;
dbAdapter.SelectCommand = command;
dbAdapter.Fill(DataTable);
return (DataTable);
}
The search functionality is currently implemented like this:
private void btnfind_Click(object sender, EventArgs e)
{
//cust is the object of class customer//
if (tbCustName.Text != "")
{
cust.Field="CustName";
cust.Value = tbCustName.Text;
}
else if (tbAddress.Text != "")
{
cust.Value = tbAddress.Text;
cust.Field="Address";
}
else if (tbEmail.Text != "")
{
cust.Value = tbEmail.Text;
cust.Field="Email";
}
else if (tbCell.Text != "")
{
cust.Value = tbCell.Text;
cust.Field = "Cell";
}
DataTable dt = new DataTable();
dt = cust.Search();
dgCustomer.DataSource = dt;
RefreshGrid();
}
private void RefreshGrid()
{
DataTable dt = new DataTable();
dt = cust.GetCustomers();
dgCustomer.DataSource = dt;
}
This is not working. I don't know why. Please help.
Add a DataBind() statement in your RefreshGrid() method to have your new results actually shown on the Grid.
private void RefreshGrid()
{
DataTable dt = cust.GetCustomers();
dgCustomer.DataSource = dt;
dgCustomer.DataBind();
}
Consider modifying your other method as well:
Your ad-hoc SQL has a SQL injection vulnerability. Stop everything until you fix that!
btnfind_Click doesn't need to end up calling cust.Search() twice.
private void btnfind_Click(object sender, EventArgs e)
{
//<snip>
// no need to do all this twice.
// DataTable dt = new DataTable();
// dt = cust.Search();
// dgCustomer.DataSource = dt;
RefreshGrid();
}
Your RefreshGrid method is overwriting the DataSource you set in btnfind_Click... don't call it, just call DataBind
private void btnfind_Click(object sender, EventArgs e)
{
...
DataTable dt = cust.Search();
dgCustomer.DataSource = dt;
dgCustomer.DataBind();
}
By the way, you don't need to assign a new DataTable to dt if you're immediately setting it to the result of cust.Search... you're just creating an instance for nothing (I fixed it in the code above)