Utilizando OracleParameter no windows forms

Bom dia nobres amigos, venho por aqui mais para documentar algo que ocorreu em um dos meus trabalhos e gostaria de compartilhar e saber da opinião de vocês.
Tenho uma aplicação legada em Windows Forms que faz controle de pasta em um determinado arquivo de uma empresa.

Em um dado momento foi necessário dar uma manutenção em determinado ponto aonde era preciso realizar um INSERT e logo após um UPDATE em tabelas diferentes. O erro vinha exatamente desse método que tentava executar uma transação mas a mesma não conseguia ser executada. O Erro apresentado era o ORA-00933: SQL command not properly ended

Resolvi vir ate a comunidade e documenta-la pois procurei em diversos fóruns e não achei referencias que pudessem solucionar esse problema.

Irei postar o código inicial abaixo que apresentava tal erro:

public String GravaRequisicao(Requisicao req, Usuario usuario)
{
    Setor setor = new Setor();
    int LinhasTotais = 0;
    string mensagem = "";
    StringBuilder strSQL = new StringBuilder();
    StringBuilder strSQLU = new StringBuilder();
    try
    {
        AbreConexao();
        OracleTransaction transacao;
        transacao = Con.BeginTransaction();

    foreach (Pasta p in req.Pastas)
    {
        //Grava a requisição
        strSQL.Append("INSERT INTO REQPASTAS ");
        strSQL.Append("(NUMPASTA, DTRETIRADA, DTDEVPREV, DTDEVOLUCAO, IDREQUERENTE, ");
        strSQL.Append("CODSETOR,   NUMREQ, IDUSUARIO, SITPASTA, SITREQ) ");
        strSQL.Append("VALUES (");
        strSQL.Append(p.CodigoPasta.ToString());
        strSQL.Append(", TO_DATE('");
        strSQL.Append(p.DataRetirada.ToString().Substring(0,10));
        strSQL.Append("', 'DD/MM/YYYY'), TO_DATE('");
        strSQL.Append(p.DataPrevisaoDevolucao.ToString().Substring(0, 10));
        strSQL.Append("', 'DD/MM/YYYY'), ");
        strSQL.Append("NULL"); // Data de Devolução não é preenchida neste momento.
        strSQL.Append(", ");
        strSQL.Append(req.Requerente);
        strSQL.Append(", '");
        strSQL.Append(p.Setor);
        strSQL.Append("', ");
        strSQL.Append(p.CodigoRequisicao);
        strSQL.Append(", ");
        strSQL.Append(usuario.IdUsuario);
        strSQL.Append(", ");
        strSQL.Append("'A'");
        strSQL.Append(", ");
        strSQL.Append("'A'");
        strSQL.Append(")");

        Cmd = new OracleCommand(strSQL.ToString(), Con)
        {
            CommandType = System.Data.CommandType.Text
        };
        int LinhasIncluidas = Cmd.ExecuteNonQuery();

        //Aloca a pasta com tituação = 1
        
        strSQLU.Append("UPDATE ARQPASTAS ");
        strSQLU.Append("SET STATUS = '1' ");
        strSQLU.Append("WHERE NUMPASTA = ");
        strSQLU.Append(p.CodigoPasta);

        Cmd = new OracleCommand(strSQLU.ToString(), Con)
        {
            CommandType = System.Data.CommandType.Text
        };

        int LinhasAlteradas = Cmd.ExecuteNonQuery();
        Cmd.Transaction = transacao;
      
        LinhasTotais += LinhasIncluidas;

        if (LinhasIncluidas != LinhasAlteradas)
        {
            transacao.Rollback();
            mensagem = "Requisição não gravada. Contactar a TI.";
        }
        else
        {
            if (LinhasTotais == req.Pastas.Count())
            {
                mensagem = "1";
                transacao.Commit();
            }
        }
    }
}
catch (Exception ex)
{
    RegistraErro(new Error(new StackTrace(true), ex, strSQL.ToString() + " " + strSQLU.ToString()));
    mensagem = "Erro no ORACLE. Contate a TI.";
}
finally
{
    FecharConexao();
}

return mensagem;

}

Segue o código com as modificações que julgo correto e gostaria da opinião dos senhores quanto ao uso de Transação dentro do Windows Forms desconsiderando detalhes de modelagem de dados:

public String GravaRequisicao(Requisicao req, Usuario usuario)
{
    Setor setor = new Setor();
    int LinhasTotais = 0;
    string mensagem = "";
    StringBuilder strSQL = new StringBuilder();
    StringBuilder strSQLU = new StringBuilder();
    try
    {
        AbreConexao();
        OracleTransaction transacao;

        foreach (Pasta p in req.Pastas)
        {
            //Limpando StringBuilder anterior
            strSQL.Clear();
            strSQLU.Clear();

            transacao = Con.BeginTransaction();
            Cmd = new OracleCommand();
            Cmd.Connection = Con;
            Cmd.Transaction = transacao;

            //Grava a requisição

            strSQL.Append("INSERT INTO REQPASTAS ");
            strSQL.Append("(NUMPASTA, DTRETIRADA, DTDEVPREV, DTDEVOLUCAO, IDREQUERENTE, ");
            strSQL.Append("CODSETOR,   NUMREQ, IDUSUARIO, SITPASTA, SITREQ) ");
            strSQL.Append("VALUES (:CODIGOPASTA, ");
            strSQL.Append("TO_DATE(:DATARETIRADA, 'DD/MM/YY'),");
            strSQL.Append("TO_DATE(:DATAPREVDEV, 'DD/MM/YY'),");
            strSQL.Append("NULL,"); // Data de Devolução não é preenchida neste momento.
            strSQL.Append(":REQUERENTE,");
            strSQL.Append(":SETOR, ");
            strSQL.Append(":NUMREQ, ");
            strSQL.Append(":IDUSUARIO, ");
            strSQL.Append("'A',");
            strSQL.Append("'A'");
            strSQL.Append(")");

            Cmd.CommandText = strSQL.ToString();

            FuncoesDAL fd = new FuncoesDAL();

            Cmd.Parameters.Add("CODIGOPASTA",OracleDbType.Int32).Value =  p.CodigoPasta;
            Cmd.Parameters.Add("DATARETIRADA",OracleDbType.Varchar2).Value = fd.FormataDataInsertOracle(p.DataRetirada);
            Cmd.Parameters.Add("DATAPREVDEV",OracleDbType.Varchar2).Value =  fd.FormataDataInsertOracle(p.DataPrevisaoDevolucao);
            Cmd.Parameters.Add("REQUERENTE", OracleDbType.Varchar2).Value = req.Requerente.ToString();
            Cmd.Parameters.Add("SETOR", OracleDbType.Varchar2).Value =  p.Setor.ToString() ;
            Cmd.Parameters.Add("NUMREQ", OracleDbType.Varchar2).Value =p.CodigoRequisicao.ToString();
            Cmd.Parameters.Add("IDUSUARIO", OracleDbType.Int32).Value = usuario.IdUsuario;

            int LinhasIncluidas = Cmd.ExecuteNonQuery();

            Cmd.Parameters.Clear();
            //Aloca a pasta com Situação = 1
            
            strSQLU.Append("UPDATE ARQPASTAS ");
            strSQLU.Append("SET STATUS = '1' ");
            strSQLU.Append("WHERE NUMPASTA = :NUMPASTA");

            Cmd.CommandText = strSQLU.ToString();

            Cmd.Parameters.Add("NUMPASTA", OracleDbType.Int32).Value = p.CodigoPasta;

            int LinhasAlteradas = Cmd.ExecuteNonQuery();
            
            LinhasTotais += LinhasIncluidas;

            if (LinhasIncluidas != LinhasAlteradas)
            {
                transacao.Rollback();
                mensagem = "Requisição não gravada. Contactar a TI.";
            }
            else
            {
                mensagem = "1";
                transacao.Commit();
            }
        }
    }
    catch (Exception ex)
    {
        RegistraErro(new Error(new StackTrace(true), ex, strSQL.ToString() + " " + strSQLU.ToString()));
        mensagem = "Erro no ORACLE. Contate a TI.";
    }
    finally
    {
        FecharConexao();
    }

    return mensagem;
}

No aguardo as críticas para juntos podermos crescer
Abraços a todos
Max

Qual linha exatamente deu o erro? Retira esse try catch que só serve pra atrapalhar e sujar o código.

O erro dava nessa linha, mas como falei o codigo ja esta funcionando, gostaria de saber a respeito da implementação do OracleParameter se tinham alguma observação

Abraços
Max

Inspecione e copie o conteúdo de strSQL.ToString(), pra poder visualizar o SQL já montado. Assim vai facilitar bastante visualizar o que está errado diretamente no SQL Plus, Sql Developer, etc. Analisar a partir da concatenação perderia muito tempo.

O correto é usar OracleParameter mesmo. Além de evitar acidentes com concatenação errada vs tipo de dado, evita principalmente SQL injection.