Exemplo n.º 1
0
        /// <summary>
        /// Create a new BPI, and serialize the changeset if not in memory
        /// </summary>
        internal static BatchPartInfo CreateBatchPartInfo(int batchIndex, SyncSet set, string fileName, string directoryFullPath, bool isLastBatch)
        {
            BatchPartInfo bpi = null;

            // Create a batch part
            // The batch part creation process will serialize the changesSet to the disk

            // Serialize the file !
            Serialize(set.GetContainerSet(), fileName, directoryFullPath);

            bpi = new BatchPartInfo {
                FileName = fileName
            };

            bpi.Index       = batchIndex;
            bpi.IsLastBatch = isLastBatch;

            // Even if the set is empty (serialized on disk), we should retain the tables names
            if (set != null)
            {
                bpi.Tables = set.Tables.Select(t => new BatchPartTableInfo(t.TableName, t.SchemaName)).ToArray();
            }

            return(bpi);
        }
Exemplo n.º 2
0
    private void redo(List <Sync <T> > syncs)
    {
        List <Change <T> > changes = new List <Change <T> >();

        {
            for (int syncCount = 0; syncCount < syncs.Count; syncCount++)
            {
                Sync <T> sync = syncs[syncCount];
                switch (sync.getType())
                {
                case Sync <T> .Type.Set:
                {
                    SyncSet <T> syncSet = (SyncSet <T>)sync;
                    // Make change
                    ChangeSet <T> changeSet = new ChangeSet <T>();
                    {
                        changeSet.index = syncSet.index;
                        changeSet.values.AddRange(syncSet.news);
                    }
                    changes.Add(changeSet);
                }
                break;

                case Sync <T> .Type.Add:
                {
                    SyncAdd <T> syncAdd = (SyncAdd <T>)sync;
                    // Make change
                    ChangeAdd <T> changeAdd = new ChangeAdd <T>();
                    {
                        changeAdd.index = syncAdd.index;
                        changeAdd.values.AddRange(syncAdd.values);
                    }
                    changes.Add(changeAdd);
                }
                break;

                case Sync <T> .Type.Remove:
                {
                    SyncRemove <T> syncRemove = (SyncRemove <T>)sync;
                    // Make change
                    ChangeRemove <T> changeRemove = new ChangeRemove <T>();
                    {
                        changeRemove.index  = syncRemove.index;
                        changeRemove.number = syncRemove.values.Count;
                    }
                    changes.Add(changeRemove);
                }
                break;

                default:
                    Logger.LogError("unknown type: " + sync.getType() + "; " + this);
                    break;
                }
            }
        }
        if (changes.Count > 0)
        {
            this.processChange(changes);
        }
    }
Exemplo n.º 3
0
        public async Task BaseOrchestrator_Provision_ShouldCreate_TrackingTable()
        {
            var dbName = HelperDatabase.GetRandomName("tcp_lo_");
            await HelperDatabase.CreateDatabaseAsync(ProviderType.Sql, dbName, true);

            var cs          = HelperDatabase.GetConnectionString(ProviderType.Sql, dbName);
            var sqlProvider = new SqlSyncProvider(cs);

            var scopeName = "scope";

            var options = new SyncOptions();
            var setup   = new SyncSetup {
                TrackingTablesSuffix = "sync", TrackingTablesPrefix = "trck"
            };

            var schema  = new SyncSet();
            var table   = new SyncTable("Product", "SalesLT");
            var colID   = new SyncColumn("ID", typeof(Guid));
            var colName = new SyncColumn("Name", typeof(string));

            table.Columns.Add(colID);
            table.Columns.Add(colName);
            table.Columns.Add("Number", typeof(int));
            table.PrimaryKeys.Add("ID");

            //schema.TrackingTablesSuffix = "sync";
            //schema.TrackingTablesPrefix = "trck";

            schema.Tables.Add(table);

            // trackign table name is composed with prefix and suffix from setup
            var trackingTableName = $"{setup.TrackingTablesPrefix}{table.TableName}{setup.TrackingTablesSuffix}";

            var localOrchestrator = new LocalOrchestrator(sqlProvider, options, setup, scopeName);

            var provision = SyncProvision.TrackingTable;

            await localOrchestrator.ProvisionAsync(schema, provision);

            using (var c = new SqlConnection(cs))
            {
                await c.OpenAsync().ConfigureAwait(false);

                var tbl = await SqlManagementUtils.GetTableAsync(c, null, trackingTableName, "SalesLT");

                var tblName = tbl.Rows[0]["TableName"].ToString();
                var schName = tbl.Rows[0]["SchemaName"].ToString();

                Assert.Equal(trackingTableName, tblName);
                Assert.Equal(table.SchemaName, schName);

                c.Close();
            }

            HelperDatabase.DropDatabase(ProviderType.Sql, dbName);
        }
Exemplo n.º 4
0
    public static void getAddAndRemoveValues <T>(List <Sync <T> > syncs, List <T> addValues, List <T> removeValues)
    {
        for (int syncCount = 0; syncCount < syncs.Count; syncCount++)
        {
            Sync <T> sync = syncs[syncCount];
            switch (sync.getType())
            {
            case Sync <T> .Type.Set:
            {
                SyncSet <T> syncSet = (SyncSet <T>)sync;
                if (syncSet.olds.Count == syncSet.news.Count)
                {
                    for (int i = 0; i < syncSet.olds.Count; i++)
                    {
                        // od value
                        removeValues.Add(syncSet.olds[i]);
                        // add new value
                        addValues.Add(syncSet.news[i]);
                    }
                }
                else
                {
                    Logger.LogError("count error: " + syncSet.olds.Count + "; " + syncSet.news.Count);
                }
            }
            break;

            case Sync <T> .Type.Add:
            {
                SyncAdd <T> syncAdd = (SyncAdd <T>)sync;
                for (int i = 0; i < syncAdd.values.Count; i++)
                {
                    T value = syncAdd.values[i];
                    addValues.Add(value);
                }
            }
            break;

            case Sync <T> .Type.Remove:
            {
                SyncRemove <T> syncRemove = (SyncRemove <T>)sync;
                for (int i = 0; i < syncRemove.values.Count; i++)
                {
                    T value = syncRemove.values[i];
                    removeValues.Add(value);
                }
            }
            break;

            default:
                Logger.LogError("unknown type: " + sync.getType());
                break;
            }
        }
    }
Exemplo n.º 5
0
    public static void replaceCallBack <T>(ValueChangeCallBack callBack, List <Sync <T> > syncs)
    {
        for (int syncCount = 0; syncCount < syncs.Count; syncCount++)
        {
            Sync <T> sync = syncs[syncCount];
            switch (sync.getType())
            {
            case Sync <T> .Type.Set:
            {
                SyncSet <T> syncSet = (SyncSet <T>)sync;
                if (syncSet.olds.Count == syncSet.news.Count)
                {
                    for (int i = 0; i < syncSet.olds.Count; i++)
                    {
                        // od value
                        RemoveCallBack(callBack, syncSet.olds[i]);
                        // add new value
                        AddCallBack(callBack, syncSet.news[i]);
                    }
                }
                else
                {
                    Logger.LogError("count error: " + syncSet.olds.Count + "; " + syncSet.news.Count);
                }
            }
            break;

            case Sync <T> .Type.Add:
            {
                SyncAdd <T> syncAdd = (SyncAdd <T>)sync;
                for (int i = 0; i < syncAdd.values.Count; i++)
                {
                    T value = syncAdd.values[i];
                    AddCallBack(callBack, value);
                }
            }
            break;

            case Sync <T> .Type.Remove:
            {
                SyncRemove <T> syncRemove = (SyncRemove <T>)sync;
                for (int i = 0; i < syncRemove.values.Count; i++)
                {
                    T value = syncRemove.values[i];
                    RemoveCallBack(callBack, value);
                }
            }
            break;

            default:
                Logger.LogError("unknown type: " + sync.getType());
                break;
            }
        }
    }
Exemplo n.º 6
0
        /// <summary>
        /// Create a new BatchInfo, containing all BatchPartInfo
        /// </summary>
        public BatchInfo(SyncSet inSchema, string rootDirectory = null, string directoryName = null)
        {
            // We need to create a change table set, containing table with columns not readonly
            foreach (var table in inSchema.Tables)
            {
                DbSyncAdapter.CreateChangesTable(inSchema.Tables[table.TableName, table.SchemaName], this.SanitizedSchema);
            }

            this.DirectoryRoot  = rootDirectory;
            this.BatchPartsInfo = new List <BatchPartInfo>();
            this.DirectoryName  = string.IsNullOrEmpty(directoryName) ? string.Concat(DateTime.UtcNow.ToString("yyyy_MM_dd_ss"), Path.GetRandomFileName().Replace(".", "")) : directoryName;
        }
Exemplo n.º 7
0
        public async Task BaseOrchestrator_Provision_SchemaCreated_If_SchemaHasColumnsDefinition()
        {
            var dbName = HelperDatabase.GetRandomName("tcp_lo_");
            await HelperDatabase.CreateDatabaseAsync(ProviderType.Sql, dbName, true);

            var cs          = HelperDatabase.GetConnectionString(ProviderType.Sql, dbName);
            var sqlProvider = new SqlSyncProvider(cs);

            var scopeName = "scope";

            var options = new SyncOptions();
            var setup   = new SyncSetup();

            var schema  = new SyncSet();
            var table   = new SyncTable("Product", "SalesLT");
            var colID   = new SyncColumn("ID", typeof(Guid));
            var colName = new SyncColumn("Name", typeof(string));

            table.Columns.Add(colID);
            table.Columns.Add(colName);
            table.Columns.Add("Number", typeof(int));
            table.PrimaryKeys.Add("ID");

            schema.Tables.Add(table);

            var localOrchestrator = new LocalOrchestrator(sqlProvider, options, setup, scopeName);

            var provision = SyncProvision.Table | SyncProvision.TrackingTable | SyncProvision.StoredProcedures | SyncProvision.Triggers;

            await localOrchestrator.ProvisionAsync(schema, provision);

            using (var c = new SqlConnection(cs))
            {
                await c.OpenAsync().ConfigureAwait(false);

                var tbl = await SqlManagementUtils.GetTableAsync(c, null, "Product", "SalesLT");

                var tblName = tbl.Rows[0]["TableName"].ToString();
                var schName = tbl.Rows[0]["SchemaName"].ToString();

                Assert.Equal(table.TableName, tblName);
                Assert.Equal(table.SchemaName, schName);

                var cols = await SqlManagementUtils.GetColumnsForTableAsync(c, null, "Product", "SalesLT");

                Assert.Equal(3, cols.Rows.Count);

                c.Close();
            }

            HelperDatabase.DropDatabase(ProviderType.Sql, dbName);
        }
 /// <summary>
 /// After deserializing all rows, call the converter for each row
 /// </summary>
 public void AfterDeserializedRows(SyncSet data, IConverter converter)
 {
     foreach (var table in data.Tables)
     {
         if (table.Rows.Count > 0)
         {
             foreach (var row in table.Rows)
             {
                 converter.AfterDeserialized(row);
             }
         }
     }
 }
Exemplo n.º 9
0
        /// <summary>
        /// Create a new BatchInfo, containing all BatchPartInfo
        /// </summary>
        public BatchInfo(bool isInMemory, SyncSet inSchema, string rootDirectory = null, string directoryName = null)
        {
            this.InMemory = isInMemory;
            this.schema   = inSchema.Clone();

            // If not in memory, generate a directory name and initialize batch parts list
            if (!this.InMemory)
            {
                this.DirectoryRoot  = rootDirectory;
                this.BatchPartsInfo = new List <BatchPartInfo>();
                this.DirectoryName  = string.IsNullOrEmpty(directoryName) ? string.Concat(DateTime.UtcNow.ToString("yyyy_MM_dd_ss"), Path.GetRandomFileName().Replace(".", "")) : directoryName;
            }
        }
Exemplo n.º 10
0
 /// <summary>
 /// Before serializing all rows, call the converter for each row
 /// </summary>
 public void BeforeSerializeRows(SyncSet data, IConverter converter)
 {
     foreach (var table in data.Tables)
     {
         if (table.Rows.Count > 0)
         {
             foreach (var row in table.Rows)
             {
                 converter.BeforeSerialize(row);
             }
         }
     }
 }
Exemplo n.º 11
0
        /// <summary>
        /// Add changes to batch info.
        /// </summary>
        public void AddChanges(SyncSet changes, int batchIndex = 0, bool isLastBatch = true)
        {
            if (this.InMemory)
            {
                this.InMemoryData = changes;
            }
            else
            {
                var bpId = this.GenerateNewFileName(batchIndex.ToString());
                //var fileName = Path.Combine(this.GetDirectoryFullPath(), bpId);
                var bpi = BatchPartInfo.CreateBatchPartInfo(batchIndex, changes, bpId, GetDirectoryFullPath(), isLastBatch);

                // add the batchpartinfo tp the current batchinfo
                this.BatchPartsInfo.Add(bpi);
            }
        }
Exemplo n.º 12
0
        /// <summary>
        /// Add changes to batch info.
        /// </summary>
        public async Task AddChangesAsync(SyncSet changes, int batchIndex = 0, bool isLastBatch = true, BaseOrchestrator orchestrator = null)
        {
            if (this.InMemory)
            {
                this.InMemoryData = changes;
            }
            else
            {
                var bpId = this.GenerateNewFileName(batchIndex.ToString());
                //var fileName = Path.Combine(this.GetDirectoryFullPath(), bpId);
                var bpi = await BatchPartInfo.CreateBatchPartInfoAsync(batchIndex, changes, bpId, GetDirectoryFullPath(), isLastBatch, orchestrator).ConfigureAwait(false);

                // add the batchpartinfo tp the current batchinfo
                this.BatchPartsInfo.Add(bpi);
            }
        }
Exemplo n.º 13
0
        /// <summary>
        /// Add changes to batch info.
        /// </summary>
        public async Task AddChangesAsync(SyncSet changes, int batchIndex = 0, bool isLastBatch = true, ISerializerFactory serializerFactory = default, BaseOrchestrator orchestrator = null)
        {
            if (this.InMemory)
            {
                this.SerializerFactoryKey = null;
                this.InMemoryData         = changes;
            }
            else
            {
                this.SerializerFactoryKey = serializerFactory.Key;
                var bpId = GenerateNewFileName(batchIndex.ToString());
                var bpi  = await BatchPartInfo.CreateBatchPartInfoAsync(batchIndex, changes, bpId, GetDirectoryFullPath(), isLastBatch, serializerFactory, orchestrator).ConfigureAwait(false);

                // add the batchpartinfo tp the current batchinfo
                this.BatchPartsInfo.Add(bpi);
            }
        }
        public async Task RemoteOrchestrator_Provision_SchemaFail_If_SchemaHasColumnsDefinitionButNoPrimaryKey()
        {
            var dbName = HelperDatabase.GetRandomName("tcp_ro_");
            await HelperDatabase.CreateDatabaseAsync(ProviderType.Sql, dbName, true);

            var cs          = HelperDatabase.GetConnectionString(ProviderType.Sql, dbName);
            var sqlProvider = new SqlSyncProvider(cs);
            var ctx         = new AdventureWorksContext((dbName, ProviderType.Sql, sqlProvider), true, false);
            await ctx.Database.EnsureCreatedAsync();

            var scopeName = "scope";

            var options = new SyncOptions();
            var setup   = new SyncSetup(new string[] { "SalesLT.Product" });

            var schema  = new SyncSet();
            var table   = new SyncTable("Product", "SalesLT");
            var colID   = new SyncColumn("ID", typeof(Guid));
            var colName = new SyncColumn("Name", typeof(string));

            table.Columns.Add(colID);
            table.Columns.Add(colName);
            table.Columns.Add("Number", typeof(int));

            schema.Tables.Add(table);

            var remoteOrchestrator = new RemoteOrchestrator(sqlProvider, options);

            var scopeInfo = await remoteOrchestrator.GetServerScopeInfoAsync(scopeName, setup);

            // Overriding scope info to introduce a bad table with no primary key
            scopeInfo.Schema = schema;
            scopeInfo.Setup  = setup;

            var provision = SyncProvision.Table | SyncProvision.TrackingTable | SyncProvision.StoredProcedures | SyncProvision.Triggers;

            var se = await Assert.ThrowsAsync <SyncException>(
                async() => await remoteOrchestrator.ProvisionAsync(scopeInfo, provision));

            Assert.Equal(SyncStage.Provisioning, se.SyncStage);
            Assert.Equal(SyncSide.ServerSide, se.Side);
            Assert.Equal("MissingPrimaryKeyException", se.TypeName);

            HelperDatabase.DropDatabase(ProviderType.Sql, dbName);
        }
Exemplo n.º 15
0
        // TODO : Set the serializer to the one choosed by user

        /// <summary>
        /// Loads the batch file and import the rows in a SyncSet instance
        /// </summary>
        public void LoadBatch(SyncSet schema, string directoryFullPath)
        {
            if (string.IsNullOrEmpty(this.FileName))
            {
                return;
            }

            // Clone the schema to get a unique instance
            var set = schema.Clone();

            // Get a Batch part, and deserialise the file into a the BatchPartInfo Set property
            var data = Deserialize(this.FileName, directoryFullPath);

            // Import data in a typed Set
            set.ImportContainerSet(data, true);

            this.Data = set;
        }
        public async Task Table_Exists()
        {
            var dbName = HelperDatabase.GetRandomName("tcp_lo_");
            await HelperDatabase.CreateDatabaseAsync(ProviderType.Sql, dbName, true);

            var cs          = HelperDatabase.GetConnectionString(ProviderType.Sql, dbName);
            var sqlProvider = new SqlSyncProvider(cs);

            var options = new SyncOptions();
            var setup   = new SyncSetup(new string[] { "SalesLT.Product", "SalesLT.ProductCategory" });

            var table   = new SyncTable("Product", "SalesLT");
            var colID   = new SyncColumn("ID", typeof(Guid));
            var colName = new SyncColumn("Name", typeof(string));

            table.Columns.Add(colID);
            table.Columns.Add(colName);
            table.Columns.Add("Number", typeof(int));
            table.PrimaryKeys.Add("ID");

            var schema = new SyncSet();

            schema.Tables.Add(table);

            var localOrchestrator = new LocalOrchestrator(sqlProvider, options);

            var scopeInfo = await localOrchestrator.GetClientScopeInfoAsync();

            scopeInfo.Setup  = setup;
            scopeInfo.Schema = schema;
            await localOrchestrator.SaveClientScopeInfoAsync(scopeInfo);

            await localOrchestrator.CreateTableAsync(scopeInfo, "Product", "SalesLT");

            var exists = await localOrchestrator.ExistTableAsync(scopeInfo, table.TableName, table.SchemaName).ConfigureAwait(false);

            Assert.True(exists);

            exists = await localOrchestrator.ExistTableAsync(scopeInfo, "ProductCategory", "SalesLT").ConfigureAwait(false);

            Assert.False(exists);

            HelperDatabase.DropDatabase(ProviderType.Sql, dbName);
        }
Exemplo n.º 17
0
        public async Task BaseOrchestrator_Provision_SchemaFail_If_SchemaHasColumnsDefinitionButNoPrimaryKey()
        {
            var dbName = HelperDatabase.GetRandomName("tcp_lo_");
            await HelperDatabase.CreateDatabaseAsync(ProviderType.Sql, dbName, true);

            var cs          = HelperDatabase.GetConnectionString(ProviderType.Sql, dbName);
            var sqlProvider = new SqlSyncProvider(cs);

            var scopeName = "scope";

            var options = new SyncOptions();
            var setup   = new SyncSetup();

            var schema  = new SyncSet();
            var table   = new SyncTable("Product", "SalesLT");
            var colID   = new SyncColumn("ID", typeof(Guid));
            var colName = new SyncColumn("Name", typeof(string));

            table.Columns.Add(colID);
            table.Columns.Add(colName);
            table.Columns.Add("Number", typeof(int));

            schema.Tables.Add(table);

            var localOrchestrator = new LocalOrchestrator(sqlProvider, options, setup, scopeName);

            var provision = SyncProvision.Table | SyncProvision.TrackingTable | SyncProvision.StoredProcedures | SyncProvision.Triggers;



            var se = await Assert.ThrowsAsync <SyncException>(async() => await localOrchestrator.ProvisionAsync(schema, provision));

            Assert.Equal(SyncStage.Provisioning, se.SyncStage);
            Assert.Equal(SyncSide.ClientSide, se.Side);
            Assert.Equal("MissingPrimaryKeyException", se.TypeName);



            HelperDatabase.DropDatabase(ProviderType.Sql, dbName);
        }
Exemplo n.º 18
0
        /// <summary>
        /// Loads the batch file and import the rows in a SyncSet instance
        /// </summary>
        public async Task LoadBatchAsync(SyncSet sanitizedSchema, string directoryFullPath, BaseOrchestrator orchestrator = null)
        {
            if (this.Data != null)
            {
                return;
            }

            if (string.IsNullOrEmpty(this.FileName))
            {
                return;
            }

            // Clone the schema to get a unique instance
            var set = sanitizedSchema.Clone();

            // Get a Batch part, and deserialise the file into a the BatchPartInfo Set property
            var data = await DeserializeAsync(this.FileName, directoryFullPath, orchestrator);

            // Import data in a typed Set
            set.ImportContainerSet(data, true);

            this.Data = set;
        }
Exemplo n.º 19
0
    private void redo(List <Sync <T> > syncs)
    {
        for (int syncCount = 0; syncCount < syncs.Count; syncCount++)
        {
            Sync <T> sync = syncs[syncCount];
            switch (sync.getType())
            {
            case Sync <T> .Type.Set:
            {
                SyncSet <T> syncSet = (SyncSet <T>)sync;
                if (syncSet.olds.Count == syncSet.news.Count && syncSet.olds.Count == 1)
                {
                    this.v = syncSet.news[0];
                }
                else
                {
                    Logger.LogError("count error: " + this);
                }
            }
            break;

            case Sync <T> .Type.Add:
            {
            }
            break;

            case Sync <T> .Type.Remove:
            {
            }
            break;

            default:
                Logger.LogError("unknown type: " + sync.getType() + "; " + this);
                break;
            }
        }
    }
Exemplo n.º 20
0
        /// <summary>
        /// Get changes from
        /// </summary>
        internal async Task <HttpMessageSendChangesResponse> ApplyThenGetChangesAsync(
            HttpMessageSendChangesRequest httpMessage, SessionCache sessionCache, int clientBatchSize, CancellationToken cancellationToken)
        {
            // Get if we need to serialize data or making everything in memory
            var clientWorkInMemory = clientBatchSize == 0;

            // Check schema.
            // If client has stored the schema, the EnsureScope will not be called on server.
            if (this.Schema == null || !this.Schema.HasTables || !this.Schema.HasColumns)
            {
                var(_, newSchema) = await this.EnsureSchemaAsync(
                    httpMessage.SyncContext, this.Setup, cancellationToken).ConfigureAwait(false);

                newSchema.EnsureSchema();
                this.Schema = newSchema;
            }

            // ------------------------------------------------------------
            // FIRST STEP : receive client changes
            // ------------------------------------------------------------

            // We are receiving changes from client
            // BatchInfo containing all BatchPartInfo objects
            // Retrieve batchinfo instance if exists

            // Get batch info from session cache if exists, otherwise create it
            if (sessionCache.ClientBatchInfo == null)
            {
                sessionCache.ClientBatchInfo = new BatchInfo(clientWorkInMemory, Schema, this.Options.BatchDirectory);
            }

            // create the in memory changes set
            var changesSet = new SyncSet(Schema.ScopeName);

            foreach (var table in httpMessage.Changes.Tables)
            {
                DbSyncAdapter.CreateChangesTable(Schema.Tables[table.TableName, table.SchemaName], changesSet);
            }

            changesSet.ImportContainerSet(httpMessage.Changes, false);

            // If client has made a conversion on each line, apply the reverse side of it
            if (this.ClientConverter != null && changesSet.HasRows)
            {
                AfterDeserializedRows(changesSet, this.ClientConverter);
            }

            // add changes to the batch info
            sessionCache.ClientBatchInfo.AddChanges(changesSet, httpMessage.BatchIndex, httpMessage.IsLastBatch);


            // Clear the httpMessage set
            if (!clientWorkInMemory && httpMessage.Changes != null)
            {
                httpMessage.Changes.Clear();
            }

            // Until we don't have received all the batches, wait for more
            if (!httpMessage.IsLastBatch)
            {
                return new HttpMessageSendChangesResponse(httpMessage.SyncContext)
                       {
                           ServerStep = HttpStep.SendChangesInProgress
                       }
            }
            ;

            // ------------------------------------------------------------
            // SECOND STEP : apply then return server changes
            // ------------------------------------------------------------

            // get changes
            var(context, remoteClientTimestamp, serverBatchInfo, policy, serverChangesSelected) =
                await this.ApplyThenGetChangesAsync(httpMessage.SyncContext,
                                                    httpMessage.Scope, this.Schema, sessionCache.ClientBatchInfo, this.Options.DisableConstraintsOnApplyChanges,
                                                    this.Options.UseBulkOperations, false, this.Options.CleanFolder, clientBatchSize,
                                                    this.Options.BatchDirectory, this.Options.ConflictResolutionPolicy, cancellationToken).ConfigureAwait(false);


            // Save the server batch info object to cache if not working in memory
            if (!clientWorkInMemory)
            {
                sessionCache.RemoteClientTimestamp = remoteClientTimestamp;

                sessionCache.ServerBatchInfo       = serverBatchInfo;
                sessionCache.ServerChangesSelected = serverChangesSelected;
            }

            // Get the firt response to send back to client
            return(GetChangesResponse(context, remoteClientTimestamp, serverBatchInfo, serverChangesSelected, 0, policy));
        }
 public MessageGetChangesBatch(Guid?excludingScopeId, Guid localScopeId, bool isNew, long lastTimestamp, SyncSet schema,
                               int batchSize, string batchDirectory)
 {
     this.Schema           = schema ?? throw new ArgumentNullException(nameof(schema));
     this.BatchDirectory   = batchDirectory ?? throw new ArgumentNullException(nameof(batchDirectory));
     this.ExcludingScopeId = excludingScopeId;
     this.LocalScopeId     = localScopeId;
     this.IsNew            = isNew;
     this.LastTimestamp    = lastTimestamp;
     this.BatchSize        = batchSize;
 }
Exemplo n.º 22
0
        private static SyncSet CreateSchema()
        {
            var set = new SyncSet();

            set.StoredProceduresPrefix = "spp";
            set.StoredProceduresSuffix = "sps";
            set.TrackingTablesPrefix   = "ttp";
            set.TrackingTablesSuffix   = "tts";
            set.TriggersPrefix         = "tp";
            set.TriggersSuffix         = "ts";

            var tbl = new SyncTable("ServiceTickets", null);

            tbl.OriginalProvider = "SqlServerProvider";
            tbl.SyncDirection    = Enumerations.SyncDirection.Bidirectional;

            set.Tables.Add(tbl);

            var c = SyncColumn.Create <int>("ServiceTicketID");

            c.DbType            = 8;
            c.AllowDBNull       = true;
            c.IsAutoIncrement   = true;
            c.AutoIncrementStep = 1;
            c.AutoIncrementSeed = 10;
            c.IsCompute         = false;
            c.IsReadOnly        = true;
            tbl.Columns.Add(c);

            tbl.Columns.Add(SyncColumn.Create <string>("Title"));
            tbl.Columns.Add(SyncColumn.Create <string>("Description"));
            tbl.Columns.Add(SyncColumn.Create <int>("StatusValue"));
            tbl.Columns.Add(SyncColumn.Create <int>("EscalationLevel"));
            tbl.Columns.Add(SyncColumn.Create <DateTime>("Opened"));
            tbl.Columns.Add(SyncColumn.Create <DateTime>("Closed"));
            tbl.Columns.Add(SyncColumn.Create <int>("CustomerID"));

            tbl.PrimaryKeys.Add("ServiceTicketID");

            // Add Second tables
            var tbl2 = new SyncTable("Product", "SalesLT");

            tbl2.SyncDirection = SyncDirection.UploadOnly;

            tbl2.Columns.Add(SyncColumn.Create <int>("Id"));
            tbl2.Columns.Add(SyncColumn.Create <string>("Title"));
            tbl2.PrimaryKeys.Add("Id");

            set.Tables.Add(tbl2);


            // Add Filters
            var sf = new SyncFilter("Product", "SalesLT");

            sf.Parameters.Add(new SyncFilterParameter {
                Name = "Title", DbType = DbType.String, MaxLength = 20, DefaultValue = "'Bikes'"
            });
            sf.Parameters.Add(new SyncFilterParameter {
                Name = "LastName", TableName = "Customer", SchemaName = "SalesLT", AllowNull = true
            });
            sf.Wheres.Add(new SyncFilterWhereSideItem {
                ColumnName = "Title", ParameterName = "Title", SchemaName = "SalesLT", TableName = "Product"
            });
            sf.Joins.Add(new SyncFilterJoin {
                JoinEnum = Join.Right, TableName = "SalesLT.ProductCategory", LeftColumnName = "LCN", LeftTableName = "SalesLT.Product", RightColumnName = "RCN", RightTableName = "SalesLT.ProductCategory"
            });
            sf.CustomWheres.Add("1 = 1");
            set.Filters.Add(sf);

            // Add Relations
            var keys       = new[] { new SyncColumnIdentifier("ProductId", "ServiceTickets") };
            var parentKeys = new[] { new SyncColumnIdentifier("ProductId", "Product", "SalesLT") };
            var rel        = new SyncRelation("AdventureWorks_Product_ServiceTickets", keys, parentKeys);

            set.Relations.Add(rel);

            return(set);
        }
Exemplo n.º 23
0
        private void Assertions(SyncSet outSchema)
        {
            // Call the EnsureSchema to propagate schema to all entities
            outSchema.EnsureSchema();

            Assert.NotNull(outSchema);
            Assert.Equal("spp", outSchema.StoredProceduresPrefix);
            Assert.Equal("sps", outSchema.StoredProceduresSuffix);
            Assert.Equal("ttp", outSchema.TrackingTablesPrefix);
            Assert.Equal("tts", outSchema.TrackingTablesSuffix);
            Assert.Equal("tp", outSchema.TriggersPrefix);
            Assert.Equal("ts", outSchema.TriggersSuffix);
            Assert.Equal(SyncOptions.DefaultScopeName, outSchema.ScopeName);
            Assert.NotEmpty(outSchema.Tables);
            Assert.NotEmpty(outSchema.Filters);
            Assert.NotEmpty(outSchema.Relations);
            Assert.Equal(2, outSchema.Tables.Count);
            Assert.Single(outSchema.Relations);
            Assert.Single(outSchema.Filters);

            var tbl1 = outSchema.Tables[0];

            Assert.Equal("ServiceTickets", tbl1.TableName);
            Assert.Null(tbl1.SchemaName);
            Assert.Equal(SyncDirection.Bidirectional, tbl1.SyncDirection);
            Assert.NotNull(tbl1.Schema);
            Assert.Equal(outSchema, tbl1.Schema);
            Assert.Equal(8, tbl1.Columns.Count);
            Assert.Equal(tbl1, tbl1.Columns.Table);
            Assert.NotEmpty(tbl1.Columns.InnerCollection);

            var col = tbl1.Columns[0];

            Assert.Equal("ServiceTicketID", col.ColumnName);
            Assert.True(col.AllowDBNull);
            Assert.Equal(10, col.AutoIncrementSeed);
            Assert.Equal(1, col.AutoIncrementStep);
            Assert.True(col.IsAutoIncrement);
            Assert.False(col.IsCompute);
            Assert.True(col.IsReadOnly);
            Assert.Equal(0, col.Ordinal);

            // check orders on others columns
            Assert.Equal(7, tbl1.Columns["CustomerID"].Ordinal);

            var tbl2 = outSchema.Tables[1];

            Assert.Equal("Product", tbl2.TableName);
            Assert.Equal("SalesLT", tbl2.SchemaName);
            Assert.Equal(SyncDirection.UploadOnly, tbl2.SyncDirection);
            Assert.NotNull(tbl2.Schema);
            Assert.Equal(outSchema, tbl2.Schema);
            Assert.Equal(2, tbl2.Columns.Count);
            Assert.Equal(tbl2, tbl2.Columns.Table);
            Assert.NotEmpty(tbl2.Columns.InnerCollection);
            Assert.Single(tbl2.PrimaryKeys);
            Assert.Equal("Id", tbl2.PrimaryKeys[0]);

            // Check Filters
            Assert.NotEmpty(outSchema.Filters);
            var sf = outSchema.Filters[0];

            Assert.Equal("Product", sf.TableName);
            Assert.Equal("SalesLT", sf.SchemaName);
            Assert.Equal(outSchema, sf.Schema);
            Assert.Equal(2, sf.Parameters.Count);
            Assert.Single(sf.Joins);
            Assert.Single(sf.CustomWheres);
            Assert.Single(sf.Wheres);
            // Parameter 01
            Assert.Equal("Title", sf.Parameters[0].Name);
            Assert.Equal(20, sf.Parameters[0].MaxLength);
            Assert.Equal("'Bikes'", sf.Parameters[0].DefaultValue);
            Assert.False(sf.Parameters[0].AllowNull);
            Assert.Null(sf.Parameters[0].TableName);
            Assert.Null(sf.Parameters[0].SchemaName);
            Assert.Equal(outSchema, sf.Parameters[0].Schema);
            // Parameter 02
            Assert.Equal("LastName", sf.Parameters[1].Name);
            Assert.Equal(0, sf.Parameters[1].MaxLength);
            Assert.Null(sf.Parameters[1].DefaultValue);
            Assert.True(sf.Parameters[1].AllowNull);
            Assert.Equal("Customer", sf.Parameters[1].TableName);
            Assert.Equal("SalesLT", sf.Parameters[1].SchemaName);
            Assert.Equal(outSchema, sf.Parameters[1].Schema);
            // Joins
            Assert.Equal(Join.Right, sf.Joins[0].JoinEnum);
            Assert.Equal("SalesLT.ProductCategory", sf.Joins[0].TableName);
            Assert.Equal("LCN", sf.Joins[0].LeftColumnName);
            Assert.Equal("SalesLT.Product", sf.Joins[0].LeftTableName);
            Assert.Equal("RCN", sf.Joins[0].RightColumnName);
            Assert.Equal("SalesLT.ProductCategory", sf.Joins[0].RightTableName);
            // Wheres
            Assert.Equal("Title", sf.Wheres[0].ColumnName);
            Assert.Equal("Title", sf.Wheres[0].ParameterName);
            Assert.Equal("SalesLT", sf.Wheres[0].SchemaName);
            Assert.Equal("Product", sf.Wheres[0].TableName);
            // Customer Wheres
            Assert.Equal("1 = 1", sf.CustomWheres[0]);



            // Check Relations
            Assert.NotEmpty(outSchema.Relations);
            var rel = outSchema.Relations[0];

            Assert.Equal("AdventureWorks_Product_ServiceTickets", rel.RelationName);
            Assert.NotEmpty(rel.ParentKeys);
            Assert.NotEmpty(rel.Keys);
            var c = rel.Keys.ToList()[0];

            Assert.Equal("ProductId", c.ColumnName);
            Assert.Equal("ServiceTickets", c.TableName);
            Assert.Null(c.SchemaName);
            var p = rel.ParentKeys.ToList()[0];

            Assert.Equal("ProductId", p.ColumnName);
            Assert.Equal("Product", p.TableName);
            Assert.Equal("SalesLT", p.SchemaName);
        }
Exemplo n.º 24
0
        /// <summary>
        /// Create a new BPI, and serialize the changeset if not in memory
        /// </summary>
        internal static async Task <BatchPartInfo> CreateBatchPartInfoAsync(int batchIndex, SyncSet set, string fileName, string directoryFullPath, bool isLastBatch, BaseOrchestrator orchestrator = null)
        {
            BatchPartInfo bpi = null;

            // Create a batch part
            // The batch part creation process will serialize the changesSet to the disk

            // Serialize the file !
            await SerializeAsync(set.GetContainerSet(), fileName, directoryFullPath, orchestrator);

            bpi = new BatchPartInfo {
                FileName = fileName
            };

            bpi.Index       = batchIndex;
            bpi.IsLastBatch = isLastBatch;

            // Even if the set is empty (serialized on disk), we should retain the tables names
            if (set != null)
            {
                bpi.Tables    = set.Tables.Select(t => new BatchPartTableInfo(t.TableName, t.SchemaName, t.Rows.Count)).ToArray();
                bpi.RowsCount = set.Tables.Sum(t => t.Rows.Count);
            }

            return(bpi);
        }
Exemplo n.º 25
0
 /// <summary>
 /// Metadatas are handled by Change Tracking
 /// So just do nothing here
 /// </summary>
 public override Task <(SyncContext, DatabaseMetadatasCleaned)> DeleteMetadatasAsync(SyncContext context, SyncSet schema, SyncSetup setup, long timestampLimit, DbConnection connection, DbTransaction transaction, CancellationToken cancellationToken, IProgress <ProgressArgs> progress = null)
 => Task.FromResult((context, new DatabaseMetadatasCleaned()));
Exemplo n.º 26
0
 /// <summary>
 /// Internally setting schema
 /// </summary>
 internal void SetSchema(SyncSet schema) => this.schema = schema;
 /// <summary>
 /// Metadatas are handled by Change Tracking
 /// So just do nothing here
 /// </summary>
 public override Task <SyncContext> DeleteMetadatasAsync(SyncContext context, SyncSet schema, long timestampLimit, DbConnection connection, DbTransaction transaction, CancellationToken cancellationToken, IProgress <ProgressArgs> progress = null)
 => Task.FromResult(context);
Exemplo n.º 28
0
        /// <summary>
        /// Create a response message content based on a requested index in a server batch info
        /// </summary>
        private HttpMessageSendChangesResponse GetChangesResponse(SyncContext syncContext, long remoteClientTimestamp, BatchInfo serverBatchInfo,
                                                                  DatabaseChangesSelected serverChangesSelected, int batchIndexRequested, ConflictResolutionPolicy policy)
        {
            // 1) Create the http message content response
            var changesResponse = new HttpMessageSendChangesResponse(syncContext);

            changesResponse.ChangesSelected          = serverChangesSelected;
            changesResponse.ServerStep               = HttpStep.GetChanges;
            changesResponse.ConflictResolutionPolicy = policy;

            // If nothing to do, just send back
            if (serverBatchInfo.InMemory || serverBatchInfo.BatchPartsInfo.Count == 0)
            {
                if (this.ClientConverter != null && serverBatchInfo.InMemoryData.HasRows)
                {
                    BeforeSerializeRows(serverBatchInfo.InMemoryData, this.ClientConverter);
                }

                changesResponse.Changes               = serverBatchInfo.InMemoryData.GetContainerSet();
                changesResponse.BatchIndex            = 0;
                changesResponse.IsLastBatch           = true;
                changesResponse.RemoteClientTimestamp = remoteClientTimestamp;
                return(changesResponse);
            }

            // Get the batch part index requested
            var batchPartInfo = serverBatchInfo.BatchPartsInfo.First(d => d.Index == batchIndexRequested);

            // if we are not in memory, we set the BI in session, to be able to get it back on next request

            // create the in memory changes set
            var changesSet = new SyncSet(Schema.ScopeName);

            foreach (var table in Schema.Tables)
            {
                DbSyncAdapter.CreateChangesTable(Schema.Tables[table.TableName, table.SchemaName], changesSet);
            }

            batchPartInfo.LoadBatch(changesSet, serverBatchInfo.GetDirectoryFullPath());

            // if client request a conversion on each row, apply the conversion
            if (this.ClientConverter != null && batchPartInfo.Data.HasRows)
            {
                BeforeSerializeRows(batchPartInfo.Data, this.ClientConverter);
            }

            changesResponse.Changes = batchPartInfo.Data.GetContainerSet();

            changesResponse.BatchIndex            = batchIndexRequested;
            changesResponse.IsLastBatch           = batchPartInfo.IsLastBatch;
            changesResponse.RemoteClientTimestamp = remoteClientTimestamp;
            changesResponse.ServerStep            = batchPartInfo.IsLastBatch ? HttpStep.GetChanges : HttpStep.GetChangesInProgress;

            // If we have only one bpi, we can safely delete it
            if (batchPartInfo.IsLastBatch)
            {
                // delete the folder (not the BatchPartInfo, because we have a reference on it)
                if (this.Options.CleanFolder)
                {
                    var shouldDeleteFolder = true;
                    if (!string.IsNullOrEmpty(this.Options.SnapshotsDirectory))
                    {
                        var dirInfo  = new DirectoryInfo(serverBatchInfo.DirectoryRoot);
                        var snapInfo = new DirectoryInfo(this.Options.SnapshotsDirectory);
                        shouldDeleteFolder = dirInfo.FullName != snapInfo.FullName;
                    }

                    if (shouldDeleteFolder)
                    {
                        serverBatchInfo.TryRemoveDirectory();
                    }
                }
            }

            return(changesResponse);
        }
Exemplo n.º 29
0
    public void processChange(List <Change <T> > changes)
    {
        if (changes.Count > 0)
        {
            // Make list change
            List <Sync <T> > syncs = new List <Sync <T> >();
            {
                for (int syncCount = 0; syncCount < changes.Count; syncCount++)
                {
                    Change <T> change = changes[syncCount];
                    switch (change.getType())
                    {
                    case Change <T> .Type.Set:
                    {
                        ChangeSet <T> changeSet = (ChangeSet <T>)change;
                        // keep old SyncSet
                        SyncSet <T> oldSyncSet = null;
                        if (changeSet.index >= 0 && changeSet.index + changeSet.values.Count <= this.vs.Count)
                        {
                            for (int i = 0; i < changeSet.values.Count; i++)
                            {
                                int setIndex = changeSet.index + i;
                                T   oldValue = this.vs[setIndex];
                                // check is different
                                bool isDifferent = true;
                                {
                                    if (object.Equals(oldValue, changeSet.values[i]))
                                    {
                                        isDifferent = false;
                                    }
                                    else
                                    {
                                        if (((Data)(object)changeSet.values[i]).uid == ((Data)(object)oldValue).uid)
                                        {
                                            isDifferent = false;
                                        }
                                    }
                                }
                                // Make change
                                if (isDifferent)
                                {
                                    // Change
                                    {
                                        // Set parent
                                        {
                                            ((Data)(object)oldValue).p            = null;
                                            ((Data)(object)changeSet.values[i]).p = this;
                                        }
                                        // add
                                        this.vs[setIndex] = changeSet.values[i];
                                    }
                                    // Make Sync
                                    {
                                        // get changeSet
                                        SyncSet <T> syncSet = null;
                                        {
                                            // check old
                                            if (oldSyncSet != null)
                                            {
                                                if (oldSyncSet.index + oldSyncSet.olds.Count == setIndex)
                                                {
                                                    syncSet = oldSyncSet;
                                                }
                                            }
                                            // make new
                                            if (syncSet == null)
                                            {
                                                syncSet = new SyncSet <T>();
                                                {
                                                    syncSet.index = setIndex;
                                                }
                                                syncs.Add(syncSet);
                                                // set new old
                                                oldSyncSet = syncSet;
                                            }
                                        }
                                        // add value
                                        {
                                            syncSet.olds.Add(oldValue);
                                            syncSet.news.Add(changeSet.values[i]);
                                        }
                                    }
                                }
                                else
                                {
                                    // Debug.LogError("why the same: " + oldValue + "; " + changeSet.values[i]);
                                }
                            }
                        }
                        else
                        {
                            Logger.LogError("index error: " + changeSet.index + "; " + this.vs.Count + "; " + this);
                        }
                    }
                    break;

                    case Change <T> .Type.Add:
                    {
                        ChangeAdd <T> changeAdd = (ChangeAdd <T>)change;
                        // Add
                        if (changeAdd.index >= 0 && changeAdd.index <= this.vs.Count)
                        {
                            // Change
                            {
                                // set parent
                                {
                                    foreach (T value in changeAdd.values)
                                    {
                                        if (value != null)
                                        {
                                            ((Data)(object)value).p = this;
                                        }
                                        else
                                        {
                                            Logger.LogError("why value null: " + this);
                                        }
                                    }
                                }
                                this.vs.InsertRange(changeAdd.index, changeAdd.values);
                            }
                            // Make Sync
                            {
                                SyncAdd <T> syncAdd = new SyncAdd <T>();
                                {
                                    syncAdd.index = changeAdd.index;
                                    syncAdd.values.AddRange(changeAdd.values);
                                }
                                syncs.Add(syncAdd);
                            }
                        }
                        else
                        {
                            Logger.LogError("index error: " + changeAdd.index + "; " + this.vs.Count + "; " + this);
                        }
                    }
                    break;

                    case Change <T> .Type.Remove:
                    {
                        ChangeRemove <T> changeRemove = (ChangeRemove <T>)change;
                        // Check index
                        if (changeRemove.number > 0 && changeRemove.index >= 0 && changeRemove.index + changeRemove.number <= this.vs.Count)
                        {
                            // Make sync: phai make sync truoc moi lay duoc oldValues
                            SyncRemove <T> syncRemove = new SyncRemove <T>();
                            {
                                syncRemove.index = changeRemove.index;
                                for (int i = 0; i < changeRemove.number; i++)
                                {
                                    syncRemove.values.Add(this.vs[changeRemove.index + i]);
                                }
                            }
                            syncs.Add(syncRemove);
                            // Change
                            {
                                // set parent
                                {
                                    foreach (T value in syncRemove.values)
                                    {
                                        if (value != null)
                                        {
                                            ((Data)(object)value).p = null;
                                        }
                                        else
                                        {
                                            Logger.LogError("why value null: " + this);
                                        }
                                    }
                                }
                                // Remove
                                this.vs.RemoveRange(changeRemove.index, changeRemove.number);
                            }
                        }
                        else
                        {
                            Logger.LogError("index error: " + this);
                        }
                    }
                    break;

                    default:
                        Logger.LogError("unknown change type: " + change.getType() + "; " + this);
                        break;
                    }
                }
            }
            // CallBack
            if (syncs.Count > 0)
            {
                foreach (ValueChangeCallBack callBack in p.callBacks.ToArray())
                {
                    callBack.onUpdateSync(this, syncs);
                }
            }
            else
            {
                // Debug.LogError("why don't have syncCount: " + this);
            }
        }
        else
        {
            Logger.LogError("why don't have changes: " + this);
        }
    }
Exemplo n.º 30
0
 /// <summary>
 /// Applying changes message.
 /// Be careful policy could be differente from the schema (especially on client side, it's the reverse one, by default)
 /// </summary>
 public MessageApplyChanges(Guid localScopeId, Guid senderScopeId, bool isNew, long lastTimestamp, SyncSet schema,
                            ConflictResolutionPolicy policy, bool disableConstraintsOnApplyChanges,
                            bool useBulkOperations, bool cleanMetadatas, bool cleanFolder, BatchInfo changes)
 {
     this.LocalScopeId  = localScopeId;
     this.SenderScopeId = senderScopeId;
     this.IsNew         = isNew;
     this.LastTimestamp = lastTimestamp;
     this.Schema        = schema ?? throw new ArgumentNullException(nameof(schema));
     this.Policy        = policy;
     this.DisableConstraintsOnApplyChanges = disableConstraintsOnApplyChanges;
     this.UseBulkOperations = useBulkOperations;
     this.CleanMetadatas    = cleanMetadatas;
     this.CleanFolder       = cleanFolder;
     this.Changes           = changes ?? throw new ArgumentNullException(nameof(changes));
 }