Beispiel #1
0
        /// <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);
            }
        }