Prasanta Baidya Ответов: 1

Как написать SQL-запросов в текстовый файл c# операции


Я хочу написать sql-запросы в txt-файле c# transaction. Я могу писать sql-запросы в txt-файле в обычной ситуации.
SQL запрос сохранить в txt файл successfullyas в мой собственный класс

string strSql = "вставить в dbo.repayment_collection_master( AccId, productId, mem_code, memberName)значения('1','1','3','Прасанта')

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();  
    }  


Но я не могу написать запрос из транзакции C#

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 


Что я уже пробовал:

 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();
}

Richard MacCutchan

Вам нужно захватить все детали транзакции и преобразовать их в строку (или несколько строк). Затем, когда вам нужно будет использовать транзакцию позже, вы читаете строку(ы) обратно и преобразуете ее в правильный формат транзакции.

Richard Deeming

NB: Ваш ExecuteNonQuery метод заставит вас написать код, который уязвим для SQL-инъекция[^].

Вам нужно переписать его так, чтобы вы могли правильно передавать параметры в запрос, а не пытаться объединить значения параметров в самом запросе.

1 Ответов

Рейтинг:
2

Richard Deeming

Для начала вам понадобится некоторый код для форматирования a SqlCommand объект для исполняемого скрипта. Код для этого довольно длинный, поэтому я поместил его на GitHub:

Форматер SQL - запросов · GitHub[^]

Далее, измените свой addQueryToDb метод так, что он принимает объект команды вместо строки. Я бы также рекомендовал переименовать его, чтобы он более точно соответствовал тому, что он делает.

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));
}

Избавиться от статического DBConnection.Connection собственность. Объекты подключения не должны совместно использоваться или храниться. Вместо этого создайте новый объект подключения, когда он вам понадобится, и избавьтесь от него, как только вы закончите с ним.
public static class DBConnection
{
    public static SqlConnection CreateConnection()
    {
        string connectionString = WebConfigurationManager.ConnectionStrings["yourConnectionStringName"].ConnectionString;
        return new SqlConnection(connectionString);
    }
}

Обновите свой ExecuteNonQuery метод, позволяющий передавать параметры:
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;
    }
}
Использование:
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: Обратите внимание на использование FormattableString чтобы преобразовать интерполированную строку в правильно параметризованный запрос. Если вы не передадите его должным образом или не включите какие-либо параметры, вы получите исключение.

Теперь, когда вы выполняете свои команды в транзакции, вы можете регистрировать их:
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: Чтобы зарегистрировать обе команды, вам нужно будет использовать отдельные SqlCommand объекты. Но вы все равно не должны использовать их повторно.


Maciej Los

Двойной, тройной, ... 5ed!