public void Save(bool autoRefresh = true)
        {
            JoinWaiter waiter = new JoinWaiter(() =>
            {
                JoinWaiter finalWaiter = new JoinWaiter(OnSaveComplete);

                if (this.RootId != null)
                {
                    this.RunTablesRefresh(this.RootId ?? 0, finalWaiter);
                }

                finalWaiter.Start();
            });

            JObject rootObject = JObjectEntityHelpers.CreateEntityObject(this.ModelName);

            bool initialSave = false;

            if (this.RootId != null)
                rootObject.SetId(this.RootId ?? 0);
            else
                initialSave = true;

            rootObject.SetModel(this.ModelName);

            foreach (var form in this.FormBindings)
            {
                form.DisableControls();
                JObject savedObject = form.SaveObject(diffOnly: true);
                rootObject.MergeFrom(savedObject);
            }

            foreach (var table in this.TableBindings)
            {
                table.Item1.DisableControls();
            }

            SaveContext rootSaver = this.Context.DeferredSave(this.ModelName, Util.BuildSaveParams(rootObject));
            waiter.AddProcess();

            rootSaver.OnComplete += (result) =>
            {
                if (autoRefresh)
                {
                    this.CachedData = result;

                    foreach (var form in this.FormBindings)
                    {
                        form.LoadObject(this.CachedData);
                        form.EnableControls();
                    }

                    if (initialSave && this.RootId != null)
                        this.RunTablesSave(this.RootId ?? 0, waiter);
                }

                waiter.ProcessComplete();
            };

            rootSaver.OnError += (error, message) =>
            {
                this.OnError(error, message);

                foreach (var form in this.FormBindings)
                {
                    form.EnableControls();
                }

                foreach (var tablePair in this.TableBindings)
                {
                    tablePair.Item1.EnableControls();
                }

                waiter.ProcessComplete();
            };

            rootSaver.Begin();

            if (!initialSave)
            {
                this.RunTablesSave(this.RootId ?? 0, waiter);
            }

            waiter.Start();
        }
        private void RunTablesSave(int id, JoinWaiter waiter)
        {
            foreach (var tablePair in this.TableBindings)
            {
                TableBinding table = tablePair.Item1;
                EntitySetModel mapping = tablePair.Item2;
                waiter.AddProcess();

                var subWaiter = new JoinWaiter(() =>
                {
                    table.EnableControls();
                    waiter.ProcessComplete();
                });

                table.DisableControls();
                JArray tableData = table.SaveArray(diffOnly: true);

                foreach (JObject value in tableData)
                {
                    subWaiter.AddProcess();

                    if (value.GetFields().Any())
                    {
                        value.SetField(mapping.KeyField, id.ToString());
                        SaveContext saver = this.Context.DeferredSave(value.GetModel(), Util.BuildSaveParams(value));

                        saver.OnComplete += (result) =>
                        {
                            subWaiter.ProcessComplete();
                        };

                        saver.OnError += (error, message) =>
                        {
                            this.OnError(error, message);
                            subWaiter.ProcessComplete();
                        };

                        saver.Begin();
                    }
                }

                JArray removedValues = table.DeletionArray();

                foreach (JObject value in removedValues)
                {
                    subWaiter.AddProcess();

                    if (mapping.Resolution == OrphanResolution.Null)
                    {
                        SaveContext saver = this.Context.DeferredSave(value.GetModel(), Util.CreateRequestParams("id", value.GetId().ToString(), mapping.KeyField, null));

                        saver.OnComplete += (result) =>
                        {
                            subWaiter.ProcessComplete();
                        };

                        saver.OnError += (error, message) =>
                        {
                            this.OnError(error, message);
                            subWaiter.ProcessComplete();
                        };

                        saver.Begin();
                    }
                    else
                    {
                        DeleteContext deleter = this.Context.DeferredDelete(value.GetModel(), value.GetId() ?? 0);

                        deleter.OnComplete += (result) =>
                        {
                            subWaiter.ProcessComplete();
                        };

                        deleter.OnError += (error, message) =>
                        {
                            this.OnError(error, message);
                            subWaiter.ProcessComplete();
                        };

                        deleter.Begin();
                    }
                }

                subWaiter.Start();
            }
        }
        public void Refresh(int? newId = null)
        {
            JoinWaiter waiter = new JoinWaiter(OnRefreshComplete);

            if (newId == null && this.RootId == null)
            {
                foreach (var form in this.FormBindings)
                {
                    form.DisableControls();
                }
                foreach (var table in this.TableBindings)
                {
                    table.Item1.DisableControls();
                }

                //this.OnError(TrackerErrorType.NullKey, "There is no active instance to refresh.");
                return;
            }

            int id = newId ?? this.RootId ?? 0;

            foreach (var form in this.FormBindings)
            {
                form.DisableControls();
            }

            SearchContext searcher = this.Context.DeferredSearch(this.ModelName, Util.CreateIdSearch(id));
            waiter.AddProcess();

            searcher.OnComplete += (results) =>
            {
                this.CachedData = results.First() as JObject;
                foreach (var form in this.FormBindings)
                {
                    form.LoadObject(this.CachedData);
                    form.EnableControls();
                }

                waiter.ProcessComplete();
            };

            searcher.OnError += (error, message) =>
            {
                this.OnError(error, message);
                waiter.ProcessComplete();

                foreach (var form in this.FormBindings)
                {
                    form.EnableControls();
                }
            };

            searcher.Begin();

            this.RunTablesRefresh(id, waiter);

            waiter.Start();
        }