Click here to Skip to main content
15,888,984 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I want to write sql queries in txt file c# transaction. I am able to write sql queries in txt file in normal situation.
Sql query save to txt file successfullyas per my own class

string strSql = "insert into dbo.repayment_collection_master( AccId, productId, mem_code, memberName)Values('1','1','3','Prasanta')

DataAccess.ExecuteNonQuery(strSql);  
public static void ExecuteNonQuery(String sql)  
        {            
            SqlConnection con = DBConnection.Connection;  
            SqlCommand cmd = new SqlCommand();  
            cmd.CommandText = sql;  
            cmd.Connection = con;  
            cmd.CommandTimeout = 0;  
            if (con.State != ConnectionState.Open)  
                con.Open();  
            int i = 0;  
           i= cmd.ExecuteNonQuery();  
           if (i != 0)  
           {  
               addQueryToDb ad = new addQueryToDb(sql);  
           }  
            con.Close();  
  
        }`  
  
  
  
     public addQueryToDb(string str)  
    {       
        string LogFileName = string.Empty;  
        string userName = "work_";        
        LogFileName = DateTime.Now.Day.ToString() + "_" + DateTime.Now.Month.ToString() + "_" + DateTime.Now.Year.ToString();  
        LogFileName = HttpContext.Current.Server.MapPath("~/userWork\\" + userName + "" + LogFileName + ".txt");       
        FileStream fs = new FileStream(LogFileName, FileMode.OpenOrCreate, FileAccess.Write);  
        StreamWriter m_streamWriter = new StreamWriter(fs);  
        m_streamWriter.BaseStream.Seek(0, SeekOrigin.End);  
        m_streamWriter.WriteLine(str);  
        m_streamWriter.WriteLine("\n");  
        m_streamWriter.Flush();  
        m_streamWriter.Close();  
        fs.Dispose();  
        m_streamWriter.Dispose();  
    }  


But I am not able to write query from C# transaction

command.CommandText = @"insert into dbo.repayment_collection_master( AccId, productId, mem_code, memberName)Values('1','1','3','Prasanta')"; command.ExecuteNonQuery();    
    
command = new SqlCommand("SP_Loan_Repayment", connection, transaction); command.CommandType = CommandType.StoredProcedure;    
    
command.Parameters.Add("@branchId", SqlDbType.VarChar).Value = branchId;    
                    command.Parameters.Add("@centerId", SqlDbType.VarChar).Value = centerId;    
                    command.Parameters.Add("@centerName", SqlDbType.VarChar).Value = centerName;    
                    command.Parameters.Add("@repayDate", SqlDbType.VarChar).Value = repayDate;    
                    command.Parameters.Add("@repayPrinc", SqlDbType.Decimal).Value = repayPrinc;    
                    command.Parameters.Add("@repayInt", SqlDbType.Decimal).Value = repayInt;    
                    command.Parameters.Add("@transactionId", SqlDbType.VarChar).Value = transactionId;    
                    command.Parameters.Add("@approvedBy", SqlDbType.VarChar).Value = approvedBy;    
    
                    SqlParameter VoucharReset = command.Parameters.Add("@VoucharReset", SqlDbType.Int, 2);    
                    VoucharReset.Direction = ParameterDirection.Output;    
                    SqlParameter ReturnIndex = command.Parameters.Add("@ReturnIndex", SqlDbType.Int, 2);    
                    ReturnIndex.Direction = ParameterDirection.Output;    
                    SqlParameter ReturnMsg = command.Parameters.Add("@ReturnMsg", SqlDbType.VarChar, 4000);    
                    ReturnMsg.Direction = ParameterDirection.Output;    
                    SqlParameter ReturntransactionId = command.Parameters.Add("@ReturntransactionId", SqlDbType.VarChar, 10);    
                    ReturntransactionId.Direction = ParameterDirection.Output;    
    
command.ExecuteNonQuery(); transaction.Commit();    
    
After transaction.Commit() //I want to write above query to txt file 


What I have tried:

 public addQueryToDb(string str)
{
    string LogFileName = string.Empty;
    string userName = "work_";
    LogFileName = DateTime.Now.Day.ToString() + "_" + DateTime.Now.Month.ToString() + "_" + DateTime.Now.Year.ToString();
    LogFileName = HttpContext.Current.Server.MapPath("~/userWork\\" + userName + "" + LogFileName + ".txt");
    FileStream fs = new FileStream(LogFileName, FileMode.OpenOrCreate, FileAccess.Write);
    StreamWriter m_streamWriter = new StreamWriter(fs);
    m_streamWriter.BaseStream.Seek(0, SeekOrigin.End);
    m_streamWriter.WriteLine(str);
    m_streamWriter.WriteLine("\n");
    m_streamWriter.Flush();
    m_streamWriter.Close();
    fs.Dispose();
    m_streamWriter.Dispose();
}
Posted
Updated 27-Mar-20 2:06am
Comments
Richard MacCutchan 27-Mar-20 6:05am    
You need to capture all the details of the transaction and convert them to a string (or multiple strings). Then when you need to use the transaction later, you read the string(s) back and convert to the correct transaction format.
Richard Deeming 27-Mar-20 6:42am    
NB: Your ExecuteNonQuery method will force you to write code which is vulnerable to SQL Injection[^].

You need to rewrite it so that you can pass parameters to the query correctly, instead of trying to concatenate the parameter values into the query itself.

1 solution

To start with, you'll need some code to format a SqlCommand object to an executable script. The code to do that is quite long, so I've put it on GitHub:

SQL Query Formatter · GitHub[^]

Next, change your addQueryToDb method so that it takes the command object instead of a string. I'd also recommend renaming it to more closely match what it's doing.
C#
public static void LogDatabaseQuery(IDbCommand command)
{
    if (command is null) throw new ArgumentNullException(nameof(command));
    
    const string userName = "work_";
    string logFileName = DateTime.Today.ToString("d_M_yyyy");
    string virtualPath = "~/userWork\\" + userName + "/" + logFileName + ".txt";
    string physicalPath = HostingEnvironment.MapPath(virtualPath);
    
    File.AppendAllText(physicalPath, SqlQueryFormatter.FormatCommand(command));
}

Get rid of your static DBConnection.Connection property. Connection objects should not be shared or stored. Instead, create a new connection object when you require it, and dispose of it as soon as you're finished with it.
C#
public static class DBConnection
{
    public static SqlConnection CreateConnection()
    {
        string connectionString = WebConfigurationManager.ConnectionStrings["yourConnectionStringName"].ConnectionString;
        return new SqlConnection(connectionString);
    }
}

Update your ExecuteNonQuery method to allow you to pass parameters:
C#
public static SqlCommand BuildCommand(SqlCommand command, FormattableString commandText)
{
    if (commandText.ArgumentCount == 0)
    {
        throw new InvalidOperationException("Parameterless command detected: review for SQL Injection.\n" + commandText);
    }
    
    int parameterIndex = 0;
    var parameterNames = new object[commandText.ArgumentCount];
    foreach (object parameter in commandText.GetArguments())
    {
        string parameterName = $"@p{parameterIndex}";
        parameterNames[parameterIndex] = parameterName;

        var p = command.CreateParameter();
        p.ParameterName = parameterName;
        p.Value = parameter;
        command.Parameters.Add(p);

        parameterIndex++;
    }

    command.CommandText = string.Format(CultureInfo.InvariantCulture, commandText.Format, parameterNames);
}

public static int ExecuteNonQuery(FormattableString commandText)  
{
    using (SqlConnection con = DBConnection.CreateConnection())
    using (SqlCommand cmd = new SqlCommand())
    {
        cmd.Connection = con;
        cmd.CommandTimeout = 0;
        BuildCommand(cmd, commandText);
        
        con.Open();
        int i = cmd.ExecuteNonQuery();
        if (i != 0) LogDatabaseQuery(cmd);
        return i;
    }
}
Usage:
C#
string accId = "1";
string productId = "1";
string memCode = "3";
string memberName = "Prasanta";

DataAccess.ExecuteNonQuery($"insert into dbo.repayment_collection_master (AccId, productId, mem_code, memberName) Values ({accId}, {productId}, {memCode}, {memberName})");
NB: Note the use of FormattableString to convert an interpolated string to a properly parameterized query. If you don't pass it properly, or you don't include any parameters, you will get an exception.

Now, when you execute your commands within a transaction, you can log them:
C#
using (SqlConnection connection = DBConnection.CreateConnection())
{
    connection.Open();
    
    using (SqlTransaction transaction = connection.BeginTransaction())
    {
        var command1 = new SqlCommand("", connection, transaction);
        DataAccess.BuildCommand(command1, $"insert into dbo.repayment_collection_master (AccId, productId, mem_code, memberName) Values ({accId}, {productId}, {memCode}, {memberName})");
        command1.CommandTimeout = 0;
        command1.ExecuteNonQuery();
        
        var command2 = new SqlCommand("SP_Loan_Repayment", connection, transaction);
        command2.CommandType = CommandType.StoredProcedure;
        command2.CommandTimeout = 0;
    
        command2.Parameters.Add("@branchId", SqlDbType.VarChar).Value = branchId;    
        command2.Parameters.Add("@centerId", SqlDbType.VarChar).Value = centerId;    
        command2.Parameters.Add("@centerName", SqlDbType.VarChar).Value = centerName;    
        command2.Parameters.Add("@repayDate", SqlDbType.VarChar).Value = repayDate;    
        command2.Parameters.Add("@repayPrinc", SqlDbType.Decimal).Value = repayPrinc;    
        command2.Parameters.Add("@repayInt", SqlDbType.Decimal).Value = repayInt;    
        command2.Parameters.Add("@transactionId", SqlDbType.VarChar).Value = transactionId;    
        command2.Parameters.Add("@approvedBy", SqlDbType.VarChar).Value = approvedBy;    
    
        SqlParameter voucharReset = command2.Parameters.Add("@VoucharReset", SqlDbType.Int, 2);    
        VoucharReset.Direction = ParameterDirection.Output;    
        SqlParameter returnIndex = command2.Parameters.Add("@ReturnIndex", SqlDbType.Int, 2);    
        ReturnIndex.Direction = ParameterDirection.Output;    
        SqlParameter returnMsg = command2.Parameters.Add("@ReturnMsg", SqlDbType.VarChar, 4000);    
        ReturnMsg.Direction = ParameterDirection.Output;    
        SqlParameter returntransactionId = command2.Parameters.Add("@ReturntransactionId", SqlDbType.VarChar, 10);    
        ReturntransactionId.Direction = ParameterDirection.Output;
        
        command2.ExecuteNonQuery();
        
        transaction.Commit();
        
        DataAccess.LogDatabaseQuery(command1);
        DataAccess.LogDatabaseQuery(command2);
    }
}
NB: To log both commands, you will need to use separate SqlCommand objects. But you shouldn't be reusing them anyway.
 
Share this answer
 
Comments
Maciej Los 27-Mar-20 17:14pm    
Double, triple, ... 5ed!

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900