Goal:
My goal is to create a MySQL query using C#, and on successful query I'd like to use File.Copy... and vice versa.
This is my current code:
//Try run code
try
{
//Add record to MySQL
using (var conn = new MySqlConnection(ConnectionString.ConnString))
{
using (var cmd = new MySqlCommand("INSERT INTO files (document_name, path, version, section, user_modified, date_modified)" +
" values (#doc, #path, #version, #section, #user, NOW());", conn))
{
conn.Open();
cmd.Parameters.AddWithValue("#doc", docName.Text);
cmd.Parameters.AddWithValue("#path", $"{finalPath}Section {Section.Text}\\{docName.Text}{Path.GetExtension(fileName)}");
cmd.Parameters.AddWithValue("#version", versionNumber.Text);
cmd.Parameters.AddWithValue("#section", Section.Text);
cmd.Parameters.AddWithValue("#user", UserDetails.userId);
cmd.ExecuteNonQuery();
}
}
//Copy file into new directory
File.Copy(fileName, $"{finalPath}Section {Section.Text}\\{docName.Text}{Path.GetExtension(fileName)}");
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
Question:
What is a good method to achieve the following..
If MySQL query not successful then do not do anything and stop and return to where the error was.
If MySQL query was successful and File.Copy wasn't (Ran into an error for any reason) - then recover the query, delete whatever was inserted and go back all the way to step one.
How can this be achieved?
Edit 10/06/2020
I have managed to get the following code :
//Try run MySQL query with roll back function if failed.
public void RunTransaction(string myConnString)
{
MySqlConnection myConnection = new MySqlConnection(ConnectionString.ConnString);
myConnection.Open();
MySqlCommand myCommand = myConnection.CreateCommand();
MySqlTransaction myTrans;
// Start a local transaction
myTrans = myConnection.BeginTransaction();
// Must assign both transaction object and connection
// to Command object for a pending local transaction
myCommand.Connection = myConnection;
myCommand.Transaction = myTrans;
try
{
myCommand.CommandText = "INSERT INTO files (document_name, path, version, section, user_modified, date_modified, review_date)" +
" values (#doc, #path, #version, #section, #user, NOW(), NOW() + interval 12 month);";
myCommand.Parameters.AddWithValue("#doc", docName.Text);
myCommand.Parameters.AddWithValue("#path", $"{finalPath}Section {Section.Text}\\{docName.Text}{Path.GetExtension(fileName)}");
myCommand.Parameters.AddWithValue("#version", versionNumber.Text);
myCommand.Parameters.AddWithValue("#section", Section.Text);
myCommand.Parameters.AddWithValue("#user", UserDetails.userId);
myCommand.ExecuteNonQuery();
myTrans.Commit();
}
catch (Exception e)
{
try
{
myTrans.Rollback();
}
catch (MySqlException ex)
{
if (myTrans.Connection != null)
{
Console.WriteLine("An exception of type " + ex.GetType() +
" was encountered while attempting to roll back the transaction.");
}
}
Console.WriteLine("An exception of type " + e.GetType() +
" was encountered while inserting the data.");
Console.WriteLine("Neither record was written to database.");
}
finally
{
myConnection.Close();
}
}
Question..
How would I then implement the File.Copy function to suit my goal?
I have this source.. https://learn.microsoft.com/en-us/dotnet/api/system.io.fileinfo.copyto?view=netcore-3.1
but not sure where to code in my current code?
Related
I want to execute an stored procedure located in a linked server database. Currently, I'm using this in SSMS:
INSERT INTO myTable
EXEC [LINKEDSERVER\LINKED].[Data_Base_Name].[Store].usp_GetInfo 1, 1, NULL, 'H'
This will insert into my Local DB the result data from the Stored procedure located in LINKEDSERVER\LINKED.
I want to be able to do this with a command from C#, is there a proper way to do it?
Thanks!
You could execute SP from DataContext:
using (DataContext ctx = DataContext())
{
int result = ctx.SP_ProcedureName("1", "2", "3");
}
But first you have to add it to DataContext Diagram from your database as you add tables but from "Stored Procedures" folder.
that is more defensive and neat solution. but if you prefer to use raw command line at least use parameterized query for it like this example :
string sqlText = "SELECT columnName FROM Test_Attachments WHERE Project_Id =#PID1 AND [Directory] = #Directory";
SqlCommand myCommand = new SqlCommand(sqlText, SqlConnection);
myCommand.Parameters.AddWithValue("#PID1", 12);
myCommand.Parameters.AddwithValue("#Directory", "testPath");
It is way for avoiding SQL injection to your code.
Also you could use finally block for close connection :
finally
{
command.Connection.Close();
}
Thank you guys for the help. Oleg thanks for your suggestion as well. What I did was this:
qSQL = "INSERT INTO " + tableName + " EXEC [LINKEDSERVER\\LINKED].[Data_Base_Name]." + spName;
using (SqlConnection _connection = new SqlConnection(connectionString))
{
try
{
command = new SqlCommand();
command.Connection = _connection;
command.Connection.Open();
command.CommandText = _qSQL;
command.CommandTimeout = 300; //Because it takes long
SqlTransaction transaction;
transaction = connection.BeginTransaction();
try
{
command.Transaction = _transaction;
command.ExecuteNonQuery();
transaction.Commit();
Debug.WriteLine("Done");
}
catch (SqlException e)
{
Debug.WriteLine("Exception [{0}:{1}]", e.Number, e.Message);
transaction.Rollback();
}
//close connection
command.Connection.Close();
}
catch (SqlException e)
{
command.Connection.Close();
Debug.WriteLine("exception error number: " + e.Number + ": " + e.Message);
}
}
}
If you have any suggestions to improve this let me know.
System.Data; Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.
this error will come only when i run service
i am using below code
try
{
LogGenerator.WriteErrorLog("Conn opened before");
if (con.State == System.Data.ConnectionState.Closed)
con.Open();
LogGenerator.WriteErrorLog("Conn opened");
// Set up a command with the given query and associate
// this with the current connection.
SqlCommand cmd = new SqlCommand("Backup database " + DBName + " to disk='" + filePathExist + "'", con);
cmd.ExecuteNonQuery();
LogGenerator.WriteErrorLog("query executed");
}
catch(Exception ex)
{
LogGenerator.WriteErrorLog("Error in Conn");
LogGenerator.WriteErrorLog(ex);
}
finally
{
con.Close();
con.Dispose();
SqlConnection.ClearPool(con);
}
As your trying take DB backup sometime it will take more than default connection time, so try to set timeout to your command.
try
{
if (con.State == System.Data.ConnectionState.Closed)
con.Open();
// Set up a command with the given query and associate
// this with the current connection.
SqlCommand cmd = new SqlCommand("Backup database " + DBName + " to disk='" +
filePathExist + "'", con);
cmd.CommandTimeout = 60;
cmd.ExecuteNonQuery();
}
catch(Exception ex)
{
//handle exception here
}
finally
{
con.Close();
con.Dispose();
SqlConnection.ClearPool(con);
}
for more information on timeout please refer here.
https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.commandtimeout(v=vs.110).aspx
https://learn.microsoft.com/en-us/sql/database-engine/configure-windows/configure-the-remote-login-timeout-server-configuration-option
As said in the title, I tried to write some prepared statement on Visual Studio 2017.
But when Starting my program, I have an exception, called
System.InvalidOperationException :'The connection property has not
been set.'
Here's the associated code :
namespace Sequence
{
class Database
{
private MySqlCommand cmd;
MySql.Data.MySqlClient.MySqlConnection conn; //new connection
public void Connect()
{
string myConnectionString; //connection String
myConnectionString = "server=127.0.0.1;uid=root;" +
"pwd=;database=internship;";
try
{
conn = new MySql.Data.MySqlClient.MySqlConnection(myConnectionString);
conn.Open();
}
catch (MySql.Data.MySqlClient.MySqlException ex)
{
MessageBox.Show(ex.Message);
}
}
public void Disconnect()
{
try
{
conn.Close();
}
catch (MySql.Data.MySqlClient.MySqlException ex)
{
MessageBox.Show(ex.Message);
}
}
public void AddData()
{
cmd = new MySql.Data.MySqlClient.MySqlCommand();
//Connecting to database
Connect();
try
{
//Prepare a statement
cmd.CommandText = "INSERT INTO storing VALUES(#longitude, #latitude, #status, #path)";
cmd.Prepare();
//Adding values
cmd.Parameters.AddWithValue("#longitude", 0.5);
cmd.Parameters.AddWithValue("#latitude", 0.5);
cmd.Parameters.AddWithValue("#status", "false");
cmd.Parameters.AddWithValue("#path", "C:\\temp\\sequence");
//Executes the Query
cmd.ExecuteNonQuery();
//Writes in Console
Console.WriteLine("Line added successfully");
}
catch (MySql.Data.MySqlClient.MySqlException ex)
{
MessageBox.Show("Error " + ex.Number + " has occured: " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}
This is my class that connects to the database.
I have a main program where I just wrote this :
static class Program
{
[STAThread]
static void Main()
{
Database db = new Database();
db.Connect();
db.AddData();
db.Disconnect();
}
}
Any idea of why I get this exception ?
I followed the tutorial on this link :
https://dev.mysql.com/doc/connector-net/en/connector-net-programming-prepared-preparing.html
Thanks in advance
You didn't set the connection property of your command:
cmd.Connection = conn;
Although it's recommended to use the using-statement that Provides a convenient syntax that ensures the correct use of IDisposable objects like SqlConnection. It will also close the connection
When I attempt to restore my backup database it restores the database successfully but when log out the system it gives me the exception"A transport-level error occured when sending the request to the server. (Provider:Shared memory provider, error 0-No process is on the other end of the pipe)" ... how this can be solved
My codes for restoring database are:
private void restoreBtn_Click(object sender, EventArgs e)
{
string database = cn.Database.ToString();
try
{
cn.Open();
string sqlres1=string.Format("ALTER DATABASE ["+database+"] SET SINGLE_USER WITH ROLLBACK IMMEDIATE");
SqlCommand command1=new SqlCommand(sqlres1,cn);
command1.ExecuteNonQuery();
string sqlres2 = "USE MASTER RESTORE DATABASE ["+database+"] FROM DISK='"+restoretxt.Text+"' WITH REPLACE";
SqlCommand command2 = new SqlCommand(sqlres2, cn);
command2.ExecuteNonQuery();
string sqlres3 = string.Format("ALTER DATABASE [" + database + "] SET MULTI_USER");
SqlCommand command3 = new SqlCommand(sqlres3, cn);
command3.ExecuteNonQuery();
MessageBox.Show("Database restore done successfully");
this.Close();
}
catch(SqlException ex)
{
MessageBox.Show(ex.Message);
}
finally
{
cn.Close();
}
}
I'm sending a command to the database and it is returning that 1 rows are affected, but when i look inside the database, there are no records. I am receiving no errors. I checked to make sure the string was building correctly and it is. Any ideas? I'm not using parameterized queries here, I know. I will later. Here is the code from the database layer:
public int InsertStartTime(certificate cert, DateTime startTime, string lineNumber)
{
string sql = "INSERT INTO checkLog(userID,lineNumber,startTime) VALUES(" +
cert.userID + ", '" + lineNumber + "', '" + startTime + "');";
int result = 0;
try
{
conn.Open();
comm.CommandText = sql;
result = comm.ExecuteNonQuery();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
conn.Close();
}
MessageBox.Show(result.ToString() + " rows affected");
return result;
}
Using an access 2000 db file:
string connStr = #"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|\assets\users.mdb;Persist Security Info=True";
Right click your database file in VS and look at the properties. Is it set to "Copy Always"? By default, visual studio will make a copy of your database for debugging and any changes will be made only to this copy and will not be reflected in the original. You can set it to copy "Never" if you want to work on the "real" database file even in debug mode.
Depending on the database / data provider you are using, your SQL command may not be executing in auto-commit mode.
Try committing your transaction explicitly. Something like this:
conn.Open();
using (var tran = conn.BeginTransaction()) {
comm.Transaction = tran; // Possibly redundant, depending on database.
comm.CommandText = sql;
result = comm.ExecuteNonQuery();
tran.Commit();
}
Nobody else pointed this out so I will. PLEASE DO NOT USE SQL this way. Use parameters. You leave yourself wide open to sql attacks otherwise.
string sql = "INSERT INTO checkLog(userID,lineNumber,startTime) VALUES(#ID, #line, #starttime);
try
{
conn.Open();
comm.CommandText = sql;
comm.Parameters.Add("ID").Value = cert.userID;
comm.Parameters.Add("line").Value = lineNumber ;
comm.Parameters.Add("starttime").Value = startTime ;
result = comm.ExecuteNonQuery();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}