Ejemplo n.º 1
0
 /// <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);
     }
 }
Ejemplo n.º 2
0
        /// <summary>
        /// Execute a command batch and synchronizes the client data model with the newer records from the server.
        /// </summary>
        public static void Execute(RemoteBatch remoteBatch)
        {
            // This 'try' block will insure that the mutual exclusion locks are released.
            try
            {
                // Maure sure only one thread at a time tries to refresh the data model.
                ClientMarketData.refreshMutex.WaitOne();

                // 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.  If, for any reason, the merge of the data on the client should fail,
                // we won't use the new value on the next cycle. That is, we're going to keep on asking for the same incremental
                // set of records until we've successfully merged them.  This guarantees that the client and server databases stay
                // consistent with each other.
                DateTime timeStamp  = ClientMarketData.timeStamp;
                long     rowVersion = ClientMarketData.rowVersion;

                // IMPORTANT CONCEPT:  Executing the 'Reconcile' Web Service 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.  This set also contains the deleted records.  When the DataSet that is
                // returned from this Web Service is merged with the current data, the client will be reconciled with the server as
                // of the 'rowVersion' returned from the server.
                DataSet   resultSet;
                WebClient webClient = new WebClient();
                DataSet   dataSet   = webClient.ExecuteAndReconcile(ref timeStamp, ref rowVersion, remoteBatch, out resultSet);
                remoteBatch.Merge(resultSet);

                // If the time stamps are out of sync, then the server has reset since our last refresh (or this is the first time
                // refreshing the data mdoel).  This will reset the client side of the data model so that, after this refresh, the
                // client and the server will be in sync again.
                if (ClientMarketData.timeStamp != timeStamp)
                {
                    ClientMarketData.rowVersion = 0;
                    ClientMarketData.timeStamp  = timeStamp;
                    ClientMarketData.Clear();
                }

                // Optimization: Don't merge the results if there's nothing to merge.
                if (rowVersion > ClientMarketData.rowVersion)
                {
                    // 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));

                    // This 'try' block will insure that the locks are released if there's an error.
                    try
                    {
                        // IMPORTANT CONCEPT: Make sure that there aren't any nested locks.  Table locks have to be aquired in the
                        // same order for every thread that uses the shared data model.  It's possible that the current thread may
                        // have locked table 'B' if the preamble, then try to lock table 'A' during the processing of a command
                        // batch.  If another thread aquires the lock on table 'A' and blocks on table 'B', we will have a
                        // deadlock.  This is designed to catch situations where the 'Execute' method is called while tables are
                        // already locked.
                        Debug.Assert(!ClientMarketData.AreLocksHeld);

                        // IMPORTANT CONCEPT: Since the results will still be on the server if the client misses a refresh cycle,
                        // we take the attitude that this 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);
                        }

                        // 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.  Also, if
                        // "AcceptChanges" is invoked for a DataSet or a Table, every single record in the DataSet or DataTable
                        // will be "Committed", even though they are unchanged.  This is a VERY inefficient operation.  To get
                        // around this, an ArrayList of modified records is constructed during the Merge operation.  After the
                        // merge, only the new records are committed.
                        ClientMarketData.mergedRows.Clear();
                        ClientMarketData.Merge(dataSet);
                        for (int index = 0; index < ClientMarketData.mergedRows.Count; index++)
                        {
                            ((DataRow)ClientMarketData.mergedRows[index]).AcceptChanges();
                        }

                        // If the merge operation was successful, then we can use the new rowVersion for the next cycle. Any
                        // exception before this point will result in a request of the same set of data becaue the rowVersion was
                        // never updated.
                        ClientMarketData.rowVersion = rowVersion;
                    }
                    catch (ConstraintException)
                    {
                        // Write out the exact location of the error.
                        foreach (DataTable dataTable in ClientMarketData.Tables)
                        {
                            foreach (DataRow dataRow in dataTable.Rows)
                            {
                                if (dataRow.HasErrors)
                                {
                                    Console.WriteLine("Error in '{0}': {1}", dataRow.Table.TableName, dataRow.RowError);
                                }
                            }
                        }
                    }
                    catch (Exception exception)
                    {
                        // Write the error and stack trace out to the debug listener
                        Debug.WriteLine(String.Format("{0}, {1}", exception.Message, exception.StackTrace));
                    }
                    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();
                            }
                        }
                    }

                    // IMPORTANT CONCEPT: When the merge is complete and the tables are unlocked, 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
            {
                // Other threads can now request a refresh of the data model.
                ClientMarketData.refreshMutex.ReleaseMutex();
            }

            // Throw a specialized exception if the server returned any errors in the RemoteBatch structure.
            if (remoteBatch.HasExceptions)
            {
                throw new BatchException(remoteBatch);
            }
        }
Ejemplo n.º 3
0
        /// <summary>
        /// ThreadBackground
        /// </summary>
        /// <remarks>
        /// This procedure runs as a thread.  It will asynchronously query the database for the raw data that makes up the
        /// hierarchy of objects found in the application.  It will then organize the raw, flat data into a hierarchial
        /// tree that's suitable to be viewed in a TreeView control.</remarks>
        private static void ThreadBackground()
        {
            // Create a web client for communicating with the server.
            WebClient webClient = new WebClient();

            // This thread body is executed until the application is terminated.
            while (true)
            {
                try
                {
                    // Maure sure only one thread at a time tries to refresh the data model.
                    ClientMarketData.refreshMutex.WaitOne();

                    // 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.  If, for any reason, the merge of the data on the client
                    // should fail, we won't use the new value on the next cycle. That is, we're going to keep on asking for the
                    // same incremental set of records until we've successfully merged them.  This guarantees that the client and
                    // server databases stay consistent with each other.
                    DateTime timeStamp  = ClientMarketData.timeStamp;
                    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.
                    DataSet dataSet = webClient.Reconcile(ref timeStamp, ref rowVersion);

                    // If the time stamps are out of sync, then the server has reset since our last refresh (or this is the first
                    // time refreshing the market data).  This will reset the client side of the data model so that -- after the
                    // current results are merged -- the client and the server will be in sync again.
                    if (ClientMarketData.timeStamp != timeStamp)
                    {
                        ClientMarketData.rowVersion = 0;
                        ClientMarketData.timeStamp  = timeStamp;
                        ClientMarketData.Clear();
                    }

                    // Optimization: Don't merge the results if there's nothing to merge.
                    if (rowVersion > ClientMarketData.rowVersion)
                    {
                        // 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));

                        try
                        {
                            // IMPORTANT CONCEPT: Since the results will still be on the server if the client misses a refresh
                            // cycle, we take the attitude that this 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);
                            }

                            // 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.  Also, if
                            // "AcceptChanges" is invoked for a DataSet or a Table, every single record in the DataSet or DataTable
                            // will be "Committed", even though they are unchanged.  This is a VERY inefficient operation.  To get
                            // around this, an ArrayList of modified records is constructed during the Merge operation.  After the
                            // merge, only the new records are committed.
                            ClientMarketData.mergedRows.Clear();
                            ClientMarketData.Merge(dataSet);
                            for (int index = 0; index < ClientMarketData.mergedRows.Count; index++)
                            {
                                ((DataRow)ClientMarketData.mergedRows[index]).AcceptChanges();
                            }

                            // If the merge operation was successful, then we can use the new rowVersion for the next cycle. Any
                            // exception before this point will result in a request of the same set of data becaue the rowVersion
                            // was never updated.
                            ClientMarketData.rowVersion = rowVersion;
                        }
                        catch (ApplicationException applicationException)
                        {
                            // Tyipcally, a failure to gain a lock will invoke this exception.  If this information ends up being
                            // too much for the log device, we can alway inhibit exception catching here.
                            Debug.WriteLine(applicationException.Message);
                        }
                        catch (ConstraintException)
                        {
                            // Write out the exact location of the error.
                            foreach (DataTable dataTable in ClientMarketData.Tables)
                            {
                                foreach (DataRow dataRow in dataTable.Rows)
                                {
                                    if (dataRow.HasErrors)
                                    {
                                        Console.WriteLine("Error in '{0}': {1}", dataRow.Table.TableName, dataRow.RowError);
                                    }
                                }
                            }
                        }
                        catch (Exception exception)
                        {
                            // Write the error and stack trace out to the debug listener
                            Debug.WriteLine(String.Format("{0}, {1}", exception.Message, exception.StackTrace));
                        }
                        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();
                                }
                            }
                        }

                        // 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));
                    }
                }
                catch (Exception exception)
                {
                    // Write the error and stack trace out to the debug listener
                    Debug.WriteLine(String.Format("{0}, {1}", exception.Message, exception.StackTrace));
                }
                finally
                {
                    // Other threads can now request a refresh of the data model.
                    ClientMarketData.refreshMutex.ReleaseMutex();
                }

                // Wait for a predetermined interval before refreshing the data model again.
                Thread.Sleep(ClientTimeout.RefreshInterval);
            }
        }
Ejemplo n.º 4
0
 /// <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(FolderList));
     this.treeView               = new System.Windows.Forms.TreeView();
     this.contextMenu            = new System.Windows.Forms.ContextMenu();
     this.menuItemOpen           = new System.Windows.Forms.MenuItem();
     this.menuItem3              = new System.Windows.Forms.MenuItem();
     this.menuItemDelete         = new System.Windows.Forms.MenuItem();
     this.menuItemRename         = new System.Windows.Forms.MenuItem();
     this.menuItem1              = new System.Windows.Forms.MenuItem();
     this.menuItemProperties     = new System.Windows.Forms.MenuItem();
     this.imageList              = new System.Windows.Forms.ImageList(this.components);
     this.clientClientMarketData = new Shadows.Quasar.Client.ClientMarketData(this.components);
     this.SuspendLayout();
     //
     // treeView
     //
     this.treeView.AllowDrop       = true;
     this.treeView.ContextMenu     = this.contextMenu;
     this.treeView.Dock            = System.Windows.Forms.DockStyle.Fill;
     this.treeView.ImageList       = this.imageList;
     this.treeView.LabelEdit       = true;
     this.treeView.Name            = "treeView";
     this.treeView.Size            = new System.Drawing.Size(150, 112);
     this.treeView.Sorted          = true;
     this.treeView.TabIndex        = 0;
     this.treeView.MouseDown      += new System.Windows.Forms.MouseEventHandler(this.treeView_MouseDown);
     this.treeView.DragOver       += new System.Windows.Forms.DragEventHandler(this.treeView_DragOver);
     this.treeView.AfterSelect    += new System.Windows.Forms.TreeViewEventHandler(this.treeView_AfterSelect);
     this.treeView.AfterLabelEdit += new System.Windows.Forms.NodeLabelEditEventHandler(this.treeView_AfterLabelEdit);
     this.treeView.ItemDrag       += new System.Windows.Forms.ItemDragEventHandler(this.treeView_ItemDrag);
     this.treeView.DragDrop       += new System.Windows.Forms.DragEventHandler(this.treeView_DragDrop);
     //
     // contextMenu
     //
     this.contextMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
         this.menuItemOpen,
         this.menuItem3,
         this.menuItemDelete,
         this.menuItemRename,
         this.menuItem1,
         this.menuItemProperties
     });
     //
     // menuItemOpen
     //
     this.menuItemOpen.Index  = 0;
     this.menuItemOpen.Text   = "&Open";
     this.menuItemOpen.Click += new System.EventHandler(this.menuItemOpen_Click);
     //
     // menuItem3
     //
     this.menuItem3.Index = 1;
     this.menuItem3.Text  = "-";
     //
     // menuItemDelete
     //
     this.menuItemDelete.Index = 2;
     this.menuItemDelete.Text  = "&Delete";
     //
     // menuItemRename
     //
     this.menuItemRename.Index  = 3;
     this.menuItemRename.Text   = "&Rename";
     this.menuItemRename.Click += new System.EventHandler(this.menuItemRename_Click);
     //
     // menuItem1
     //
     this.menuItem1.Index = 4;
     this.menuItem1.Text  = "-";
     //
     // menuItemProperties
     //
     this.menuItemProperties.Index  = 5;
     this.menuItemProperties.Text   = "&Properties";
     this.menuItemProperties.Click += new System.EventHandler(this.menuItemProperties_Click);
     //
     // imageList
     //
     this.imageList.ColorDepth       = System.Windows.Forms.ColorDepth.Depth24Bit;
     this.imageList.ImageSize        = new System.Drawing.Size(16, 16);
     this.imageList.ImageStream      = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("imageList.ImageStream")));
     this.imageList.TransparentColor = System.Drawing.Color.Aqua;
     //
     // FolderList
     //
     this.Controls.AddRange(new System.Windows.Forms.Control[] {
         this.treeView
     });
     this.Name = "FolderList";
     this.Size = new System.Drawing.Size(150, 112);
     this.ResumeLayout(false);
 }