/// <summary> /// Execute the batch and catch the errors. /// </summary> /// <param name="batch">A batch of commands to be executed.</param> /// <returns>Any exceptions from executing the batch, null if there are none.</returns> public static bool Execute(Batch batch, Control owner, BatchExceptionEventHandler batchExceptionEventHandler) { bool success = false; // Execute the batch and process the results. if (batch != null) { success = true; try { // Execute the batch on the server and process the results. ClientMarketData.Execute(batch); } catch (BatchException batchException) { success = false; owner.BeginInvoke(batchExceptionEventHandler, new object[] { typeof(ClientMarketData), new BatchExceptionEventArgs(batchException) }); } } return(success); }
/// <summary> /// Event trigger when any record in the data model has changed. /// </summary> /// <param name="sender">Object originating the event.</param> public static void OnEndMerge(object sender) { // If a handler has been associated with this event, invoke it. if (ClientMarketData.EndMerge != null) { ClientMarketData.EndMerge(sender, EventArgs.Empty); } }
/// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { this.components = new System.ComponentModel.Container(); System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(AdvertisementViewer)); this.clientClientMarketData = new MarkThree.Guardian.Client.ClientMarketData(this.components); this.SuspendLayout(); // // AdvertisementViewer // this.Name = "AdvertisementViewer"; this.ResumeLayout(false); }
/// <summary> /// Execute a command batch and synchronizes the client data model with the newer records from the server. /// </summary> public static void Execute(Batch batch) { // This 'try' block will insure that the mutual exclusion locks are released. try { // Make sure only one thread at a time tries to refresh the data model. ClientMarketData.clientDataMutex.WaitOne(); // Switching credentials without exiting the application will force the data model to be cleared and the row // counting to begin at zero again. That is, it effectively resets the client data model. if (ClientMarketData.isReset) { ClientMarketData.isReset = false; ClientMarketData.rowVersion = 0; ClientMarketData.Clear(); } // IMPORTANT CONCEPT: The rowVersion keeps track of the state of the client in-memory database. The server // returns a value for us to use on the next cycle. long rowVersion = ClientMarketData.rowVersion; // IMPORTANT CONCEPT: Executing the 'GetClientMarketData' with a rowVersion returns to the client a DataSet // with only records that are the same age or younger than the rowVersion. This reduces the traffic on the // network to include only the essential records. We are also merging it with the current ClientMarketData, // which adds new records and records that were deleted by the server. AssemblyPlan assembly = batch.Assemblies.Add("Server Market Data"); TypePlan type = assembly.Types.Add("MarkThree.Guardian.Server.ServerMarketData"); TransactionPlan transaction = batch.Transactions.Add(); MethodPlan method = transaction.Methods.Add(type, "Reconcile"); Parameter rowVersionParameter = method.Parameters.Add(new InputParameter("rowVersion", rowVersion)); // Execute the the command to get the incremental changes from the server. WebTransactionProtocol.Execute(batch); // This is the updated data model information returned from the server. ArrayList reconciledData = (ArrayList)method.Parameters.Return.Value; // Optimization: Don't merge the results if there's nothing to merge. if (reconciledData != null) { // IMPORTANT CONCEPT: Since the results will still be on the server if the client misses a refresh // cycle, we take the attitude that thBzis update process doesn't have to wait for locks. That is, if we // can't get all the tables locked quickly, we'll just wait until the next refresh period to get the // results. This effectively prevents deadlocking on the client. Make sure all the tables are locked // before populating them. foreach (TableLock tableLock in ClientMarketData.TableLocks) { tableLock.AcquireWriterLock(ClientTimeout.LockWait); } // IMPORTANT CONCEPT: This broadcast can be used to set up conditions for the data event handlers. Often, // optimizing algorithms will be used to consolidate the results of a merge. This will allow the event // driven logic to clear previous results and set up initial states for handling the bulk update of data. ClientMarketData.OnBeginMerge(typeof(ClientMarketData)); // IMPORANT CONCEPT: Once all the write locks have been obtained, we can merge the results. This will // trigger the events associated with the tables for updated and deleted rows. Note that this 'Merge' // method has different characteristics than the original one provided by Microsoft (that is, this one // works). ClientMarketData.rowVersion = MarketData.Merge(reconciledData); // IMPORTANT CONCEPT: When the merge is complete, this will broadcast an event which allows optimization code // to consolidate the results, examine the changed values and update reports based on the changed data. ClientMarketData.OnEndMerge(typeof(ClientMarketData)); } } finally { // No matter what happens above, we need to release the locks acquired above. foreach (TableLock tableLock in ClientMarketData.TableLocks) { if (tableLock.IsWriterLockHeld) { tableLock.ReleaseWriterLock(); } } // Other threads can now request a refresh of the data model. ClientMarketData.clientDataMutex.ReleaseMutex(); } // Throw a specialized exception if the server returned any errors in the Batch structure. if (batch.HasExceptions) { throw new BatchException(batch); } }