Пример #1
0
        /// <summary>
        /// Gets a value indicating whether the DmState has changes
        /// </summary>
        public bool HasChanges(DmRowState rowStates)
        {
            try
            {
                const DmRowState allRowStates = DmRowState.Detached | DmRowState.Unchanged | DmRowState.Added | DmRowState.Deleted | DmRowState.Modified;

                if ((rowStates & (~allRowStates)) != 0)
                {
                    throw new ArgumentOutOfRangeException(nameof(rowStates));
                }

                for (int i = 0; i < Tables.Count; i++)
                {
                    DmTable table = Tables[i];

                    for (int j = 0; j < table.Rows.Count; j++)
                    {
                        DmRow row = table.Rows[j];
                        if ((row.RowState & rowStates) != 0)
                        {
                            return(true);
                        }
                    }
                }
                return(false);
            }
            finally
            {
            }
        }
Пример #2
0
 public AppliedChangesEventArgs(DmView changes, DmRowState state, DbConnection connection, DbTransaction transaction)
 {
     this.Changes     = changes;
     this.State       = state;
     this.Connection  = connection;
     this.Transaction = transaction;
     this.Action      = ChangeApplicationAction.Continue;
 }
Пример #3
0
        private void ConvertToSurrogateRecords(DmRow row)
        {
            int          count      = row.Table.Columns.Count;
            DmRowState   rowState   = row.RowState;
            DmRowVersion rowVersion = rowState == DmRowState.Deleted ? DmRowVersion.Original : DmRowVersion.Current;

            for (int i = 0; i < count; i++)
            {
                this.Records[i].Add(row[i, rowVersion]);
            }
        }
Пример #4
0
        public DmView(DmTable table, DmRowState state)
        {
            if (table == null)
            {
                return;
            }

            this.Table = table;
            Predicate <DmRow> filter = new Predicate <DmRow>(r => r.RowState == state);

            this.internalFilter(filter, this.Table.Rows);
        }
        /// <summary>
        /// Get a DmRow state to know is we have an inserted, updated, or deleted row to apply
        /// </summary>
        private DmRowState GetStateFromDmRow(DmRow dataRow, ScopeInfo scopeInfo)
        {
            DmRowState dmRowState = DmRowState.Unchanged;

            var isTombstone = Convert.ToInt64(dataRow["sync_row_is_tombstone"]) > 0;

            if (isTombstone)
            {
                dmRowState = DmRowState.Deleted;
            }
            else
            {
                var createdTimeStamp = DbManager.ParseTimestamp(dataRow["create_timestamp"]);
                var updatedTimeStamp = DbManager.ParseTimestamp(dataRow["update_timestamp"]);
                var updateScopeIdRow = dataRow["update_scope_id"];
                var createScopeIdRow = dataRow["create_scope_id"];

                Guid?updateScopeId = (updateScopeIdRow != DBNull.Value && updateScopeIdRow != null) ? (Guid)updateScopeIdRow : (Guid?)null;
                Guid?createScopeId = (createScopeIdRow != DBNull.Value && createScopeIdRow != null) ? (Guid)createScopeIdRow : (Guid?)null;

                var isLocallyCreated = !createScopeId.HasValue;
                var islocallyUpdated = !updateScopeId.HasValue || updateScopeId.Value != scopeInfo.Id;


                // Check if a row is modified :
                // 1) Row is not new
                // 2) Row update is AFTER last sync of asker
                // 3) Row insert is BEFORE last sync of asker (if insert is after last sync, it's not an update, it's an insert)
                if (!scopeInfo.IsNewScope && islocallyUpdated && updatedTimeStamp > scopeInfo.Timestamp && (createdTimeStamp <= scopeInfo.Timestamp || !isLocallyCreated))
                {
                    dmRowState = DmRowState.Modified;
                }
                else if (scopeInfo.IsNewScope || (isLocallyCreated && createdTimeStamp >= scopeInfo.Timestamp))
                {
                    dmRowState = DmRowState.Added;
                }
                // The line has been updated from an other host
                else if (islocallyUpdated && updateScopeId.HasValue && updateScopeId.Value != scopeInfo.Id)
                {
                    dmRowState = DmRowState.Modified;
                }
                else
                {
                    dmRowState = DmRowState.Unchanged;
                    Debug.WriteLine($"Row is in Unchanegd state. " +
                                    $"\tscopeInfo.Id:{scopeInfo.Id}, scopeInfo.IsNewScope :{scopeInfo.IsNewScope}, scopeInfo.LastTimestamp:{scopeInfo.Timestamp}" +
                                    $"\tcreateScopeId:{createScopeId}, updateScopeId:{updateScopeId}, createdTimeStamp:{createdTimeStamp}, updatedTimeStamp:{updatedTimeStamp}.");
                }
            }

            return(dmRowState);
        }
Пример #6
0
        public DmSet GetChanges(DmRowState rowStates)
        {
            DmSet dsNew = null;

            if (0 != (rowStates & ~(DmRowState.Added | DmRowState.Deleted | DmRowState.Modified | DmRowState.Unchanged)))
            {
                throw new Exception($"InvalidRowState {rowStates}");
            }

            // Initialize all the individual table bitmaps.
            TableChanges[] bitMatrix = new TableChanges[Tables.Count];

            for (int i = 0; i < bitMatrix.Length; ++i)
            {
                bitMatrix[i] = new TableChanges(Tables[i].Rows.Count);
            }

            // find all the modified rows and their parents
            MarkModifiedRows(bitMatrix, rowStates);

            // copy the changes to a cloned table
            for (int i = 0; i < bitMatrix.Length; ++i)
            {
                Debug.Assert(0 <= bitMatrix[i].HasChanges, "negative change count");
                if (0 < bitMatrix[i].HasChanges)
                {
                    if (dsNew == null)
                    {
                        dsNew = this.Clone();
                    }

                    DmTable table     = this.Tables[i];
                    DmTable destTable = dsNew.Tables.First(t => t.TableName == table.TableName);

                    for (int j = 0; 0 < bitMatrix[i].HasChanges; ++j)
                    { // Loop through the rows.
                        if (bitMatrix[i][j])
                        {
                            destTable.ImportRow(table.Rows[j]);
                            bitMatrix[i].HasChanges--;
                        }
                    }
                }
            }
            return(dsNew);
        }
Пример #7
0
        /// <summary>
        /// Get Changes from the DmTable, with a DmRowState value
        /// </summary>
        public DmTable GetChanges(DmRowState rowStates)
        {
            DmTable dtChanges = this.Clone();
            DmRow   row       = null;

            for (int i = 0; i < Rows.Count; i++)
            {
                row = Rows[i];
                if ((row.RowState & rowStates) == row.RowState)
                {
                    dtChanges.ImportRow(row);
                }
            }

            if (dtChanges.Rows.Count == 0)
            {
                return(null);
            }

            return(dtChanges);
        }
Пример #8
0
        private void MarkModifiedRows(TableChanges[] bitMatrix, DmRowState rowStates)
        {
            // for every table, every row & every relation find the modified rows and for non-deleted rows, their parents
            for (int tableIndex = 0; tableIndex < bitMatrix.Length; ++tableIndex)
            {
                var rows     = Tables[tableIndex].Rows;
                int rowCount = rows.Count;

                for (int rowIndex = 0; rowIndex < rowCount; ++rowIndex)
                {
                    DmRow      row      = rows[rowIndex];
                    DmRowState rowState = row.RowState;

                    // if bit not already set and row is modified
                    if (((rowStates & rowState) != 0) && !bitMatrix[tableIndex][rowIndex])
                    {
                        bitMatrix[tableIndex][rowIndex] = true;
                    }
                }
            }
        }
        /// <summary>
        /// Enumerate all internal changes, no batch mode
        /// </summary>
        internal async Task <(BatchInfo, ChangesSelected)> EnumerateChangesInBatchesInternal
            (SyncContext context, ScopeInfo scopeInfo, int downloadBatchSizeInKB, DmSet configTables, string batchDirectory, ConflictResolutionPolicy policy, ICollection <FilterClause> filters)
        {
            DmTable dmTable = null;
            // memory size total
            double memorySizeFromDmRows = 0L;

            int batchIndex = 0;

            // this batch info won't be in memory, it will be be batched
            BatchInfo batchInfo = new BatchInfo
            {
                // directory where all files will be stored
                Directory = BatchInfo.GenerateNewDirectoryName(),
                // not in memory since we serialized all files in the tmp directory
                InMemory = false
            };

            // Create stats object to store changes count
            ChangesSelected changes = new ChangesSelected();

            using (var connection = this.CreateConnection())
            {
                try
                {
                    // Open the connection
                    await connection.OpenAsync();

                    using (var transaction = connection.BeginTransaction())
                    {
                        // create the in memory changes set
                        DmSet changesSet = new DmSet(configTables.DmSetName);

                        foreach (var tableDescription in configTables.Tables)
                        {
                            // if we are in upload stage, so check if table is not download only
                            if (context.SyncWay == SyncWay.Upload && tableDescription.SyncDirection == SyncDirection.DownloadOnly)
                            {
                                continue;
                            }

                            // if we are in download stage, so check if table is not download only
                            if (context.SyncWay == SyncWay.Download && tableDescription.SyncDirection == SyncDirection.UploadOnly)
                            {
                                continue;
                            }

                            var builder     = this.GetDatabaseBuilder(tableDescription);
                            var syncAdapter = builder.CreateSyncAdapter(connection, transaction);
                            syncAdapter.ConflictApplyAction = SyncConfiguration.GetApplyAction(policy);

                            // raise before event
                            context.SyncStage = SyncStage.TableChangesSelecting;
                            var beforeArgs = new TableChangesSelectingEventArgs(this.ProviderTypeName, context.SyncStage, tableDescription.TableName);
                            this.TryRaiseProgressEvent(beforeArgs, this.TableChangesSelecting);

                            // Get Command
                            DbCommand     selectIncrementalChangesCommand;
                            DbCommandType dbCommandType;

                            if (this.CanBeServerProvider && context.Parameters != null && context.Parameters.Count > 0 && filters != null && filters.Count > 0)
                            {
                                var filtersName = filters
                                                  .Where(f => f.TableName.Equals(tableDescription.TableName, StringComparison.InvariantCultureIgnoreCase))
                                                  .Select(f => f.ColumnName);

                                if (filtersName != null && filtersName.Count() > 0)
                                {
                                    dbCommandType = DbCommandType.SelectChangesWitFilters;
                                    selectIncrementalChangesCommand = syncAdapter.GetCommand(dbCommandType, filtersName);
                                }
                                else
                                {
                                    dbCommandType = DbCommandType.SelectChanges;
                                    selectIncrementalChangesCommand = syncAdapter.GetCommand(dbCommandType);
                                }
                            }
                            else
                            {
                                dbCommandType = DbCommandType.SelectChanges;
                                selectIncrementalChangesCommand = syncAdapter.GetCommand(dbCommandType);
                            }

                            // Deriving Parameters
                            syncAdapter.SetCommandParameters(DbCommandType.SelectChanges, selectIncrementalChangesCommand);

                            if (selectIncrementalChangesCommand == null)
                            {
                                var exc = "Missing command 'SelectIncrementalChangesCommand' ";
                                throw new Exception(exc);
                            }

                            dmTable = BuildChangesTable(tableDescription.TableName, configTables);

                            try
                            {
                                // Set commons parameters
                                SetSelectChangesCommonParameters(context, scopeInfo, selectIncrementalChangesCommand);

                                // Set filter parameters if any
                                // Only on server side
                                if (this.CanBeServerProvider && context.Parameters != null && context.Parameters.Count > 0 && filters != null && filters.Count > 0)
                                {
                                    var filterTable = filters.Where(f => f.TableName.Equals(tableDescription.TableName, StringComparison.InvariantCultureIgnoreCase)).ToList();

                                    if (filterTable != null && filterTable.Count > 0)
                                    {
                                        foreach (var filter in filterTable)
                                        {
                                            var parameter = context.Parameters.FirstOrDefault(p => p.ColumnName.Equals(filter.ColumnName, StringComparison.InvariantCultureIgnoreCase) && p.TableName.Equals(filter.TableName, StringComparison.InvariantCultureIgnoreCase));

                                            if (parameter != null)
                                            {
                                                DbManager.SetParameterValue(selectIncrementalChangesCommand, parameter.ColumnName, parameter.Value);
                                            }
                                        }
                                    }
                                }

                                this.AddTrackingColumns <int>(dmTable, "sync_row_is_tombstone");

                                // Statistics
                                TableChangesSelected tableChangesSelected = new TableChangesSelected
                                {
                                    TableName = tableDescription.TableName
                                };

                                changes.TableChangesSelected.Add(tableChangesSelected);

                                // Get the reader
                                using (var dataReader = selectIncrementalChangesCommand.ExecuteReader())
                                {
                                    while (dataReader.Read())
                                    {
                                        DmRow dmRow = CreateRowFromReader(dataReader, dmTable);

                                        DmRowState state = DmRowState.Unchanged;

                                        state = GetStateFromDmRow(dmRow, scopeInfo);

                                        // If the row is not deleted inserted or modified, go next
                                        if (state != DmRowState.Deleted && state != DmRowState.Modified && state != DmRowState.Added)
                                        {
                                            continue;
                                        }

                                        var fieldsSize = DmTableSurrogate.GetRowSizeFromDataRow(dmRow);
                                        var dmRowSize  = fieldsSize / 1024d;

                                        if (dmRowSize > downloadBatchSizeInKB)
                                        {
                                            var exc = $"Row is too big ({dmRowSize} kb.) for the current Configuration.DownloadBatchSizeInKB ({downloadBatchSizeInKB} kb.) Aborting Sync...";
                                            throw new Exception(exc);
                                        }

                                        // Calculate the new memory size
                                        memorySizeFromDmRows = memorySizeFromDmRows + dmRowSize;

                                        // add row
                                        dmTable.Rows.Add(dmRow);
                                        tableChangesSelected.TotalChanges++;

                                        // acceptchanges before modifying
                                        dmRow.AcceptChanges();

                                        // Set the correct state to be applied
                                        if (state == DmRowState.Deleted)
                                        {
                                            dmRow.Delete();
                                            tableChangesSelected.Deletes++;
                                        }
                                        else if (state == DmRowState.Added)
                                        {
                                            dmRow.SetAdded();
                                            tableChangesSelected.Inserts++;
                                        }
                                        else if (state == DmRowState.Modified)
                                        {
                                            dmRow.SetModified();
                                            tableChangesSelected.Updates++;
                                        }

                                        // We exceed the memorySize, so we can add it to a batch
                                        if (memorySizeFromDmRows > downloadBatchSizeInKB)
                                        {
                                            // Since we dont need this column anymore, remove it
                                            this.RemoveTrackingColumns(dmTable, "sync_row_is_tombstone");

                                            changesSet.Tables.Add(dmTable);

                                            // generate the batch part info
                                            batchInfo.GenerateBatchInfo(batchIndex, changesSet, batchDirectory);

                                            // increment batch index
                                            batchIndex++;

                                            changesSet.Clear();

                                            // Recreate an empty DmSet, then a dmTable clone
                                            changesSet = new DmSet(configTables.DmSetName);
                                            dmTable    = dmTable.Clone();
                                            this.AddTrackingColumns <int>(dmTable, "sync_row_is_tombstone");

                                            // Init the row memory size
                                            memorySizeFromDmRows = 0L;

                                            // add stats for a SyncProgress event
                                            context.SyncStage = SyncStage.TableChangesSelected;
                                            var args2 = new TableChangesSelectedEventArgs
                                                            (this.ProviderTypeName, SyncStage.TableChangesSelected, tableChangesSelected);

                                            this.TryRaiseProgressEvent(args2, this.TableChangesSelected);
                                        }
                                    }

                                    // Since we dont need this column anymore, remove it
                                    this.RemoveTrackingColumns(dmTable, "sync_row_is_tombstone");

                                    context.SyncStage = SyncStage.TableChangesSelected;

                                    changesSet.Tables.Add(dmTable);

                                    // Init the row memory size
                                    memorySizeFromDmRows = 0L;

                                    // Event progress
                                    context.SyncStage = SyncStage.TableChangesSelected;
                                    var args = new TableChangesSelectedEventArgs(this.ProviderTypeName, SyncStage.TableChangesSelected, tableChangesSelected);
                                    this.TryRaiseProgressEvent(args, this.TableChangesSelected);
                                }
                            }
                            catch (Exception)
                            {
                                throw;
                            }
                            finally
                            {
                            }
                        }

                        // We are in batch mode, and we are at the last batchpart info
                        if (changesSet != null && changesSet.HasTables && changesSet.HasChanges())
                        {
                            var batchPartInfo = batchInfo.GenerateBatchInfo(batchIndex, changesSet, batchDirectory);

                            if (batchPartInfo != null)
                            {
                                batchPartInfo.IsLastBatch = true;
                            }
                        }

                        transaction.Commit();
                    }
                }
                catch (Exception)
                {
                    throw;
                }
                finally
                {
                    if (connection != null && connection.State == ConnectionState.Open)
                    {
                        connection.Close();
                    }
                }
            }

            return(batchInfo, changes);
        }
        /// <summary>
        /// Enumerate all internal changes, no batch mode
        /// </summary>
        internal async Task <(BatchInfo, ChangesSelected)> EnumerateChangesInternal(
            SyncContext context, ScopeInfo scopeInfo, DmSet configTables, string batchDirectory, ConflictResolutionPolicy policy, ICollection <FilterClause> filters)
        {
            // create the in memory changes set
            DmSet changesSet = new DmSet(SyncConfiguration.DMSET_NAME);

            // Create the batch info, in memory
            var batchInfo = new BatchInfo
            {
                InMemory = true
            };

            using (var connection = this.CreateConnection())
            {
                // Open the connection
                await connection.OpenAsync();

                using (var transaction = connection.BeginTransaction())
                {
                    try
                    {
                        // changes that will be returned as selected changes
                        ChangesSelected changes = new ChangesSelected();

                        foreach (var tableDescription in configTables.Tables)
                        {
                            // if we are in upload stage, so check if table is not download only
                            if (context.SyncWay == SyncWay.Upload && tableDescription.SyncDirection == SyncDirection.DownloadOnly)
                            {
                                continue;
                            }

                            // if we are in download stage, so check if table is not download only
                            if (context.SyncWay == SyncWay.Download && tableDescription.SyncDirection == SyncDirection.UploadOnly)
                            {
                                continue;
                            }

                            var builder     = this.GetDatabaseBuilder(tableDescription);
                            var syncAdapter = builder.CreateSyncAdapter(connection, transaction);
                            syncAdapter.ConflictApplyAction = SyncConfiguration.GetApplyAction(policy);

                            // raise before event
                            context.SyncStage = SyncStage.TableChangesSelecting;
                            var beforeArgs = new TableChangesSelectingEventArgs(this.ProviderTypeName, context.SyncStage, tableDescription.TableName);
                            this.TryRaiseProgressEvent(beforeArgs, this.TableChangesSelecting);

                            // selected changes for the current table
                            TableChangesSelected tableSelectedChanges = new TableChangesSelected
                            {
                                TableName = tableDescription.TableName
                            };

                            // Get Command
                            DbCommand     selectIncrementalChangesCommand;
                            DbCommandType dbCommandType;

                            if (this.CanBeServerProvider && context.Parameters != null && context.Parameters.Count > 0 && filters != null && filters.Count > 0)
                            {
                                var filtersName = filters
                                                  .Where(f => f.TableName.Equals(tableDescription.TableName, StringComparison.InvariantCultureIgnoreCase))
                                                  .Select(f => f.ColumnName);

                                if (filtersName != null && filtersName.Count() > 0)
                                {
                                    dbCommandType = DbCommandType.SelectChangesWitFilters;
                                    selectIncrementalChangesCommand = syncAdapter.GetCommand(dbCommandType, filtersName);
                                }
                                else
                                {
                                    dbCommandType = DbCommandType.SelectChanges;
                                    selectIncrementalChangesCommand = syncAdapter.GetCommand(dbCommandType);
                                }
                            }
                            else
                            {
                                dbCommandType = DbCommandType.SelectChanges;
                                selectIncrementalChangesCommand = syncAdapter.GetCommand(dbCommandType);
                            }

                            if (selectIncrementalChangesCommand == null)
                            {
                                var exc = "Missing command 'SelectIncrementalChangesCommand' ";
                                throw new Exception(exc);
                            }

                            // Deriving Parameters
                            syncAdapter.SetCommandParameters(dbCommandType, selectIncrementalChangesCommand);

                            // Get a clone of the table with tracking columns
                            var dmTableChanges = BuildChangesTable(tableDescription.TableName, configTables);

                            SetSelectChangesCommonParameters(context, scopeInfo, selectIncrementalChangesCommand);

                            // Set filter parameters if any
                            if (this.CanBeServerProvider && context.Parameters != null && context.Parameters.Count > 0 && filters != null && filters.Count > 0)
                            {
                                var tableFilters = filters.Where(f => f.TableName.Equals(tableDescription.TableName, StringComparison.InvariantCultureIgnoreCase)).ToList();

                                if (tableFilters != null && tableFilters.Count > 0)
                                {
                                    foreach (var filter in tableFilters)
                                    {
                                        var parameter = context.Parameters.FirstOrDefault(p => p.ColumnName.Equals(filter.ColumnName, StringComparison.InvariantCultureIgnoreCase) && p.TableName.Equals(filter.TableName, StringComparison.InvariantCultureIgnoreCase));

                                        if (parameter != null)
                                        {
                                            DbManager.SetParameterValue(selectIncrementalChangesCommand, parameter.ColumnName, parameter.Value);
                                        }
                                    }
                                }
                            }

                            this.AddTrackingColumns <int>(dmTableChanges, "sync_row_is_tombstone");

                            // Get the reader
                            using (var dataReader = selectIncrementalChangesCommand.ExecuteReader())
                            {
                                while (dataReader.Read())
                                {
                                    DmRow dataRow = CreateRowFromReader(dataReader, dmTableChanges);

                                    //DmRow dataRow = dmTableChanges.NewRow();

                                    // assuming the row is not inserted / modified
                                    DmRowState state = DmRowState.Unchanged;

                                    // get if the current row is inserted, modified, deleted
                                    state = GetStateFromDmRow(dataRow, scopeInfo);

                                    if (state != DmRowState.Deleted && state != DmRowState.Modified && state != DmRowState.Added)
                                    {
                                        continue;
                                    }

                                    // add row
                                    dmTableChanges.Rows.Add(dataRow);

                                    // acceptchanges before modifying
                                    dataRow.AcceptChanges();
                                    tableSelectedChanges.TotalChanges++;

                                    // Set the correct state to be applied
                                    if (state == DmRowState.Deleted)
                                    {
                                        dataRow.Delete();
                                        tableSelectedChanges.Deletes++;
                                    }
                                    else if (state == DmRowState.Added)
                                    {
                                        dataRow.SetAdded();
                                        tableSelectedChanges.Inserts++;
                                    }
                                    else if (state == DmRowState.Modified)
                                    {
                                        dataRow.SetModified();
                                        tableSelectedChanges.Updates++;
                                    }
                                }

                                // Since we dont need this column anymore, remove it
                                this.RemoveTrackingColumns(dmTableChanges, "sync_row_is_tombstone");

                                // add it to the DmSet
                                changesSet.Tables.Add(dmTableChanges);
                            }

                            // add the stats to global stats
                            changes.TableChangesSelected.Add(tableSelectedChanges);

                            // Raise event for this table
                            context.SyncStage = SyncStage.TableChangesSelected;
                            var args = new TableChangesSelectedEventArgs(this.ProviderTypeName, SyncStage.TableChangesSelected, tableSelectedChanges);
                            this.TryRaiseProgressEvent(args, this.TableChangesSelected);
                        }


                        transaction.Commit();

                        // generate the batchpartinfo
                        batchInfo.GenerateBatchInfo(0, changesSet, batchDirectory);

                        // Create a new in-memory batch info with an the changes DmSet
                        return(batchInfo, changes);
                    }
                    catch (Exception)
                    {
                        throw;
                    }
                    finally
                    {
                        if (connection != null && connection.State == ConnectionState.Open)
                        {
                            connection.Close();
                        }
                    }
                }
            }
        }
Пример #11
0
        /// <summary>
        /// Construct a row from a dmTable, a rowState and the bitIndex
        /// </summary>
        private DmRow ConstructRow(DmTable dt, DmRowState rowState, int bitIndex)
        {
            DmRow dmRow = dt.NewRow();
            int   count = dt.Columns.Count;

            dmRow.BeginEdit();

            for (int i = 0; i < count; i++)
            {
                object dmRowObject = this.Records[i][bitIndex];

                // Sometimes, a serializer could potentially serialize type into string
                // For example JSON.Net will serialize GUID into STRING
                // So we try to deserialize in correct type
                if (dmRowObject != null)
                {
                    var columnType      = dt.Columns[i].DataType;
                    var dmRowObjectType = dmRowObject.GetType();

                    if (dmRowObjectType != columnType && columnType != typeof(object))
                    {
                        if (columnType == typeof(Guid) && (dmRowObject as string) != null)
                        {
                            dmRowObject = new Guid(dmRowObject.ToString());
                        }
                        if (columnType == typeof(Guid) && (dmRowObject.GetType() == typeof(byte[])))
                        {
                            dmRowObject = new Guid((byte[])dmRowObject);
                        }
                        else if (columnType == typeof(Int32) && dmRowObjectType != typeof(Int32))
                        {
                            dmRowObject = Convert.ToInt32(dmRowObject);
                        }
                        else if (columnType == typeof(UInt32) && dmRowObjectType != typeof(UInt32))
                        {
                            dmRowObject = Convert.ToUInt32(dmRowObject);
                        }
                        else if (columnType == typeof(Int16) && dmRowObjectType != typeof(Int16))
                        {
                            dmRowObject = Convert.ToInt16(dmRowObject);
                        }
                        else if (columnType == typeof(UInt16) && dmRowObjectType != typeof(UInt16))
                        {
                            dmRowObject = Convert.ToUInt16(dmRowObject);
                        }
                        else if (columnType == typeof(Int64) && dmRowObjectType != typeof(Int64))
                        {
                            dmRowObject = Convert.ToInt64(dmRowObject);
                        }
                        else if (columnType == typeof(UInt64) && dmRowObjectType != typeof(UInt64))
                        {
                            dmRowObject = Convert.ToUInt64(dmRowObject);
                        }
                        else if (columnType == typeof(Byte) && dmRowObjectType != typeof(Byte))
                        {
                            dmRowObject = Convert.ToByte(dmRowObject);
                        }
                        else if (columnType == typeof(Char) && dmRowObjectType != typeof(Char))
                        {
                            dmRowObject = Convert.ToChar(dmRowObject);
                        }
                        else if (columnType == typeof(DateTime) && dmRowObjectType != typeof(DateTime))
                        {
                            dmRowObject = Convert.ToDateTime(dmRowObject);
                        }
                        else if (columnType == typeof(Decimal) && dmRowObjectType != typeof(Decimal))
                        {
                            dmRowObject = Convert.ToDecimal(dmRowObject);
                        }
                        else if (columnType == typeof(Double) && dmRowObjectType != typeof(Double))
                        {
                            dmRowObject = Convert.ToDouble(dmRowObject);
                        }
                        else if (columnType == typeof(SByte) && dmRowObjectType != typeof(SByte))
                        {
                            dmRowObject = Convert.ToSByte(dmRowObject);
                        }
                        else if (columnType == typeof(Single) && dmRowObjectType != typeof(Single))
                        {
                            dmRowObject = Convert.ToSingle(dmRowObject);
                        }
                        else if (columnType == typeof(String) && dmRowObjectType != typeof(String))
                        {
                            dmRowObject = Convert.ToString(dmRowObject);
                        }
                        else if (columnType == typeof(Boolean) && dmRowObjectType != typeof(Boolean))
                        {
                            dmRowObject = Convert.ToBoolean(dmRowObject);
                        }
                        else if (columnType == typeof(Byte[]) && dmRowObjectType != typeof(Byte[]) && dmRowObjectType == typeof(String))
                        {
                            dmRowObject = Convert.FromBase64String(dmRowObject.ToString());
                        }
                        else if (dmRowObjectType != columnType)
                        {
                            var t         = dmRowObject.GetType();
                            var converter = columnType.GetConverter();
                            if (converter != null && converter.CanConvertFrom(t))
                            {
                                dmRowObject = converter.ConvertFrom(dmRowObject);
                            }
                        }
                    }
                }

                if (rowState == DmRowState.Deleted)
                {
                    // Since some columns might be not null (and we have null because the row is deleted)
                    if (this.Records[i][bitIndex] != null)
                    {
                        dmRow[i] = dmRowObject;
                    }
                }
                else
                {
                    dmRow[i] = dmRowObject;
                }
            }

            dt.Rows.Add(dmRow);

            switch (rowState)
            {
            case DmRowState.Unchanged:
            {
                dmRow.AcceptChanges();
                dmRow.EndEdit();
                return(dmRow);
            }

            case DmRowState.Added:
            {
                dmRow.EndEdit();
                return(dmRow);
            }

            case DmRowState.Deleted:
            {
                dmRow.AcceptChanges();
                dmRow.Delete();
                dmRow.EndEdit();
                return(dmRow);
            }

            case DmRowState.Modified:
            {
                dmRow.AcceptChanges();
                dmRow.SetModified();
                dmRow.EndEdit();
                return(dmRow);
            }

            default:
                throw new ArgumentException("InvalidRowState");
            }
        }
Пример #12
0
        private DmRow ConvertToDmRow(DmTable dt, int bitIndex)
        {
            DmRowState rowState = (DmRowState)this.RowStates[bitIndex];

            return(this.ConstructRow(dt, rowState, bitIndex));
        }
Пример #13
0
        /// <summary>
        /// Apply changes internal method for one Insert or Update or Delete for every dbSyncAdapter
        /// </summary>
        internal ChangeApplicationAction ApplyChangesInternal(
            SyncContext context,
            MessageApplyChanges message,
            DbConnection connection,
            DbTransaction transaction,
            DmRowState applyType,
            ChangesApplied changesApplied)
        {
            ChangeApplicationAction changeApplicationAction = ChangeApplicationAction.Continue;

            // for each adapters (Zero to End for Insert / Updates -- End to Zero for Deletes
            for (int i = 0; i < message.Schema.Tables.Count; i++)
            {
                // If we have a delete we must go from Up to Down, orthewise Dow to Up index
                var tableDescription = (applyType != DmRowState.Deleted ?
                                        message.Schema.Tables[i] :
                                        message.Schema.Tables[message.Schema.Tables.Count - i - 1]);

                // if we are in upload stage, so check if table is not download only
                if (context.SyncWay == SyncWay.Upload && tableDescription.SyncDirection == SyncDirection.DownloadOnly)
                {
                    continue;
                }

                // if we are in download stage, so check if table is not download only
                if (context.SyncWay == SyncWay.Download && tableDescription.SyncDirection == SyncDirection.UploadOnly)
                {
                    continue;
                }


                var builder     = this.GetDatabaseBuilder(tableDescription);
                var syncAdapter = builder.CreateSyncAdapter(connection, transaction);

                syncAdapter.ConflictApplyAction = SyncConfiguration.GetApplyAction(message.Policy);

                // Set syncAdapter properties
                syncAdapter.applyType = applyType;

                // Get conflict handler resolver
                if (syncAdapter.ConflictActionInvoker == null && this.ApplyChangedFailed != null)
                {
                    syncAdapter.ConflictActionInvoker = GetConflictAction;
                }

                if (message.Changes.BatchPartsInfo != null && message.Changes.BatchPartsInfo.Count > 0)
                {
                    // getting the table to be applied
                    // we may have multiple batch files, so we can have multipe dmTable with the same Name
                    // We can say that dmTable may be contained in several files
                    foreach (DmTable dmTablePart in message.Changes.GetTable(tableDescription.TableName))
                    {
                        if (dmTablePart == null || dmTablePart.Rows.Count == 0)
                        {
                            continue;
                        }

                        // check and filter
                        var dmChangesView = new DmView(dmTablePart, (r) => r.RowState == applyType);

                        if (dmChangesView.Count == 0)
                        {
                            dmChangesView.Dispose();
                            dmChangesView = null;
                            continue;
                        }

                        // Conflicts occured when trying to apply rows
                        List <SyncConflict> conflicts = new List <SyncConflict>();

                        // Raise event progress only if there are rows to be applied
                        context.SyncStage = SyncStage.TableChangesApplying;
                        var args = new TableChangesApplyingEventArgs(this.ProviderTypeName, context.SyncStage, tableDescription.TableName, applyType);
                        this.TryRaiseProgressEvent(args, this.TableChangesApplying);

                        int rowsApplied;
                        // applying the bulkchanges command
                        if (message.UseBulkOperations && this.SupportBulkOperations)
                        {
                            rowsApplied = syncAdapter.ApplyBulkChanges(dmChangesView, message.FromScope, conflicts);
                        }
                        else
                        {
                            rowsApplied = syncAdapter.ApplyChanges(dmChangesView, message.FromScope, conflicts);
                        }

                        // If conflicts occured
                        // Eventuall, conflicts are resolved on server side.
                        if (conflicts != null && conflicts.Count > 0)
                        {
                            foreach (var conflict in conflicts)
                            {
                                //var scopeBuilder = this.GetScopeBuilder();
                                //var scopeInfoBuilder = scopeBuilder.CreateScopeInfoBuilder(message.ScopeInfoTableName, connection, transaction);
                                //var localTimeStamp = scopeInfoBuilder.GetLocalTimestamp();
                                var fromScopeLocalTimeStamp = message.FromScope.Timestamp;

                                changeApplicationAction = syncAdapter.HandleConflict(conflict, message.Policy, message.FromScope, fromScopeLocalTimeStamp, out DmRow resolvedRow);

                                if (changeApplicationAction == ChangeApplicationAction.Continue)
                                {
                                    // row resolved
                                    if (resolvedRow != null)
                                    {
                                        rowsApplied++;
                                    }
                                }
                                else
                                {
                                    context.TotalSyncErrors++;
                                    // TODO : Should we break at the first error ?
                                    return(ChangeApplicationAction.Rollback);
                                }
                            }
                        }

                        // Get all conflicts resolved
                        context.TotalSyncConflicts = conflicts.Where(c => c.Type != ConflictType.ErrorsOccurred).Sum(c => 1);

                        // Handle sync progress for this syncadapter (so this table)
                        var changedFailed = dmChangesView.Count - rowsApplied;

                        // raise SyncProgress Event

                        var existAppliedChanges = changesApplied.TableChangesApplied.FirstOrDefault(
                            sc => string.Equals(sc.TableName, tableDescription.TableName) && sc.State == applyType);

                        if (existAppliedChanges == null)
                        {
                            existAppliedChanges = new TableChangesApplied
                            {
                                TableName = tableDescription.TableName,
                                Applied   = rowsApplied,
                                Failed    = changedFailed,
                                State     = applyType
                            };
                            changesApplied.TableChangesApplied.Add(existAppliedChanges);
                        }
                        else
                        {
                            existAppliedChanges.Applied += rowsApplied;
                            existAppliedChanges.Failed  += changedFailed;
                        }

                        // Event progress
                        context.SyncStage = SyncStage.TableChangesApplied;
                        var progressEventArgs = new TableChangesAppliedEventArgs(this.ProviderTypeName, context.SyncStage, existAppliedChanges);
                        this.TryRaiseProgressEvent(progressEventArgs, this.TableChangesApplied);
                    }
                }

                // Dispose conflict handler resolver
                if (syncAdapter.ConflictActionInvoker != null)
                {
                    syncAdapter.ConflictActionInvoker = null;
                }
            }

            return(ChangeApplicationAction.Continue);
        }
Пример #14
0
        /// <summary>
        /// Construct a row from a dmTable, a rowState and the bitIndex
        /// </summary>
        private DmRow ConstructRow(DmTable dt, DmRowState rowState, int bitIndex)
        {
            DmRow dmRow = dt.NewRow();
            int   count = dt.Columns.Count;

            dmRow.BeginEdit();

            for (int i = 0; i < count; i++)
            {
                object dmRowObject = this.Records[i][bitIndex];

                // Sometimes, a serializer could potentially serialize type into string
                // For example JSON.Net will serialize GUID into STRING
                // So we try to deserialize in correct type
                if (dmRowObject != null)
                {
                    var columnType = dt.Columns[i].DataType;

                    if (columnType == typeof(Guid) && (dmRowObject as string) != null)
                    {
                        dmRowObject = new Guid(dmRowObject.ToString());
                    }
                    else if (columnType == typeof(Int32) && dmRowObject.GetType() != typeof(Int32))
                    {
                        dmRowObject = Convert.ToInt32(dmRowObject);
                    }
                    else if (columnType == typeof(Int16) && dmRowObject.GetType() != typeof(Int16))
                    {
                        dmRowObject = Convert.ToInt16(dmRowObject);
                    }
                    else if (dmRowObject.GetType() != columnType)
                    {
                        Debug.WriteLine($"Can't convert serialized value {dmRowObject.ToString()} to {columnType}");

                        var t         = dmRowObject.GetType();
                        var converter = columnType.GetConverter();
                        if (converter.CanConvertFrom(t))
                        {
                            dmRowObject = converter.ConvertFrom(dmRowObject);
                        }
                        else
                        {
                            dmRowObject = Convert.ChangeType(dmRowObject, columnType, CultureInfo.InvariantCulture);
                        }
                    }
                }

                if (rowState == DmRowState.Deleted)
                {
                    // Since some columns might be not null (and we have null because the row is deleted)
                    if (this.Records[i][bitIndex] != null)
                    {
                        dmRow[i] = dmRowObject;
                    }
                }
                else
                {
                    dmRow[i] = dmRowObject;
                }
            }

            //if (rowState == DmRowState.Deleted)
            //{
            //    // we are in a deleted state, so we have only primary keys available
            //    for (int i = 0; i < count; i++)
            //    {
            //        // since some columns might be not null (and we have null because the row is deleted)
            //        if (this.Records[i][bitIndex] != null)
            //            dmRow[i] = this.Records[i][bitIndex];
            //    }

            //}
            //else
            //{
            //    for (int i = 0; i < count; i++)
            //        dmRow[i] = this.Records[i][bitIndex];

            //}

            dt.Rows.Add(dmRow);

            switch (rowState)
            {
            case DmRowState.Unchanged:
            {
                dmRow.AcceptChanges();
                dmRow.EndEdit();
                return(dmRow);
            }

            case DmRowState.Added:
            {
                dmRow.EndEdit();
                return(dmRow);
            }

            case DmRowState.Deleted:
            {
                dmRow.AcceptChanges();
                dmRow.Delete();
                dmRow.EndEdit();
                return(dmRow);
            }

            case DmRowState.Modified:
            {
                dmRow.AcceptChanges();
                dmRow.SetModified();
                dmRow.EndEdit();
                return(dmRow);
            }

            default:
                throw new ArgumentException("InvalidRowState");
            }
        }
Пример #15
0
 public TableChangesApplyingEventArgs(string providerTypeName, SyncStage stage, string tableName, DmRowState state, DbConnection connection, DbTransaction transaction) : base(providerTypeName, stage, connection, transaction)
 {
     this.TableName = tableName;
     this.State     = state;
 }
Пример #16
0
        internal void MergeRow(DmRow row, DmRow targetRow, bool preserveChanges)
        {
            // Si le merge ne concerne pas une ligne déjà existante
            if (targetRow == null)
            {
                ImportRow(row);
                return;
            }

            int proposedRecord = targetRow.tempRecord; // by saving off the tempRecord, EndEdit won't free newRecord

            targetRow.tempRecord = -1;
            try
            {
                DmRowState saveRowState  = targetRow.RowState;
                int        saveIdxRecord = (saveRowState == DmRowState.Added) ? targetRow.newRecord : saveIdxRecord = targetRow.oldRecord;
                int        newRecord;
                int        oldRecord;
                if (targetRow.RowState == DmRowState.Unchanged && row.RowState == DmRowState.Unchanged)
                {
                    // unchanged row merging with unchanged row
                    oldRecord = targetRow.oldRecord;

                    if (preserveChanges)
                    {
                        newRecord = this.Rows.GetNewVersionId();
                        CopyRecords(this, oldRecord, newRecord);
                    }
                    else
                    {
                        newRecord = targetRow.newRecord;
                    }


                    CopyRecords(row.Table, row.oldRecord, targetRow.oldRecord);
                }
                else if (row.newRecord == -1)
                {
                    // Incoming row is deleted
                    oldRecord = targetRow.oldRecord;
                    if (preserveChanges)
                    {
                        if (targetRow.RowState == DmRowState.Unchanged)
                        {
                            newRecord = this.Rows.GetNewVersionId();
                            CopyRecords(this, oldRecord, newRecord);
                        }
                        else
                        {
                            newRecord = targetRow.newRecord;
                        }
                    }
                    else
                    {
                        newRecord = -1;
                    }

                    CopyRecords(row.Table, row.oldRecord, oldRecord);
                }
                else
                {
                    // incoming row is added, modified or unchanged (targetRow is not unchanged)
                    oldRecord = targetRow.oldRecord;
                    newRecord = targetRow.newRecord;
                    if (targetRow.RowState == DmRowState.Unchanged)
                    {
                        newRecord = this.Rows.GetNewVersionId();
                        CopyRecords(this, oldRecord, newRecord);
                    }
                    CopyRecords(row.Table, row.oldRecord, oldRecord);

                    if (!preserveChanges)
                    {
                        CopyRecords(row.Table, row.newRecord, newRecord);
                    }
                }
            }
            finally
            {
                targetRow.tempRecord = proposedRecord;
            }
        }
Пример #17
0
        /// <summary>
        /// Apply changes internal method for one Insert or Update or Delete for every dbSyncAdapter
        /// </summary>
        internal async Task <ChangeApplicationAction> ApplyChangesInternalAsync(
            DmTable table,
            SyncContext context,
            MessageApplyChanges message,
            DbConnection connection,
            DbTransaction transaction,
            DmRowState applyType,
            DatabaseChangesApplied changesApplied)
        {
            var changeApplicationAction = ChangeApplicationAction.Continue;

            // if we are in upload stage, so check if table is not download only
            if (context.SyncWay == SyncWay.Upload && table.SyncDirection == SyncDirection.DownloadOnly)
            {
                return(ChangeApplicationAction.Continue);
            }

            // if we are in download stage, so check if table is not download only
            if (context.SyncWay == SyncWay.Download && table.SyncDirection == SyncDirection.UploadOnly)
            {
                return(ChangeApplicationAction.Continue);
            }

            var builder = this.GetDatabaseBuilder(table);

            var syncAdapter = builder.CreateSyncAdapter(connection, transaction);

            syncAdapter.ApplyType = applyType;

            if (message.Changes.BatchPartsInfo != null && message.Changes.BatchPartsInfo.Count > 0)
            {
                // getting the table to be applied
                // we may have multiple batch files, so we can have multipe dmTable with the same Name
                // We can say that dmTable may be contained in several files
                foreach (var dmTablePart in message.Changes.GetTable(table.TableName))
                {
                    if (dmTablePart == null || dmTablePart.Rows.Count == 0)
                    {
                        continue;
                    }

                    // check and filter
                    var dmChangesView = new DmView(dmTablePart, (r) => r.RowState == applyType);

                    if (dmChangesView.Count == 0)
                    {
                        dmChangesView.Dispose();
                        dmChangesView = null;
                        continue;
                    }

                    // Conflicts occured when trying to apply rows
                    var conflicts = new List <SyncConflict>();

                    context.SyncStage = SyncStage.TableChangesApplying;
                    // Launch any interceptor if available
                    await this.InterceptAsync(new TableChangesApplyingArgs(context, table, applyType, connection, transaction));

                    int rowsApplied;
                    // applying the bulkchanges command
                    if (this.Options.UseBulkOperations && this.SupportBulkOperations)
                    {
                        rowsApplied = syncAdapter.ApplyBulkChanges(dmChangesView, message.FromScope, conflicts);
                    }
                    else
                    {
                        rowsApplied = syncAdapter.ApplyChanges(dmChangesView, message.FromScope, conflicts);
                    }

                    // If conflicts occured
                    // Eventuall, conflicts are resolved on server side.
                    if (conflicts != null && conflicts.Count > 0)
                    {
                        foreach (var conflict in conflicts)
                        {
                            //var scopeBuilder = this.GetScopeBuilder();
                            //var scopeInfoBuilder = scopeBuilder.CreateScopeInfoBuilder(message.ScopeInfoTableName, connection, transaction);
                            //var localTimeStamp = scopeInfoBuilder.GetLocalTimestamp();
                            var fromScopeLocalTimeStamp = message.FromScope.Timestamp;

                            var   conflictCount = 0;
                            DmRow resolvedRow   = null;
                            (changeApplicationAction, conflictCount, resolvedRow) =
                                await this.HandleConflictAsync(syncAdapter, context, conflict, message.Policy, message.FromScope, fromScopeLocalTimeStamp, connection, transaction);

                            if (changeApplicationAction == ChangeApplicationAction.Continue)
                            {
                                // row resolved
                                if (resolvedRow != null)
                                {
                                    context.TotalSyncConflicts += conflictCount;
                                    rowsApplied++;
                                }
                            }
                            else
                            {
                                context.TotalSyncErrors++;
                                // TODO : Should we break at the first error ?
                                return(ChangeApplicationAction.Rollback);
                            }
                        }
                    }

                    // Handle sync progress for this syncadapter (so this table)
                    var changedFailed = dmChangesView.Count - rowsApplied;

                    // raise SyncProgress Event
                    var existAppliedChanges = changesApplied.TableChangesApplied.FirstOrDefault(
                        sc => string.Equals(sc.Table.TableName, table.TableName) && sc.State == applyType);

                    if (existAppliedChanges == null)
                    {
                        existAppliedChanges = new TableChangesApplied
                        {
                            Table   = new DmTableSurrogate(table),
                            Applied = rowsApplied,
                            Failed  = changedFailed,
                            State   = applyType
                        };
                        changesApplied.TableChangesApplied.Add(existAppliedChanges);
                    }
                    else
                    {
                        existAppliedChanges.Applied += rowsApplied;
                        existAppliedChanges.Failed  += changedFailed;
                    }

                    // Progress & Interceptor
                    context.SyncStage = SyncStage.TableChangesApplied;
                    var tableChangesAppliedArgs = new TableChangesAppliedArgs(context, existAppliedChanges, connection, transaction);
                    this.ReportProgress(context, tableChangesAppliedArgs, connection, transaction);
                    await this.InterceptAsync(tableChangesAppliedArgs);
                }
            }

            return(ChangeApplicationAction.Continue);
        }
 public TableChangesApplyingEventArgs(string providerTypeName, SyncStage stage, string tableName, DmRowState state) : base(providerTypeName, stage)
 {
     this.TableName = tableName;
     this.State     = state;
 }