Pergunta

I have a class with all my data access code in it for my ASP.NET 4.0 application. There are two methods in the class which insert data into the database. I want to enlist these inserts in a SqlTransaction and roll the transaction back if one of the inserts fail. However I'm not sure how to do it though, because of the way I've coded it. Here's my data access code:

public class DBUtil
{

    private static readonly string _connectionString;

    static DBUtil()
    {
        _connectionString = WebConfigurationManager.ConnectionStrings["MooDB"].ConnectionString;
        if (string.IsNullOrEmpty(_connectionString))
            throw new Exception("Connection string not configured in Web.Config file");
    }

    public int InsertTrade(
        string symbol,
        string tradeSetupId,
        int tradeTypeId,
        decimal lotsPerUnit,
        string chartTimeFrame,
        decimal pctAccountRisked,
        int? tradeGrade = null,
        int? executionGrade = null,
        int? MFEPips = null,
        int? MAEPips = null
        )
    {
        SqlCommand cmd = new SqlCommand("usp_InsertTrade");
        // required parameters
        cmd.Parameters.AddWithValue("@symbol", symbol);
        cmd.Parameters.AddWithValue("@tradeSetupId", tradeSetupId);
        cmd.Parameters.AddWithValue("@tradeTypeId", tradeTypeId);
        cmd.Parameters.AddWithValue("@lotsPerUnit", lotsPerUnit);
        cmd.Parameters.AddWithValue("@chartTimeFrame", chartTimeFrame);
        cmd.Parameters.AddWithValue("@pctAccountRisked", pctAccountRisked);

        // optional parameters
        if (MAEPips.HasValue)
            cmd.Parameters.AddWithValue("@MAEPips", MAEPips);
        if (MFEPips.HasValue)
            cmd.Parameters.AddWithValue("@MFEPips", MFEPips);
        if (tradeGrade.HasValue)
            cmd.Parameters.AddWithValue("@tradeGrade", tradeGrade);
        if (executionGrade.HasValue)
            cmd.Parameters.AddWithValue("@executionGrade", executionGrade);
        return (InsertData(cmd, "trade"));
    }

    public int InsertOrder(
        int tradeId,
        int units,
        string side,
        decimal price,
        decimal spread,
        int strategyId,
        string signalTypeId,
        int brokerId,
        string orderTypeId,
        DateTime orderDateTime,
        string comment,
        int? accountId = null
        )
    {
        SqlCommand cmd = new SqlCommand("usp_InsertOrder");
        // required parameters
        cmd.Parameters.Add(new SqlParameter("@tradeId", tradeId));
        cmd.Parameters.Add(new SqlParameter("@units", units));
        cmd.Parameters.Add(new SqlParameter("@side", side));
        cmd.Parameters.Add(new SqlParameter("@price", price));
        cmd.Parameters.Add(new SqlParameter("@spread", spread));
        cmd.Parameters.Add(new SqlParameter("@strategyId", strategyId));
        cmd.Parameters.Add(new SqlParameter("@signalTypeId", signalTypeId));
        cmd.Parameters.Add(new SqlParameter("@brokerId", brokerId));            
        cmd.Parameters.Add(new SqlParameter("@orderTypeId", orderTypeId));
        cmd.Parameters.Add(new SqlParameter("@orderDateTime", orderDateTime));
        cmd.Parameters.Add(new SqlParameter("@comment", comment));

        // optional parameters
        if (accountId.HasValue)
            cmd.Parameters.Add(new SqlParameter("@accountId", accountId));
        return (InsertData(cmd, "order"));
    } 

    private int InsertData(SqlCommand cmd, string tableName)
    {
        SqlConnection con = new SqlConnection(_connectionString);
        cmd.Connection = con;
        cmd.CommandType = CommandType.StoredProcedure;
        int rc = -1;
        try
        {
            con.Open();
            rc = (int) cmd.ExecuteScalar();
        }
        finally
        {
            con.Close();

        }
        return rc;
    }        
}

I am accessing that code from my ASP.NET page like so:

int tradeId = DB.InsertTrade (
    ddlSymbols.SelectedValue,
    ddlTradeSetups.SelectedValue, 
    int.Parse(ddlTradeTypes.SelectedValue), 
    decimal.Parse(txtLotsPerUnit.Text),
    ddlTimeFrames.Text,
    decimal.Parse(txtAcctRisk.Text));

int orderId = DB.InsertOrder (
    tradeId,
    int.Parse(txtUnits.Text),
    radSide.SelectedValue,
    Decimal.Parse(txtEntryPrice.Text),
    Decimal.Parse(txtSpread.Text),
    int.Parse(ddlStrategies.SelectedValue),
    "IE",
    int.Parse(ddlBrokers.SelectedValue),
    radSide.SelectedValue + radOrderType.SelectedValue,
    DateTime.Parse(txtEntryDate.Text + " " + txtEntryTime.Text),
    txtEntryComments.Text,
    int.Parse(ddlAccounts.SelectedValue));

What I want to do is wrap the calls from the ASP.NET page in a SqlTransaction. What is the best way to do this? Will I have to refactor my code somewhat?

Thanks very much.

Foi útil?

Solução

Use the TransactionScope object to put several statements within one transaction:

using (TransactionScope scope = new TransactionScope())
{
    // Database call 1 - within transaction
    // Database call 2 - within same transaction

    scope.Complete(); // Commit the transaction, or face an automatic rollback
}

Outras dicas

The way you are inserting new data in your application is not transaction save. You should refactor your code all that within the same transaction and probably connection.

Quick and dirty fix for that is to create a connection, open transaction and pass the connection to every method, instead of creating new connections within the methods... After all methods are invoked and no exceptions/errors were indicated Commit the transaction, otherwise Rollback it.

I assume that t would be difficult but, I would consider using some ORM mappers like nHibernate. Mature mappers can handle a lot of different scenarios including yours.

Or using the SqlTransaction class:

public void RunAsTransaction(string myConnString) 
{

SqlConnection myConnection = new SqlConnection(myConnString);
myConnection.Open();

SqlCommand myCommand = myConnection.CreateCommand();
SqlTransaction myTrans;

myTrans = myConnection.BeginTransaction();

myCommand.Connection = myConnection;
myCommand.Transaction = myTrans;

try
{
  myCommand.CommandText = "Insert into Region (RegionID, RegionDescription) VALUES (100, 'Description')";
  myCommand.ExecuteNonQuery();
  myCommand.CommandText = "Insert into Region (RegionID, RegionDescription) VALUES (101, 'Description')";
  myCommand.ExecuteNonQuery();
  myTrans.Commit();
  }
catch(Exception e)
{
  myTrans.Rollback();
}  
finally 
{
  myConnection.Close();
}

}

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top