Updating and persisting dataset problem - c#

I think I'm missing sth. trivial here : I want to update a dataset and push it back to the database where it came from, but I am keep getting a :
Concurrency violation: the
UpdateCommand affected 0 of the
expected 1 records.
Here's some code producing this error :
public static void UpdateNorthWindWithDataset()
{
string connString =
#"Data Source=localhost;Initial Catalog=NorthWind;Integrated Security=SSPI;";
using (SqlConnection conn = new SqlConnection(connString))
{
conn.Open();
// Declaring a DataAdapter and initiating it with a Select and updateCommand
SqlDataAdapter da = new SqlDataAdapter();
SqlCommand selectCmd = new SqlCommand("SELECT CustomerId, City, Region " +
"FROM Customers"
, conn
);
da.SelectCommand = selectCmd;
SqlCommand updateCmd = new SqlCommand(
#"UPDATE Customers SET City='#City', Region='#Region'" +
#"WHERE CustomerID = '#CustomerID'",
conn
);
updateCmd.Parameters.AddRange(
new SqlParameter[]
{
new SqlParameter()
{
ParameterName = "#CustomerID",
SourceColumn = "customerid"
},
new SqlParameter()
{
ParameterName = "#City",
SourceColumn = "city",
SqlDbType = SqlDbType.VarChar
},
new SqlParameter()
{
ParameterName = "#Region",
SourceColumn = "region",
SqlDbType = SqlDbType.VarChar
}
}
);
da.UpdateCommand = updateCmd;
// filling dataset
DataSet ds = new DataSet();
da.Fill(ds, "srcCustomers");
// declaring and editing datatable
DataTable tblCustomers = ds.Tables["srcCustomers"];
foreach (DataRow row in tblCustomers.Rows)
{
row["City"] = "justUpdated";
row["Region"] = "justUpdated too";
}
da.Update(ds, "srcCustomers");
}
}
Now, my endgoal is using this kind of code with MsAccess throug OLEdb, but because I wanted it as clear as possible, I tried MSSQL (still 2k here) with native .net support but still got the error...

The update is failing because it can't find a record that matches the customer ID supplied and I think that that is because the parameter value is not being defaulted - there are additional values for the SQL parameters that will allow you to do this.
If you're looking at OLEDB you need to be aware that the parameters are not named (you can and probably should name them, but they will be used in the order they are entered and not according to their names - this also means you can't use the same parameter twice which can be a bit tedious).

Related

ASP.NET getting data from SQL Server

I am trying to get the name of the employee from the database and fill it in the textbox for the respective employee id.
I tried this code but nothing is happening on the page. It just reloads and the textbox (name) is left blank only.
SqlConnection con = new SqlConnection(#"Data Source=DESKTOP-0FUUV7B\SQLEXPRESS;Initial Catalog=EmployeeDetails;Integrated Security=True");
con.Open();
           
SqlCommand cmd = new SqlCommand("select * from ProfessionalDetails where EmpId='"+EmployeeId.Text+"'", con);
          
SqlDataReader da = cmd.ExecuteReader();
while (da.Read())
{
    Name.Text = da.GetValue(1).ToString();
}
            
con.Close();
Better solution is to execute the sql statement through Parameterized value.
The details of that process is given below:
using (SqlConnection con = new SqlConnection(live_connectionString))
{
using (SqlCommand cmd = new SqlCommand("Query", con))
{
con.Open();
cmd.CommandType = CommandType.Text;
cmd.Parameters.AddWithValue("#EmpId", employeeId);
SqlDataAdapter da = new SqlDataAdapter();
da.SelectCommand = cmd;
var ds = new DataSet();
da.Fill(ds);
string? name = ds.Tables[0].Rows[1]["Variable name"].ToString();
Name.Text =name;
};
}
}
As mentioned above in comments, you have lot of issues.
you should use using with the connection to dispose of them.
You should use parameterized queries to avoid SQL injection.
Put your code in try catch so that you can easily identify the root cause of the issue.
Define the connection string in config file three than defining in the c# code.
You don’t need to select all the columns. And please avoid select * in the query, instead just write your column name, as you want to select only one column here.
You can use ExecuteScalar, it’s used when you are expecting single value.
And first make sure that textbox has the expected value when you are calling this query.
As noted, use paramters, and BETTER use STRONG typed paramters.
And no need to use a dataset, this is a single table - so use a datatable.
thus:
string strSQL =
#"select * from ProfessionalDetails where EmpId= #ID";
using (SqlConnection con = new SqlConnection(Properties.Settings.Default.TEST4))
{
using (SqlCommand cmd = new SqlCommand(strSQL, con))
{
con.Open();
cmd.Parameters.Add("#ID", SqlDbType.Int).Value = EmployeeID.Text;
DataTable rstData = new DataTable();
rstData.Load(cmd.ExecuteReader());
if (rstData.Rows.Count > 0)
Name.Text = rstData.Rows[0]["Name"].ToString();
}
}

SQLConnection bringing data to TextBox

I know this looks really simple but i've been looking for an answer for hours with no luck.
I want to fill my row values into a bunch of textboxes. How can I specify that [CompanyName] is going to be used by the companyName textbox? Please keep it as simple as possible (beginner level).
string customerUniqueID = "test";
string constr = ConfigurationManager.ConnectionStrings["SQLConnection"].ToString(); // connection string
SqlConnection con = new SqlConnection(constr);
con.Open();
SqlCommand com = new SqlCommand("SELECT * FROM [Customers] WHERE [UniqueID] = #UniqueID", con); // table name
com.Parameters.Add("#UniqueID", SqlDbType.Int);
com.Parameters["#UniqueID"].Value = customerUniqueID;
SqlDataAdapter da = new SqlDataAdapter(com);
DataSet ds = new DataSet();
companyName.Text = ?????????
string customerUniqueID = "test";
string constr = ConfigurationManager.ConnectionStrings["SQLConnection"].ToString(); // connection string
SqlConnection con = new SqlConnection(constr);
con.Open();
SqlCommand com = new SqlCommand("SELECT * FROM [Customers] WHERE [UniqueID] = #UniqueID", con); // table name
com.Parameters.Add("#UniqueID", SqlDbType.Int);
com.Parameters["#UniqueID"].Value = customerUniqueID;
SqlDataAdapter da = new SqlDataAdapter(com);
DataSet ds = new DataSet();
da.Fill(ds, "Customers");
companyName.Text = ds.Tables[0].Rows[0]["CompanyName"].ToString();
I will recommend some changes in your code:
Your sql query returning result from one set, so you can use DataTabe instead of DataSet.
To fill results from DB to your DataTable you can use SqlAdapter.Fill() method.
Use Field() generic method (more examples of Field()) to get values from your DataTable.
Use using blocks for disposable objects, or at least make sure you've closed them after.
There is no need of con.Open() to open connection when using Fill() method, because from MSDN:
The Fill method implicitly opens the Connection that the DataAdapter is using if it finds that the connection is not already open. If Fill opened the connection, it will also close the connection when Fill is finished. This can simplify your code when dealing with a single operation such as a Fill or an Update.
string customerUniqueID = "test";
string constr = ConfigurationManager.ConnectionStrings["SQLConnection"].ToString(); // connection string
using(SqlConnection con = new SqlConnection(constr))
{
SqlCommand com = con.CreateCommand();
com.CommandText = "SELECT * FROM [Customers] WHERE [UniqueID] = #UniqueID";
com.Parameters.Add("#UniqueID", SqlDbType.Int);
com.Parameters["#UniqueID"].Value = customerUniqueID;
using(SqlDataAdapter da = new SqlDataAdapter(com))
{
DataTable dt = new DataTable();
da.Fill(dt);
companyName.Text = dt.Rows[0].Field<string>("CompanyName");
}
}
Please feel free to comment, if I missed something.

SQL Values to integers

I'm trying to compare values in a database that are updated every time a user logs in. When I execute a query with the given code nothing happens. However if I give it a value of say (where Attempt >10) it works where am I going wrong?
private void User_Tick(object sender, EventArgs e)
{
SqlConnection con13 = new SqlConnection("Data Source = *** ")
SqlDataAdapter SDA2 = new SqlDataAdapter("SELECT [User],[Login],[number1],[number2],[number3],[Alertcount] FROM Users.dbo.[Email] where [Alertcount] = 1 and [Alertcount] !=2", con13);
DataTable Users = new DataTable();
DataTable DATA2 = new DataTable();
SDA2.Fill(DATA2);
dataGridView2.DataSource = DATA2;
foreach (DataGridViewRow dr in dataGridView2.Rows)
{
string col2 = 1.Cells["User"].Value.ToString();
string col1 = 1.Cells["Login"].Value.ToString();
string col3 = 1.Cells["number1"].Value.ToString();
string col4 = 1.Cells["number2"].Value.ToString();
string col5 = 1.Cells["number3"].Value.ToString();
string col6 = 1.Cells["Alertcount"].Value.ToString();
var mine = Convert.ToInt32(col3);
var mine2 = Convert.ToInt32(col5);
SqlConnection CON2 = new SqlConnection("Data Source = ***")
CON2.Open();
SqlDataAdapter SDA = new SqlDataAdapter("SELECT [User],[Login],[Attempt] FROM User.dbo.Actions where [Attempt] > '"+mine+ "' and [Attempt] < '" + mine2 + "'", CON2);
DataTable DATA = new DataTable();
SDA.Fill(DATA);
dataGridView1.DataSource = DATA;
}
}
If column Attempt is an integer (as evident from the fact that Attempt < 10 runs), you need not pass comparison values to it in string. So your query should be like this:
SqlDataAdapter SDA = new SqlDataAdapter("SELECT [User],[Login],[Attempt] FROM User.dbo.Actions where [Attempt] > "+mine+ " and [Attempt] < " + mine2 , CON2);
I would suggest you to debug such errors in future by creating a query variable and then running the query in SQL manually to see what the error is. You could do something like this:
var query = "SELECT [User],[Login],[Attempt] FROM User.dbo.Actions where [Attempt] > "+mine+ " and [Attempt] < " + mine2 ;
SqlDataAdapter SDA = new SqlDataAdapter(query , CON2);
nothing happens - not enough information for correct answer. If actually nothing happens, then remove all try catch blocks you have around code and run application again. Then if something wrong you will get very useful information about what gone wrong in the form of Exception.
However, problem seems is that you passing wrong data to database query.
Always use SqlParameter for passing dynamic data to the query. SqlParameter have type which you can set to correspondent type of column you want operate on. Also SqlParameter will protect you from sql injection.
Use using for disposable objects when ever it possible (read "always")
var emailQuery =
#"SELECT [User] ,[Login] ,[number1] ,[number2] ,[number3] ,[Alertcount]
FROM Users.dbo.[Email]
WHERE [Alertcount] = 1
AND [Alertcount] !=2"; // Useless condition, because Alertcount already = 1
using(var connection2 = new SqlConnection("Data Source = *** "))
using(var adapter2 = new SqlDataAdapter(emailQuery, connection1))
{
var data2 = new DataTable();
adapter2.Fill(data2);
dataGridView2.DataSource = data2;
}
var actionsQuery =
#"SELECT [User] ,[Login] ,[Attempt]
FROM User.dbo.Actions
WHERE Attempt > #Mine AND Attempt < #Mine2";
foreach (var row in dataGridView2.Rows)
{
var mine = (int)row.Cells["number1"].Value; // it is already integer, just cast it
var mine2 = (int)row.Cells["number3"].Value;
using(var connection1 = new SqlConnection("Data Source = *** "))
using(var adapter1 = new SqlDataAdapter(actionsQuery, connection1))
{
var parameters = new[]
{
new SqlParameter
{
ParameterName = "#Mine",
SqlDbType = SqlDbType.Int,
Value = mine
},
new SqlParameter
{
ParameterName = "#Mine2",
SqlDbType = SqlDbType.Int,
Value = mine2
}
};
adapter1.SelectCommand.Parameters.AddRange(parameters);
var data1 = new DataTable();
adapter.Fill(data1);
dataGridView1.DataSource = data1
}
}

Insert new DataRow with autoincrement Primary Key with C# (OleDB)

I' m trying to insert a new DataRow (locally stored in a DataSet) to a Access table with C#. The table is also created by this app and has 3 columns:
ID (integer, primary key, Required is set to No though I've set it as primary key)
Name (string)
Money (int)
My current code looks like that:
OleDbConnection con = new OleDbConnection();
OleDbCommand cmd = new OleDbCommand();
OleDbDataAdapter da;
DataSet ds = new DataSet();
DataView dv = new DataView();
DataRow row;
string Con = #"Provider=Microsoft.Jet.OLEDB.4.0;" + "Data Source=";
string path = "V:\\ProjectProgress\\Test.mdb";
con.ConnectionString = Con + path;
if (con.State == ConnectionState.Closed)
{
con.Open();
}
cmd.Connection = con;
cmd.CommandText = "SELECT ID, Name, Money FROM Test";
da = new OleDbDataAdapter(cmd);
da.TableMappings.Add("Table", "Test");
da.Fill(ds, "Test");
ds.Tables["Test"].Columns[0].AutoIncrement = true;
ds.Tables["Test"].Columns[0].AutoIncrementSeed = -1;
ds.Tables["Test"].Columns[0].AutoIncrementStep = -1;
dv.Table = ds.Tables["Test"];
row = ds.Tables["Test"].NewRow();
row["Name"] = "Huber";
row["Money"] = 100;
ds.Tables["Test"].Rows.Add(row);
string strOLE = "INSERT INTO Test ([Name], [Money]) Values(#Name, #Money)";
OleDbCommand cmdi = new OleDbCommand(strOLE, con);
cmdi.Parameters.Add("#Name", OleDbType.VarChar, 25, "Name");
cmdi.Parameters.Add("#Money", OleDbType.Integer, 4, "Money");
da.InsertCommand = cmdi;
da.Update(ds.Tables["Test"]);
con.Close();
When updating I'm always getting a
Index or primary key cannot contain a Null value
error.
Setting the Required value of the ID column to Yes, will throw a
Index or Primary Key Cannot Contain a Null Value
error.
How can I let Access assign the right primary key and how do I get the new value back into my dataset?
Using SCOPE_IDENTITY() is not possible in Access as far as I know and found in some forums.
(Working with Visual C# Express 2010, Access 2003)
The following is complete working test code to illustrate the procedure. All we need to do is provide an OleDbDataAdapter.SelectCommand that includes the primary key and the columns we want to update, and the OleDbCommandBuilder object will create the INSERT statement for us:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.OleDb;
namespace oleDbTest
{
class Program
{
static void Main(string[] args)
{
string myConnectionString;
myConnectionString =
#"Provider=Microsoft.ACE.OLEDB.12.0;" +
#"Data Source=C:\Users\Public\Database1.accdb;";
using (OleDbConnection con = new OleDbConnection())
{
con.ConnectionString = myConnectionString;
con.Open();
using (OleDbDataAdapter da = new OleDbDataAdapter())
using (OleDbCommandBuilder bld = new OleDbCommandBuilder(da))
{
bld.QuotePrefix = "["; // these are
bld.QuoteSuffix = "]"; // important!
da.SelectCommand = new OleDbCommand(
"SELECT [ID], [Name], [Money] " +
"FROM [Test] " +
"WHERE False",
con);
using (System.Data.DataTable dt = new System.Data.DataTable("Test"))
{
// create an empty DataTable with the correct structure
da.Fill(dt);
System.Data.DataRow dr = dt.NewRow();
dr["Name"] = "Huber";
dr["Money"] = 100;
dt.Rows.Add(dr);
da.Update(dt); // write new row back to database
}
}
con.Close();
}
Console.WriteLine();
Console.WriteLine("Done.");
}
}
}
My first thought is that your problem might be that both AutoIncrementSeed and AutoIncrementStep are set to a negative value of -1. Try setting both to a positive value.
[EDIT]
Second thought, you might want to try the OleDbCommandBuilder (MSDN documentation here). It creates your INSERT, UPDATE, and DELETE statements automatically using the column information of your DataSet.
At my former employer we used OleDbCommandBuilder all the time when working with Access and it worked like a charm, even with auto increment fields.

Get records between two days which are selected by two datetimepickers and fill a datagridview with them in Visual Studio C#

This is my code:
This is in a different class named DBAccess
public DataSet getRecords(DateTime dtpFloor,DateTime dtpCeiling)
{
if (conn.State.ToString() == "Closed")
{
conn.Open();
}
SqlCommand newCmd = conn.CreateCommand();
newCmd.Connection = conn;
newCmd.CommandType = CommandType.Text;
newCmd.CommandText = " SELECT * FROM dbo.ClientInvoice WHERE invDate BETWEEN '" + dtpCeiling + "' AND '" + dtpFloor + "'";
SqlDataAdapter da = new SqlDataAdapter(newCmd);
DataSet dsIncome = new DataSet();
da.Fill(dsIncome, "Client");
conn.Close();
return dsIncome;
}
Below Coding is in the ProfitLos form class
public void btnClickFillGrid()
{
DataSet dsIncome = dba.getRecords(dtpFloor.Value.ToString(), dtpCeiling.Value.ToString()); //dba is an object of DBAccess class
dgvproIncome.DataSource = dsIncome.Tables["Client"].DefaultView;
}
btnClickFillGrid() will invoke at the button click event.
In the database - invdate datetime;(invDate is the variable name and its in the datetime format)
i edited my coding like this
public DataSet getRecords(DateTime dtpFloor,DateTime dtpCeiling)
{
using (SqlConnection conn = new SqlConnection("Data Source=KOSHITHA-PC;Initial Catalog=ITP;Integrated Security=True"))
{
conn.Open();
using (SqlCommand command = conn.CreateCommand())
{
string sql = "SELECT * FROM dbo.ClientInvoice WHERE invDate BETWEEN" + "#from AND #to";
command.CommandText = sql;
command.Parameters.AddWithValue("#from",dtpFloor);
command.Parameters.AddWithValue("#to", dtpCeiling);
SqlDataAdapter da = new SqlDataAdapter(command);
DataSet dataSetClient = new DataSet();
da.Fill(dataSetClient, "Client");
return dataSetClient;
}
}
}
DataSet dataSetClient = dba.getRecords(dtpFloor.Value, dtpCeiling.Value);
dgvproIncome.DataSource = dataSetClient.Tables["Client"].DefaultView;
now i m getting an exception in "da.Fill(dataSetClient, "Client");" line saying
sqlException was unhandled
An expression of non-boolean type specified in a context where a condition is expected, near 'BETWEEN#from'.
i m not familiar with the parameter passing method to sql query,so couldnt find the problem that i m having
Look at this call:
dba.getRecords(dtpFloor.Value.ToString(), dtpCeiling.Value.ToString());
That's clearly passing in strings as the arguments. Now look at your method declaration:
public DataSet getRecords(DateTime dtpFloor,DateTime dtpCeiling)
Those parameters are of type DateTime, not string. So the first thing to fix is the call, to:
dba.getRecords(dtpFloor.Value, dtpCeiling.Value);
Now the next problem is that you're embedding the values in the SQL directly. Don't do that. Never do that. In some cases it can lead to SQL injection attacks, and in other cases it causes data conversion issues (as you've got here). Use parameterized SQL instead - oh, and use connection pooling rather than trying to use a single connection in multiple places:
public DataSet GetRecords(DateTime dtpFloor,DateTime dtpCeiling)
{
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
using (SqlCommand command = conn.CreateCommand())
{
string sql = "SELECT * FROM dbo.ClientInvoice WHERE invDate BETWEEN "
+ "#from AND #to";
command.CommandText = sql;
command.Parameters.AddWithValue("#from", dtpFloor");
command.Parameters.AddWithValue("#to", dtpCeiling");
SqlDataAdapter da = new SqlDataAdapter(command);
DataSet dataSet = new DataSet();
da.Fill(dataSet, "Client");
return dataSet;
}
}
}

Categories