I'm just learning databases to store a (large amount) of user entry data.
I have the following code, which checks a record and chooses whether to update or create new
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
string sc1 = #"select count(*) from job1 where report = #report";
SqlCommand check = new SqlCommand(sc1, connection);
check.Parameters.AddWithValue("#report", jname);
// check if the report number already exists, if not make a new table otherwise insert
int test = (int)check.ExecuteScalar();
if (test > 0)
{
jobCardExistingTable(connection);
digCardExistingTable(connection);
//insert into existing table code
}
If I use either jobCardExistingTable or digCardExisting table, they work fine. If I use both, I get the error
require open and available connection
I assume that the first ExecuteNonQuery (which are contained in the jobCard and digCard methods) is doing something with the connection - can I keep this one open, or do I have to open a new one each time I call a method? Maybe I'm doing this all wrong anyways...each method is calling a new table in the database, should I be calling them all at once?
Edit: part of the issue is jobCardTable (digCardTable is identical, just a different query)
public void jobCardNewTable(SqlConnection connection)
{
using (connection)
{
string sc3 = "";
sc3 = #"INSERT INTO job1 (" + pv.jobstring + ") VALUES (" + pv.jobparam + ")";
SqlCommand cmd = new SqlCommand(sc3, connection);
queryParams(cmd, 0);
cmd.ExecuteNonQuery();
}
}
Edit: solved - realised that using{} disposes the connection. Took all the using{} out of the methods, and used a single using{} to encompass all the method calls and it works
You should not use using (connection) if you are using same connection in other part of code. using dispose connection and make unavailable for further connection.
So, your jobCardNewTable method implementation should be without using statement :
public void jobCardNewTable(SqlConnection connection)
{
string sc3 = "";
sc3 = #"INSERT INTO job1 (" + pv.jobstring + ") VALUES (" + pv.jobparam + ")";
SqlCommand cmd = new SqlCommand(sc3, connection);
queryParams(cmd, 0);
cmd.ExecuteNonQuery();
}
I would recommend to create new connection whenever you need it and dispose it.
Related
How to write tests for delegate methods?
or
Beware of 2 open connections both with 'hooks' onto the same SQL table .... .
This was not straight forward to diagnose, test and prove is not a problem with my current solution.
How could I have TDD'd or written unit/integration tests to have trapped this? Redesign suggestions ...
Create a connection to the table 'TransferWebTransmit' to process
all rows.
Execute a Reader to loop through 'old' records, (ID=1)
Call a delegate method to process the 'old' record. (NB keep current
connection open until all rows are processed i.e. have called the delegate).
Delegate method:
Opens a new connection, executes a Stored Proc 'TransferWebTransmitUpdate'
which -> Updates the table 'TransferWebTransmit' row (ID=1), then does a SELECT on (ID=1) row
----> cursor lock!
----> .Net throws "System.Data.SqlClient.SqlException (0x801
31904): Timeout expired. The timeout period elapsed prior to completion of the
operation or the server is not responding".
----> Connections are locked.
----> Have to Kill processes to recover
Here's the delegate method:
public int Update(int transferID)
{
var obj = new TransferWebMessage();
using (SqlConnection conn = base.GetNewConnection())
{
using (SqlCommand sp_cmd = new SqlCommand())
{
sp_cmd.CommandText = "TransferWebTransmitUpdate";
sp_cmd.CommandType = CommandType.StoredProcedure;
sp_cmd.Parameters.AddWithValue("TransferID", transferID);
sp_cmd.Connection = conn;
conn.Open();
SqlDataReader rdr = sp_cmd.ExecuteReader();
int roweffected;
while (rdr.Read())
{
roweffected = rdr.GetInt32(0),
}
}
}
return roweffected;
}
Here's the call to get the rows to process and call the delegate:
public void WatchForDataTransferRequests(_delegateMethod callback)
{
using (SqlConnection conn = new SqlConnection(_insol_SubscriberConnectionString))
{
// Construct the command to get any new ProductReview rows from the database along with the corresponding product name
// from the Product table.
SqlCommand cmd = new SqlCommand(
"SELECT [TransferID]" +
" FROM [dbo].[TransferWebTransmit]" +
" ORDER BY [TransferID] ASC", conn);
cmd.Parameters.Add("#CurrentTransferID", SqlDbType.Int);
conn.Open();
SqlDataReader rdr = cmd.ExecuteReader();
// Process the rows
while (rdr.Read())
{
Int32 transferID = (Int32)rdr.GetInt32(0);
callback(transferID);
}
}
}
public TransImport()
{
ConnString = ConfigurationManager.ConnectionStrings["Connection"].ConnectionString;
SqlConnection conn_new;
SqlCommand command_serial_new;
SqlConnection conn;
SqlCommand command_serial;
SqlTransaction InsertUpdateSerialNumbers;
conn = new SqlConnection(ConnString);
command_serial = conn.CreateCommand();
conn_new = new SqlConnection(ConnString);
command_serial_new = conn_new.CreateCommand();
command_serial_new.CommandText = "SELECT 1 FROM YSL00 WHERE SERLNMBR = #slnr";
var p = new SqlParameter("#slnr", SqlDbType.NVarChar, 50);
command_serial_new.Parameters.Add(p);
//Here you will start reading flat file to get serialnumber.
InsertUpdateSerialNumbers = conn.BeginTransaction();
while (!headerFileReader.EndOfStream)
{
headerRow = headerFileReader.ReadLine();
if (CheckSerialNumber(headerFields[0].Trim()))
DisplayMessage("Good serialnumber"); //this function is not copied here.
}
InsertUpdateSerialNumbers.Commit();
}
private Boolean CheckSerialNumber(string SerialNumber)
{
command_serial_new.Parameters["#slnr"].Value = SerialNumber;
try
{
var itExists = Convert.ToInt32(command_serial_new.ExecuteScalar()) > 0;
if (!itExists)
{
command_serial.Transaction = InsertUpdateSerialNumbers;
command_serial.CommandText = "INSERT INTO YSL00([Manifest_Number],[PONUMBER],[ITEMNMBR],[SERLNMBR]"
+ "VALUES ('" + Manifest + "','" + PONr + "','" + itemNumber + "','" + serialNr + "')";
var insertStatus = command_serial.ExecuteNonQuery();
return true;
}
}
catch (Exception ex)
{
LogException(ex, "Error in CheckSerialNumber =>"+ command_serial_new.CommandText.ToString());
}
return false;
}
I get error "Timeout expired. The timeout period elapsed prior to completion of the operation or server is not responding".
The CheckSerialNumber function also does an insert to YSL00 (the same table where I had executescalar. See code above).
As I mentioned earlier there are 1000s of line in a flat file that I read and update YSL000 table.
Note that I have two separate sqlcommands and also two separate connections to handle this. Reason is with sqltransaction it doesn't let me to query on the same table. I think timeout may be happening because of this?
Thanks for reading. Please suggest
Update 1: Since I have not pasted entire code, I want to mention that dispose is done using below code in the program.
if (conn != null)
{
conn.Close();
conn.Dispose();
}
if (conn_new != null)
{
conn_new.Close();
conn_new.Dispose();
}
you can increase the time out of your SqlConnection object.
you can do this with your ConnString:
string connStr = "Data Source=(local);Initial Catalog=AdventureWorks;Integrated
Security=SSPI;Connection Timeout=300";
I think default isolation level - read commited - is preventing your 'CheckSerialNumber' method from being effective. Command_serial_new will not take into consideration rows inserted in your loop - this might lead to some troubles. To be honest I would also look for some deadlock. Perhaps command_serial_new is actually completely blocked by the other transaction.
To start off:
Set command_serial_new query as:
SELECT 1 FROM YSL00 WITH (NOLOCK) WHERE SERLNMBR = #slnr
Think about using lower isolation level to query inserted rows as well (set it to read uncommited).
Close your connections and transactions.
Use just one SqlConnection - you don't need two of them.
Many of the objects you are using implement IDisposable, and you should be wrapping them with using statements. Without these using statements, .NET won't necessarily get rid of your objects until an undetermined time when the garbage collector runs, and could block subsequent queries if it's still holding a transaction open somewhere.
So for example, you'll need to wrap your connections with using statements:
using (conn_new = new SqlConnection(ConnString)) {
...
If I am not mistaken you need to merge the file content with the table content.
For this purpose I would recommend you
Copy the file content in to a temporary table (see temporary tables and BulkInsert)
Use command MERGE (http://msdn.microsoft.com/en-us/library/bb510625.aspx) to merge the temporary table content with the original table
I've a form opened which is has loaded some sort of data (like username, CNIC, Contact no, etc etc) in Check boxes, now I want to update the data in such manner that I simply change the text in the text boxes and click on the save changes to save it. I've tried it but I am not able to do it in correct manner.
Let me show you how I've coded, the code I did in frmViewformList savechanges button is :
private void btnSaveChanges_Click(object sender, EventArgs e)
{
string sql;
string UserName;
UserName = txtUserName.Text; // saving data loaded on run time to UserName
sql = "";
sql += "UPDATE UserLogin";
sql += "SET Name = "+ //how to access data I've changed in TextBox after loading +"";
sql += "WHERE Name= " + //how to access data which was in text box right after loading + ""; //
}
I am a bit confused about how to refer to data, like the name already in the text box or the name which I have changed and how to write it in SQL query...
This question is a bit confusing, I know. Let me explain; the form is loaded, there are text boxes which is being populated with the data in database on load event, I change the data in text boxes and save on click so that the update query runs and changes the data in database as well.
I'm not able to create logic here how to do this, can any one help me out, I am sorry I am a new developer of C# that's why I am a bit confused.
You should use Sql Parameters in order to avoid SQL Injection which could leave your database vulnerable to malicious exploitation.
It's a good idea to separate the logic for performing the update to the logic where you create your query so you don't have to repeat code and so that you can maintain your code easier.
Here is an example you can reference:
public void DoWork()
{
// Build Query Use #Name Parameters instead of direct values to prevent SQL Injection
StringBuilder sql = new StringBuilder();
sql.Append("UPDATE UserLogin");
sql.Append("SET Name = #UpdatedName");
sql.Append("WHERE Name = #Name");
// Create parameters with the value you want to pass to SQL
SqlParameter name = new SqlParameter("#Name", "whatEverOldNameWas");
SqlParameter updatedName = new SqlParameter("#UpdatedName", txtUserName.Text);
Update(sql.ToString(), new [] { name, updatedName });
}
private static readonly string connectionString = "Your connection string"
private static readonly DbProviderFactory factory = DbProviderFactories.GetFactory("System.Data.SqlClient");
public static int Update(string sql, SqlParameter[] parameters)
{
try
{
using (DbConnection connection = factory.CreateConnection())
{
connection.ConnectionString = connectionString;
using (DbCommand command = factory.CreateCommand())
{
command.Connection = connection;
command.CommandText = sql;
foreach (var parameter in parameters)
{
if (parameter != null)
command.Parameters.Add(parameter);
}
connection.Open();
return command.ExecuteNonQuery();
}
}
}
catch (Exception)
{
throw;
}
}
You will want to strip all ', ", and ` characters out of your input so that people can't inject SQL. When you do SET Name = " +, you'll want to actually wrap whatever you're including in quotes because it's a string: SET Name = '" + UserName "' " +...
This is probably best done using
string.Format("UPDATE UserLogin SET Name = '{0}' WHERE Name = '{1}'", UserName, FormerUserName);
Then you will execute your query by using System.Data.SqlClient; and then work with SqlConnection to establish a connection to the server, and execute a SqlCommand of some kind; take a look at: http://www.codeproject.com/Articles/4416/Beginners-guide-to-accessing-SQL-Server-through-C
The following is a code snippet to insert data into database using ADO.NET and assuming SQL Server database.
At the top of your .cs file you should have.
using System.Data.SqlClient; // for sql server for other data bases you should use OleClient instead.
And inside your button click event you could put the following.
// to know how to get the right connection string please check this site: http://www.connectionstrings.com
string connString = "database connection string here";
using (SqlConnection con = new SqlConnection(connString))
{
con.Open();
//insert text into db
string sql_insert = "INSERT INTO ....."; // Use parameters here.
SqlCommand cmd_insert = new SqlCommand(sql_insert, con);
int rowsAffected = cmd_insert.ExecuteNonQuery();
}
Hopefully this is enough to get you started.
I have the following code:
static void InsertRes(string Data)
{
string query = "INSERT INTO results (gamenum,result) " + Data;
query += ";";
//open connection
if (OpenConnection() == true)
{
//create command and assign the query and connection from the constructor
MySqlCommand cmd = new MySqlCommand(query, connection);
//Execute command
cmd.ExecuteNonQuery();
//close connection
CloseConnection();
}
}
I know it's not the best practices but I generate data within my application, how can I force this function to block until the query has been successfully executed upon the mysql database?
It will do that automatically.
cmd.ExecuteNonQuery(); will return after the insertion is finished.
That is standard programming behaviour. Doing multiple task simultaniously is where it gets tricky. Doing one thing after the other is just normal. Your program will continue to the next line only when the execution if the preivious line is finished.
I am using this code to delete a database through C#
Int32 result = 0;
try
{
String Connectionstring = CCMMUtility.CreateConnectionString(false, txt_DbDataSource.Text, "master", "sa", "happytimes", 1000);
SqlConnection con = new SqlConnection();
con.ConnectionString = Connectionstring;
String sqlCommandText = "DROP DATABASE [" + DbName + "]";
if (con.State == ConnectionState.Closed)
{
con.Open();
SqlConnection.ClearPool(con);
con.ChangeDatabase("master");
SqlCommand sqlCommand = new SqlCommand(sqlCommandText, con);
sqlCommand.ExecuteNonQuery();
}
else
{
con.ChangeDatabase("master");
SqlCommand sqlCommand = new SqlCommand(sqlCommandText, con);
sqlCommand.ExecuteNonQuery();
}
con.Close();
con.Dispose();
result = 1;
}
catch (Exception ex)
{
result = 0;
}
return result;
But I get an error
Database currently in use
Can anyone help?
Try this:
String sqlCommandText = #"
ALTER DATABASE " + DbName + #" SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
DROP DATABASE [" + DbName + "]";
Also make sure that your connection string defaults you to the master database, or any other database other than the one you're dropping!
As an aside, you really don't need all of that stuff around your queries. The ConnectionState will always start off Closed, so you don't need to check for that. Likewise, wrapping your connection in a using block eliminates the need to explicitly close or dispose the connection. All you really need to do is:
String Connectionstring = CCMMUtility.CreateConnectionString(false, txt_DbDataSource.Text, "master", "sa", "happytimes", 1000);
using(SqlConnection con = new SqlConnection(Connectionstring)) {
con.Open();
String sqlCommandText = #"
ALTER DATABASE " + DbName + #" SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
DROP DATABASE [" + DbName + "]";
SqlCommand sqlCommand = new SqlCommand(sqlCommandText, con);
sqlCommand.ExecuteNonQuery();
}
result = 1;
Here is how you do it using Entity Framework version 6
System.Data.Entity.Database.Delete(connectionString);
You should take a look at SMO.
These allow you to manage all aspects of SQL Server from code, including deleting of databases.
The database object has a Drop method to delete database.
Create sqlconnection object for different database other than you want to delete.
sqlCommandText = "DROP DATABASE [DBNAME]";
sqlCommand = new SqlCommand(sqlCommandText , sqlconnection);
sqlCommand.ExecuteNonQuery();
In this case i would recommend that you take the database offline first... that will close all connections and etc... heres an article on how to do it: http://blog.sqlauthority.com/2010/04/24/sql-server-t-sql-script-to-take-database-offline-take-database-online/
Microsoft clearly states that A database can be dropped regardless of its state: offline, read-only, suspect, and so on. on this MSDN article (DROP DATABASE (Transact-SQL))
Connection pooling at a guess, use sql server's activity monitor to make sure though.
Pooling keeps connections to the database alive in a cache, then when you create a new one, if there's one in the cache it hands it back instead of instantiating a new one. They hang around for a default time, (2 minutes I think) if they don't get re-used in that time, then they killed off.
So as a first go connect straight to master, instead of using change database, as I suspect change database will simply swap connections in the pool.
Add a check routine for database in use (use a connection to master to do it!). You can force the database to be dropped anyway by first executing
ALTER DATABASE [MyDatabase] SET SINGLE_USER WITH ROLLBACK IMMEDIATE
again from the connection to master!
However everybody else using the db, will no longer like you at all...
Just don't use DB name in connection string.
"Data Source=.\SQLEXPRESS;Integrated Security=True;"
I was having the same troubles as Anshuman...
By my testing of the code in question of Anshuman there have been very simple error:
there have to be SqlConnection.ClearAllPools(); instead of SqlConnection.ClearPool(con);
Like this trouble of
"cannot drop database because is in use..."
disappears.