using Microsoft.Win32; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Data.Entity.Validation; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using Tango.Core.Commands; using Tango.DAL.Local.DB; using Tango.DAL.Observables; using Tango.DAL.Remote.DB; using Tango.Logging; using Tango.MachineStudio.Common.Notifications; using Tango.MachineStudio.Common.StudioApplication; using Tango.MachineStudio.Synchronization.Navigation; using Tango.Settings; using Tango.SharedUI; using Tango.Synchronization; using Tango.Synchronization.Local; using Tango.Synchronization.Remote; namespace Tango.MachineStudio.Synchronization.ViewModels { public class RemoteSynchronizationViewVM : ViewModel, IShutdownRequestBlocker { private SyncNavigationManager _navigation; private String _slaveDBFile; private INotificationProvider _notification; private bool _isWorking; private RemoteDBComparer _comparer; private RemoteDB _remoteDB; private LocalDB _localDB; #region Constructors /// /// Initializes a new instance of the class. /// /// The navigation. /// The notification. public RemoteSynchronizationViewVM(SyncNavigationManager navigation, INotificationProvider notification) { _navigation = navigation; _notification = notification; BackCommand = new RelayCommand(() => _navigation.NavigateTo(NavigationView.MenuView)); Differences = new ObservableCollection(); BrowseSlaveDBCommand = new RelayCommand(BrowseSlaveDB, (x) => !_isWorking); CompareCommand = new RelayCommand(Compare, (x) => SlaveDBFile != null && !_isWorking && SelectedMachine != null); CommitCommand = new RelayCommand(Commit, (x) => SelectedDifference != null && !_isWorking && SelectedMachine != null); CommitAllCommand = new RelayCommand(CommitAll, (x) => Differences.Count > 0 && !_isWorking && SelectedMachine != null); CleanCommand = new RelayCommand(CleanSlave, (x) => !_isWorking && SlaveDBFile != null); if (File.Exists(SettingsManager.Default.MachineStudio.SynchronizationModule.RemoteSQLiteFile)) { SlaveDBFile = SettingsManager.Default.MachineStudio.SynchronizationModule.RemoteSQLiteFile; SlaveDBName = Path.GetFileName(SlaveDBFile); } } #endregion #region Commands /// /// Gets or sets the back command. /// public RelayCommand BackCommand { get; set; } /// /// Gets or sets the browse master database command. /// public RelayCommand BrowseMasterDBCommand { get; set; } /// /// Gets or sets the browse slave database command. /// public RelayCommand BrowseSlaveDBCommand { get; set; } /// /// Gets or sets the compare command. /// public RelayCommand CompareCommand { get; set; } /// /// Gets or sets the commit command. /// public RelayCommand CommitCommand { get; set; } /// /// Gets or sets the commit all command. /// public RelayCommand CommitAllCommand { get; set; } /// /// Gets or sets the clean command. /// public RelayCommand CleanCommand { get; set; } #endregion #region Properties private ObservableCollection _differences; /// /// Gets or sets the differences. /// public ObservableCollection Differences { get { return _differences; } set { _differences = value; RaisePropertyChanged(nameof(Differences)); } } private Diff _selectedDifference; /// /// Gets or sets the selected difference. /// public Diff SelectedDifference { get { return _selectedDifference; } set { _selectedDifference = value; RaisePropertyChanged(nameof(SelectedDifference)); InvalidateRelayCommands(); } } private String _slaveDBName; /// /// Gets or sets the name of the slave database. /// public String SlaveDBName { get { return _slaveDBName; } set { _slaveDBName = value; RaisePropertyChanged(nameof(SlaveDBName)); } } private Machine _selectedMachine; /// /// Gets or sets the selected machine. /// public Machine SelectedMachine { get { return _selectedMachine; } set { _selectedMachine = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); } } /// /// Gets or sets the slave database file. /// public String SlaveDBFile { get { return _slaveDBFile; } set { _slaveDBFile = value; RaisePropertyChangedAuto(); } } #endregion #region Private Methods private async void CleanSlave() { if (_notification.ShowQuestion("Are you sure you want to erase all data on slave database?")) { using (_notification.PushTaskItem("Clearing database...")) { try { _isWorking = true; await Task.Factory.StartNew(() => { SQLiteDataBase localDB = new SQLiteDataBase(SlaveDBFile); localDB.LoadTables(); localDB.ClearDataBase(); try { localDB.Dispose(); } catch{ } }); Differences.Clear(); } catch (Exception ex) { ShowError(LogManager.Log(ex).Message); } finally { _isWorking = false; InvalidateRelayCommands(); SelectedDifference = null; } } } } private void Compare() { Task.Factory.StartNew(() => { using (_notification.PushTaskItem("Comparing Databases...")) { try { if (_comparer != null) { _comparer.Dispose(); } _remoteDB = RemoteDB.CreateDefault(); _localDB = new LocalDB(SlaveDBFile); _comparer = new RemoteDBComparer(_remoteDB, _localDB, SelectedMachine.SerialNumber); _isWorking = true; InvalidateRelayCommands(); Thread.Sleep(1500); var diffs = _comparer.Compare(); Differences = new ObservableCollection(diffs); if (diffs.Where(x => x.Action != DiffAction.ReplaceTableDataInSlave).Count() > 0) { ShowInfo("Found " + Differences.Where(x => x.Action != DiffAction.ReplaceTableDataInSlave).Count() + " differences."); } else { ShowInfo("The master and slave databases are synchronized."); } } catch (Exception ex) { ShowError(ex.Message); } finally { _isWorking = false; SelectedDifference = null; InvalidateRelayCommands(); SettingsManager.Default.MachineStudio.SynchronizationModule.RemoteSQLiteFile = SlaveDBFile; SettingsManager.SaveDefaultSettings(); } } }); } private void Commit() { Task.Factory.StartNew(() => { using (_notification.PushTaskItem("Committing difference...")) { try { _isWorking = true; InvalidateRelayCommands(); Thread.Sleep(1500); SelectedDifference.Commit(); _remoteDB.SaveChanges(); _localDB.SaveChanges(); InvokeUINow(() => Differences.Remove(SelectedDifference)); } catch (Exception ex) { ShowError(ex.Message); } finally { _isWorking = false; SelectedDifference = null; InvalidateRelayCommands(); } } }); } private void CommitAll() { Task.Factory.StartNew(() => { using (_notification.PushTaskItem("Committing all differences...")) { try { _isWorking = true; InvalidateRelayCommands(); Thread.Sleep(1500); for (int i = 0; i < Differences.Count; i++) { var diff = Differences[i]; using (_notification.PushTaskItem("Committing difference " + (Differences.IndexOf(diff) + 1) + "...")) { diff.Commit(); InvokeUINow(() => Differences.Remove(diff)); i--; } } _remoteDB.SaveChanges(); _localDB.SaveChanges(); } catch (DbEntityValidationException ex) { String message = "The following validation errors occurred while trying to update the database." + Environment.NewLine + Environment.NewLine; foreach (var error in ex.EntityValidationErrors.SelectMany(x => x.ValidationErrors).ToList()) { message += error.ErrorMessage + Environment.NewLine; } ShowError(message); } catch (Exception ex) { ShowError(ex.Message); } finally { _isWorking = false; SelectedDifference = null; InvalidateRelayCommands(); } } }); } private void BrowseSlaveDB() { String file = BrowseForFilePath(); if (file != null) { SlaveDBFile = file; SlaveDBName = Path.GetFileName(file); InvalidateRelayCommands(); } } private String BrowseForFilePath() { OpenFileDialog dlg = new OpenFileDialog(); dlg.Title = "Select SQLite Database File"; dlg.Filter = "SQLite Database|*.db"; if (dlg.ShowDialog().Value) { return dlg.FileName; } return null; } private void ShowError(String message) { InvokeUINow(() => _notification.ShowError(message)); } private void ShowInfo(String message) { InvokeUINow(() => _notification.ShowInfo(message)); } public Task OnShutdownRequest() { if (_comparer != null) { _comparer.Dispose(); } return Task.FromResult(true); } #endregion } }