/// <summary> /// This is the business logic for placing orders, handling txs. /// StockTrader allows the user to select whether to use System.Transactions /// or use ADO.NET Provider-supplied transactions for order placement (buy, sell); and new user registrations. /// The best choice for performance will vary based on the backend database being used with the /// application. /// </summary> /// <param name="orderType">Buy or sell type.</param> /// <param name="userID">User id to create/submit order for.</param> /// <param name="holdingID">Holding id to sell.</param> /// <param name="symbol">Stock symbol to buy.</param> /// <param name="quantity">Shares to buy.</param> public OrderDataModel placeOrder(string orderType, string userID, int holdingID, string symbol, double quantity) { OrderDataModel order = null; HoldingDataModel holding = new HoldingDataModel(); switch (Settings.TRANSACTION_MODEL) { case (StockTraderUtility.TRANSACTION_MODEL_SYSTEMDOTTRANSACTION_TRANSACTION): { //This short try/catch block is introduced to deal with idle-timeout on SQL Azure //connections. It may not be required in the near future, but as of publication //SQL Azure disconnects idle connections after 30 minutes. While command retry-logic //in the DAL automatically deals with this, when performing a tx, with the BSL handling //tx boundaries, we want to go into the tx with known good connections. The try/catch below //ensures this. try { dalOrder = Trade.DALFactory.Order.Create(Settings.DAL); dalOrder.Open(Settings.TRADEDB_SQL_CONN_STRING); dalOrder.getSQLContextInfo(); } catch { } finally { dalOrder.Close(); } System.Transactions.TransactionOptions txOps = new TransactionOptions(); txOps.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted; txOps.Timeout = TimeSpan.FromSeconds(Settings.SYSTEMDOTTRANSACTION_TIMEOUT); //Start our System.Transactions tx with the options set above. using (TransactionScope tx = new TransactionScope(TransactionScopeOption.Required, txOps)) { dalOrder.Open(Settings.TRADEDB_SQL_CONN_STRING); try { //Business Step 1: create the order header. order = createOrder(orderType, userID, holdingID, symbol, quantity, ref holding); //Business Step 2: Determine which order processing mode to use, //and either process order right away (sync) and in-process with //calling ASP.NET Web app; or interface with the //async WCF Order Processing Service (cooler) via a service-to-service call, //distributed/remote. if (Settings.ORDER_PROCESSING_MODE != StockTraderUtility.OPS_INPROCESS) { //Fire up our async client; we follow the same model here as with the //StockTrader Web App in that we do not talk 'directly' to the generated proxy //for the service; rather we channel all calls through a single //class that then talks to the service proxy. This will aid in more //easily knowing where communication and proxy logic sits; and make changes to services //you might want to interface with vs. littering proxy calls throughout the //business tier itself. TradeOrderServiceAsyncClient asyncclient = new TradeOrderServiceAsyncClient(Settings.ORDER_PROCESSING_MODE, new Settings()); asyncclient.processOrderASync(order); } else { processOrderSync(order, holding); } //Commit! tx.Complete(); return order; } //If per chance you are doing step-through debugging through here and are getting a // "TRANSACTION HAS ABORTED" exception and do not know why, //it's quite likely you are hitting the 15-second timeout we set in //ConfigurationSettings for the System.Transaction options so its just doing what we told it to. //Simply adjust timeout higher, recompile if you need to. catch { throw; } finally { dalOrder.Close(); } } } //Repeat for ADO.NET transactions config option case. case (StockTraderUtility.TRANSACTION_MODEL_ADONET_TRANSACTION): { if (Settings.ORDER_PROCESSING_MODE == StockTraderUtility.OPS_SELF_HOST_MSMQ) goto case StockTraderUtility.TRANSACTION_MODEL_SYSTEMDOTTRANSACTION_TRANSACTION; dalOrder.Open(Settings.TRADEDB_SQL_CONN_STRING); dalOrder.BeginADOTransaction(); try { //Business Step 1: create the order header. order = createOrder(orderType, userID, holdingID, symbol, quantity, ref holding); //Business Step 2: Determine which order processing mode to use, //and either process order right away (sync); or interface with the //async WCF Order Processing Service (cooler) via a service-to-service call. if (Settings.ORDER_PROCESSING_MODE != StockTraderUtility.OPS_INPROCESS) { //Fire up our async client; we follow the same model here as with the //StockTrader Web App in that we do not talk 'directly' to the generated proxy //for the service; rather we channel all calls through a single //class that then talks to the service proxy. This will aid in more //easily knowing where communication and proxy logic sits; and make changes to services //you might want to interface with vs. littering proxy calls throughout the //business tier itself. TradeOrderServiceAsyncClient asyncclient = new TradeOrderServiceAsyncClient(Settings.ORDER_PROCESSING_MODE, new Settings()); asyncclient.processOrderASync(order); dalOrder.CommitADOTransaction(); } else { processOrderSync(order, holding); } dalOrder.CommitADOTransaction(); return order; } catch { dalOrder.RollBackTransaction(); throw; } finally { dalOrder.Close(); } } } throw new Exception(Settings.ENABLE_GLOBAL_SYSTEM_DOT_TRANSACTIONS_CONFIGSTRING + ": " + StockTraderUtility.EXCEPTION_MESSAGE_INVALID_TXMMODEL_CONFIG + " Repository ConfigKey table."); }