I've been trying to use the same SqlConnection and SqlCommand objects to execute to different commands.
the first one checks for duplicate and the second one inserts the data if the data the user entered is not a duplicate.
Here's a sample of my code:
using (SqlConnection conn = new SqlConnection(ConnStr))
{
string Command = "SELECT CountryName FROM [Countries] WHERE CountryName = #Name";
using (SqlCommand comm = new SqlCommand(Command, conn))
{
comm.Parameters.Add("#Name", System.Data.SqlDbType.NVarChar, 20);
comm.Parameters["#Name"].Value = Name;
comm.Parameters.Add("#IsVisible", System.Data.SqlDbType.Bit);
comm.Parameters["#IsVisible"].Value = IsVisible;
conn.Open();
if (comm.ExecuteScalar() == null)
{
Command = "INSERT INTO [Countries] (CountryName, IsVisible) VALUES (#Name, #IsVisible);";
comm.ExecuteNonQuery();
}
}
I was trying to save a trip to the database by using one connection.
The Problem is:
The first command runs okay but the
second command which inserts into the
database won't work (it doesn't add
any records to the db) and when I
tried to display the rows affected it
gave me -1 !!
The Question is:
Is this is the ideal way to check for
a duplicate records to constraint a
unique country ? and why the second
command is not executing ?
You are changing the value of string Command, but you are never actually changing the command string in SqlCommand comm.
When you rewrite the Command variable with the insert statement, you are simply modifying the string named Command that you've defined earlier. You are not modifying the command text stored inside of the SqlCommand object.
Try:
comm.CommandText = "INSERT INTO [Countries] (CountryName, IsVisible) VALUES (#Name, #IsVisible);";
To answer your first question: no, this is not the way to ensure uniqueness for country name. In your database, you should define your Countries table so that CountryName is the primary key (alternatively, you can declare some other column as the PK and define a unique constraint on CountryName).
The attempt to insert a duplicate value, then, will throw an exception, which you can handle appropriately (discard the existing record, overwrite it, prompt the user for a different value etc.).
Checking for uniqueness via your method is considered bad because A) it places logic that belongs in the database itself into your application's code; and B) it introduces a potential race condition, wherein some other application or thread inserts a value in between your read of the database and your write to it.
I thing i suggest to seperate the insert with your select statement..
someting like:
private void Insert()
{
using (SqlConnection conn = new SqlConnection(ConnStr))
{
string Command = "INSERT INTO [Countries] (CountryName, IsVisible) VALUES (#Name, #IsVisible)";
using (SqlCommand comm = new SqlCommand(Command, conn))
{
comm.Parameters.Add("#Name", System.Data.SqlDbType.NVarChar, 20);
comm.Parameters["#Name"].Value = Name;
comm.Parameters.Add("#IsVisible", System.Data.SqlDbType.Bit); comm.Parameters["#IsVisible"].Value = IsVisible;
conn.Open();
comm.ExecuteNonQuery();
conn.Close();
}
}
private void SelectInsert()
{
using (SqlConnection conn = new SqlConnection(ConnStr))
{
string Command = "SELECT CountryName FROM [Countries] WHERE CountryName = #Name";
using (SqlCommand comm = new SqlCommand(Command, conn))
{
comm.Parameters.Add("#Name", System.Data.SqlDbType.NVarChar, 20);
comm.Parameters["#Name"].Value = Name;
conn.Open();
if (comm.ExecuteScalar() == null)
{
Insert(); //your save method
}
}
}
Regards
Related
I have a table with CREATED and MODIFIED columns. I only want to insert the CREATED value once and have it be thereafter immutable. I know how to do this in a tedious way (write a "DoesRecordExist()" method and then alter the query and number of query parameters based on that), but surely there is a slicker way to accomplish this. After all, this has to be a common requirement (a "database pattern" if you will).
My code is this:
public void InsertUserSiteRecord(UserSite us)
{
using (SQLiteConnection conn = new SQLiteConnection(HHSUtils.GetDBConnection()))
{
conn.Open();
using (SQLiteCommand cmd = new SQLiteCommand(conn))
{
cmd.CommandText =
String.Format(
#"INSERT INTO UserSite (SiteNum, SerialNum, UserName, Created, Modified)
VALUES (#SiteNum, #SerialNum, #UserName, #Created, #Modified)");
cmd.Parameters.Add(new SQLiteParameter("SiteNum", us.SiteNum));
cmd.Parameters.Add(new SQLiteParameter("SerialNum", us.SerialNum));
cmd.Parameters.Add(new SQLiteParameter("UserName", us.UserName));
cmd.Parameters.Add(new SQLiteParameter("Created", us.Created));
cmd.Parameters.Add(new SQLiteParameter("Modified", us.Modified));
cmd.ExecuteNonQuery();
}
conn.Close();
}
}
...and I want to avoid having to do something like this:
public void InsertUserSiteRecord(UserSite us)
{
using (SQLiteConnection conn = new SQLiteConnection(HHSUtils.GetDBConnection()))
{
conn.Open();
using (SQLiteCommand cmd = new SQLiteCommand(conn))
{
if (!RecordExists(us.SiteNum, us.SerialNum, us.UserName))
{
cmd.CommandText =
String.Format(
#"INSERT INTO UserSite (SiteNum, SerialNum, UserName, Created, Modified)
VALUES (#SiteNum, #SerialNum, #UserName, #Created, #Modified)");
else
{
cmd.CommandText =
String.Format(
#"INSERT INTO UserSite (SiteNum, SerialNum, UserName, Modified)
VALUES (#SiteNum, #SerialNum, #UserName, #Modified)");
}
cmd.Parameters.Add(new SQLiteParameter("SiteNum", us.SiteNum));
cmd.Parameters.Add(new SQLiteParameter("SerialNum", us.SerialNum));
cmd.Parameters.Add(new SQLiteParameter("UserName", us.UserName));
if (!RecordExists(us.SiteNum, us.SerialNum, us.UserName))
{
cmd.Parameters.Add(new SQLiteParameter("Created", us.Created));
}
cmd.Parameters.Add(new SQLiteParameter("Modified", us.Modified));
cmd.ExecuteNonQuery();
}
conn.Close();
}
}
private bool RecordExists(String SiteNum, String SerialNum, String UserId)
{
// query the table to see if those three values exist in any record
}
Is there a SQL[ite] construct that is something like:
cmd.Parameters.AddOnlyIfColumnIsEmpty(new SQLiteParameter("Created", us.Created));
? Or how can this be best tackled?
You need to check for existance of the record; then do an UPDATE if it exists (not changing your Created value), and an INSERT if it doesn't.
Your original code will give a duplicate key error, assuming you have a PK on the table.
You may be able to accomplish this using a trigger. See the SQLite documentation on triggers here: https://www.sqlite.org/lang_createtrigger.html
Basically, you would create an INSTEAD OF trigger, then set up your query accordingly. From the documentation:
For an example of an INSTEAD OF trigger, consider the following schema:
CREATE TABLE customer(
cust_id INTEGER PRIMARY KEY,
cust_name TEXT,
cust_addr TEXT
);
CREATE VIEW customer_address AS
SELECT cust_id, cust_addr FROM customer;
CREATE TRIGGER cust_addr_chng
INSTEAD OF UPDATE OF cust_addr ON customer_address
BEGIN
UPDATE customer SET cust_addr=NEW.cust_addr
WHERE cust_id=NEW.cust_id;
END;
With the schema above, a statement of the form:
UPDATE customer_address SET cust_addr=$new_address WHERE cust_id=$cust_id;
Causes the customer.cust_addr field to be updated for a specific customer entry that has customer.cust_id equal to the $cust_id parameter. Note how the values assigned to the view are made available as field in the special "NEW" table within the trigger body.
What you would want to do is just set up the trigger to not update that column even if the original query passes in that column to be updated.
I was wondering if it is possible for the update button to save the changes made in the table. I wrote this code but I have no idea how it could possibly work
This is the code i wrote for the update button:
string conString = "Data Source=MIRANDA-PC;Initial Catalog=Futebol do Rosa;Integrated Security=True";
SqlConnection con = new SqlConnection(conString);
string selectSql = "Update Players$ set Player Name='" + dataGridView2.Text + "";
SqlCommand cmd = new SqlCommand(selectSql, con);
con.Open();
This is the table I want to update the values in:
Well, you just need to execute your query with ExecuteNonQuery.
But more important, you should always use parameterized queries. This kind of string concatenations are open for SQL Injection attacks.
Also use using statement to dispose your SqlConnection and SqlCommand.
And if your table or column names more than one word, you need to use them with [] as [Player Name]. And honestly, it is a little bit weird to use $ sign in a table name.
using(SqlConnection con = new SqlConnection(conString))
using(SqlCommand cmd = con.CreateCommand())
{
cmd.CommandText = "Update Players$ set [Player Name] = #name";
cmd.Parameters.Add("#name", SqlDbType.NVarChar, 16).Value = dataGridView2.Text;
con.Open();
cmd.ExecuteNonQuery();
}
You have to execute your SQL query with your db object.
dbinstance.ExecuteSqlCommand(string sqlcommand, object[] params);
This method is both for DDL and DML.
you can also use ExecuteNonQuery method.
cmd.CommandText = "Update Players$ set [Player Name] = #Playername";
cmd.Parameters.Add("#Playername", SqlDbType.NVarChar, 16).Value = dataGridView2.Text;
con.Open();
cmd.ExecuteNonQuery();
The best solution (if possible) to to convert your DAL (Data Access Layer) to Entity-framework based, instead of writing your own SQL queries. This is safe-by-design and never is vulnerable to SQL Injection of any kind.
Here is some mockup code:
using (AppEntities currDb = new AppEntities)
{
Players PlayerToEdit =
from player in currDb.Players
where player.PlayerID == lngPlayerID
select player.First();
PlayerToEdit.PlayerName = dataGridView2.Text;
currDb.SaveChanges();
}
You can read about it some more here:
https://msdn.microsoft.com/en-us/data/ef.aspx
I'm trying to solve why my code isn't working. Tip is preciated. I'm also wondering, when this works, will the Primary key, in this case, the ID columns also reset and start all over from 1?
connection = new SqlConnection(connectionString);
connection.Open();
sql = "DELETE * From Guests";
sqlCommand = new SqlCommand(sql, connection);
sqlCommand.EndExecuteNonQuery();
connection.Close();
You don't need the asterisk
DELETE FROM Guests
To reset the primary key, use
TRUNCATE TABLE Guests
And you want
sqlCommand.ExecuteNonQuery();
not EndExecuteNonQuery
You don't need the "*". The correct syntax for a delete statement is:
delete from Guests
You should also get into the habit of using "using" for disposable types, like SqlConnection and SqlCommand. Like this:
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
string sql = "DELETE From Guests";
using (SqlCommand sqlCommand = new SqlCommand(sql, connection))
{
sqlCommand.ExecuteNonQuery();
}
}
I'm using the following code to clear a database table:
public void ClearAll()
{
SqlCommand info = new SqlCommand();
info.Connection = con;
info.CommandType = CommandType.Text;
info.CommandText = "edit_.Clear()";
}
Why does it not work?
With a sql command you usually pass a TSQL statement to execute. Try something more like,
SqlConnection con = new SqlConnection(ConfigurationSettings.AppSettings["con"]);
SqlCommand cmd = new SqlCommand();
cmd.CommandText = "DELETE FROM Edit_ ";
cmd.Connection = con;
con.Open();
cmd.ExecuteNonQuery();
con.Close();
You need to execute the command, so info.Execute() or info.ExecuteNonQuery().
Try info.CommandText='DELETE FROM edit_';
The CommandText attribute is the TSQL statement(s) that are run.
You also need a info.ExecuteNonQuery();
1) Decide whether to use a TRUNCATE or a DELETE statement
Use TRUNCATE to reset the table with all its records and indexes:
using (SqlCommand command = connection.CreateCommand())
{
command.CommandType = CommandType.Text;
command.CommandText = "TRUNCATE TABLE [dbo].[Edit_]";
command.ExecuteNonQuery();
}
Use DELETE to delete all records but do not reset identity/auto increment columns
using (SqlCommand command = connection.CreateCommand())
{
command.CommandType = CommandType.Text;
command.CommandText = "DELETE FROM [dbo].[Edit_]";
command.ExecuteNonQuery();
}
Note that there is another line in the samples. In the sample you provided the SQL statement never gets executed until you call one of the ExecuteXXX() methods like ExecuteNonQuery().
2) Make sure you use the correct object (are you sure its called edit_?). I recommend to put the schema before the table name as in the examples before.
3) Make sure you use the correct connection string. Maybe everything worked fine on the production environment ;-)
I am getting exception: "Specific cast is not valid", here is the code
con.Open();
string insertQuery = #"Insert into Tender (Name, Name1, Name2) values ('Val1','Val2','Val3');Select Scope_Identity();";
SqlCommand cmd = new SqlCommand(insertQuery, con);
cmd.ExecuteNonQuery();
tenderId = (int)cmd.ExecuteScalar();
In the interests of completeness, there are three issues with your code sample.
1) You are executing your query twice by calling ExecuteNonQuery and ExecuteScalar. As a result, you will be inserting two records into your table each time this function runs. Your SQL, while being two distinct statements, will run together and therefore you only need the call to ExecuteScalar.
2) Scope_Identity() returns a decimal. You can either use Convert.ToInt32 on the result of your query, or you can cast the return value to decimal and then to int.
3) Be sure to wrap your connection and command objects in using statements so they are properly disposed.
using (SqlConnection connection = new SqlConnection(connectionString))
{
using (SqlCommand command = new SqlCommand(sql, connection))
{
connection.Open();
int tenderId = (int)(decimal)command.ExecuteScalar();
}
}
Try this:-
con.Open();
string insertQuery = #"Insert into Tender (Name, Name1, Name2) values ('Val1','Val2','Val3');Select Scope_Identity();";
SqlCommand cmd = new SqlCommand(insertQuery, con);
tenderId = Convert.ToInt32(cmd.ExecuteScalar());
EDIT
It should be this as it is correctly pointed out that scope_identity() returns a numeric(38,0) :-
tenderId = Convert.ToInt32(cmd.ExecuteScalar());
Note: You still need to remove the:-
cmd.ExecuteNonQuery();
Test the following first:
object id = cmd.ExcuteScalar()
Set a break point and have a look at the type of id. It is probably a Decimal and cannot directly be casted to int.
it needs Convert.ToInt32(cmd.ExecuteScalar());