// private methods -------------------------------------------------------------------------- private async Task TransactStartTask(ITableDataFlow source, ITableDataFlow destination) { transactionInProgress = true; await source.GetHeadersFromSourceAsync(); // transact column headers and meta datas destination.DataTable.TableName = source.DataTable.TableName; destination.CurrentRow = source.CurrentRow; // clear destination - remove all rows and columns, reaad if (ClearDestination) { destination.DataTable.Rows.Clear(); destination.DataTable.Columns.Clear(); foreach (DataColumn c in source.DataTable.Columns) { destination.DataTable.Columns.Add(new DataColumn(c.ColumnName) { Prefix = c.Prefix, DataType = c.DataType }); } } // only readd columns that don't already exist else { foreach (DataColumn c in source.DataTable.Columns) { if (!destination.DataTable.Columns.Contains(c.ColumnName)) { destination.DataTable.Columns.Add(new DataColumn(c.ColumnName) { Prefix = c.Prefix, DataType = c.DataType }); } } } await destination.PutHeaderToDestinationAsync(); if (destination.DataTable.Columns.Count > 0) { cancelSource = new CancellationTokenSource(); CancellationToken token = cancelSource.Token; try { await TransferOnePageTask(source, destination, token); } catch (Exception e) { HandleTransactException(e); } } transactionInProgress = false; }
// continuation task below private async Task TransferOnePageTask(ITableDataFlow source, ITableDataFlow destination, CancellationToken cancellationToken) { do { cancellationToken.ThrowIfCancellationRequested(); Tuple <int, int> tuple = await source.GetPageFromSourceAsync(); // restart transaction if (newTransactionPending) { newTransactionPending = false; await TransactStartTask(source, destination); } else { if (tuple.Item1 < tuple.Item2) // we actually got some data { TransactOnePage(source, destination, tuple.Item1, tuple.Item2, cancellationToken); if (!AutoLoadNextBatch) { // notify transaction await destination.PutPageToDestinationAsync(tuple.Item1, tuple.Item2, () => { if (tuple.Item1 < tuple.Item2) { // KL: bug of unplugging device while loading session data var _ = TransferOnePageTask(source, destination, cancellationToken); _.ContinueWith((t) => HandleTransactException(t.Exception), TaskContinuationOptions.OnlyOnFaulted); } }); } } else { transactionInProgress = false; await destination.PutPageToDestinationAsync(AutoLoadNextBatch? 0 : tuple.Item1, tuple.Item2, null); eventCompleteNoErrors?.Execute(); if (transactCompleteFlag != null) { transactCompleteFlag.Data = true; } if (dataFlowTransacting != null) { dataFlowTransacting.Data = false; } break; } } transactionInProgress = false; }while (AutoLoadNextBatch); }
// start to transact the data from source to destination // firstly trasact the headers, then the columns // the destination will be filled with the data that it does not have in the source private void TransactOnePage(ITableDataFlow source, ITableDataFlow destination, int firstRowIndex, int lastRowIndex, CancellationToken token) { var dtSource = source.DataTable; var dtDestination = destination.DataTable; // -------------------------------------------- // transact rows for (int i = firstRowIndex; i < lastRowIndex; i++) { token.ThrowIfCancellationRequested(); dtDestination.ImportRow(dtSource.Rows[i]); // the reason of using i+1 is the index of a table starts from 0, the user should see 1 when it's 0. foreach (var d in dataFlowsIndex) { d.Data = (i + 1).ToString(); } } // if merge key is set - remove any duplicates, prioritizing rows with a higher index // the "MergeKey" will be checked in each row and compared - if they are equal, remove if (!String.IsNullOrEmpty(MergeKey)) { for (int i = 0; i < dtDestination.Rows.Count; i++) { for (int j = i + 1; j < dtDestination.Rows.Count; j++) { if (dtDestination.Rows[i][MergeKey].Equals(dtDestination.Rows[j][MergeKey])) { dtDestination.Rows.RemoveAt(i); break; } } } } }