Replies: 8 comments 12 replies
-
Upgrading from v0.94 to v0.95Upgrading from v0.94 to v0.95 is now supported.
// Upgrade to last version
if (await remoteOrchestrator.NeedsToUpgradeAsync())
await remoteOrchestrator.UpgradeAsync(progress:progress); When making the upgrade of your system (server / clients), be sure to remember that:
That being said, be sure to firstly upgrade your server to the last version, call the |
Beta Was this translation helpful? Give feedback.
-
Immutablity & stateless objectsDMS main objects ( That's why a lot of code have been refactored to be able to extract any dependencies in these objects. For instance, let's see how we called // v0.94
// ------------------
var serverProvider = new SqlSyncProvider(serverConnectionString);
var clientProvider = new SqliteSyncProvider(clientConnectionString);
var agent = new SyncAgent(clientProvider, serverProvider, tables);
var s1 = await agent.SynchronizeAsync(); // v0.95
// ------------------
var serverProvider = new SqlSyncProvider(serverConnectionString);
var clientProvider = new SqliteSyncProvider(clientConnectionString);
var agent = new SyncAgent(clientProvider, serverProvider);
var s1 = await agent.SynchronizeAsync(tables); As you can see the tables are not part of the A new feature has been added to allow calling // v0.95
// ------------------
// Already done a sync with the correct setup before
var s1 = await agent.SynchronizeAsync(); Working with scopes becames way more easily: // v0.95
// ------------------
var agent = new SyncAgent(client.Provider, Server.Provider, options);
var setup = new SyncSetup("Product", "ProductCategory");
var s = await agent.SynchronizeAsync(setup);
var setup = new SyncSetup("Employee");
var s2 = await agent.SynchronizeAsync(setup, "v2"); And as said earlier, on the next call, you do not have to pass the setup instance: // v0.95
// ------------------
var agent = new SyncAgent(clientProvider, serverProvider);
var s1 = await agent.SynchronizeAsync();
var s2 = await agent.SynchronizeAsync("v2"); Following this pattern, a lot of methods from
For example the method // v0.94
// ------------------
var setup = new SyncSetup(new string[] { "ProductCategory", "Product" });
var remoteOrchestrator = new RemoteOrchestrator(serverProvider, setup);
await remoteOrchestrator.ProvisionAsync(); And now the new method signature is: // v0.95
// ------------------
var setup = new SyncSetup(new string[] { "ProductCategory", "Product" });
var remoteOrchestrator = new RemoteOrchestrator(serverProvider);
var serverScope = await remoteOrchestrator.ProvisionAsync(setup); |
Beta Was this translation helpful? Give feedback.
-
MigrationFirst of all, DMS will not update or migrate your schema anymore
The only timeframe where DMS has an interaction with your schema is during the first setup.
Why removing the automatic schema update ? Well basically, automatic updates on clients and server will force to update all clients at the same time. We need to be able to serve and sync clients :
How to migrate from one version to the next oneYou have an in depth example here : https://github.com/Mimetis/Dotmim.Sync/blob/v0.9.5/Samples/Migration/Program.cs First of all, it's your responsibility to ensure your database can handle several versions.
Here is a full example on how to handle multiple clients where:
// v0.95
// ------------------
var serverProvider = new SqlSyncProvider(serverDbConnectionString);
var client1Provider = new SqlSyncProvider(clientDbConnectionString);
var client2Provider = new SqlSyncProvider(clientDbConnectionString));
// first setup will contain 1 table with 4 columns
var setup = new SyncSetup(new string[] { "ProductCategory" });
setup.Tables[productCategoryTableName].Columns.AddRange(
new string[] { "ProductCategoryId", "Name", "rowguid", "ModifiedDate" });
// Assuming we have two clients that are syncing this first setup on a scope named "v1":
var agent1 = new SyncAgent(client1Provider, serverProvider);
await agent1.SynchronizeAsync( setup, "v1");
var agent2 = new SyncAgent(client2Provider, serverProvider);
await agent2.SynchronizeAsync( setup, "v1"); Now, we are updating the server schema: // v0.95
// ------------------
// assuming we are adding a column to ProductCategory
// this method will add a new column : "CreatedDate" using a sql stmt:
// ALTER TABLE dbo.ProductCategory ADD CreatedDate datetime NULL;
await AddColumnsToProductCategoryAsync(serverProvider);
// Now we are creating a new scope called V2:
// ProductCategory with 1 more column
// a new table Product
var setupV2 = new SyncSetup(new string[] { "ProductCategory", "Product" });
setupV2.Tables["ProductCategory"].Columns.AddRange(
new string[] { "ProductCategoryID", "ParentProductCategoryID", "Name", "rowguid", "ModifiedDate", "CreatedDate" });
// We are provisioning this new scope on the server
var serverScope = await remoteOrchestrator.ProvisionAsync("v2", setupV2); We have here two scopes running in parrallel on the server :
At this point, you need to update your client schema as it's not made automatically by DMS anymore: // v0.95
// ------------------
// Add the column CreatedDate to client 1
await AddColumnsToProductCategoryAsync(clientProvider1);
// Add product table to client 1
await CreateTableProductAsync(clientProvider1); Note that we are not updating the schema of client 2. We are provsioning the client 1 with the new scope v2: // v0.95
// ------------------
var serverScope = await remoteOrchestrator.GetServerScopeInfoAsync("v2")
// Provision the "v2" scope on the client 1 with the new setup
await localOrchestrator.ProvisionAsync(serverScope);
// makeing a sync on the client 1 with the v2 scope
Console.WriteLine(await agent.SynchronizeAsync("v2", SyntType.Reinitialize)); Client 1 updated to |
Beta Was this translation helpful? Give feedback.
-
Removing triggers columns comparisonTriggers are not making a comparison on each column anymore Here is a comparison before and after this change: -- v0.94
-- ------------------
UPDATE [side]
SET [update_scope_id] = NULL -- since the update if from local, it's a NULL
,[last_change_datetime] = GetUtcDate()
FROM [ProductCategory_tracking] [side]
JOIN INSERTED AS [i] ON [side].[ProductCategoryID] = [i].[ProductCategoryID]
JOIN DELETED AS [d] ON [d].[ProductCategoryID] = [i].[ProductCategoryID]
WHERE (
ISNULL(NULLIF([d].[ParentProductCategoryID], [i].[ParentProductCategoryID]), NULLIF([i].[ParentProductCategoryID],
[d].[ParentProductCategoryID])) IS NOT NULL
OR ISNULL(NULLIF([d].[Name], [i].[Name]), NULLIF([i].[Name], [d].[Name])) IS NOT NULL
OR ISNULL(NULLIF([d].[rowguid], [i].[rowguid]), NULLIF([i].[rowguid], [d].[rowguid])) IS NOT NULL
OR ISNULL(NULLIF([d].[ModifiedDate], [i].[ModifiedDate]), NULLIF([i].[ModifiedDate], [d].[ModifiedDate])) IS NOT NULL -- v0.95
-- ------------------
UPDATE [side]
SET [update_scope_id] = NULL -- since the update if from local, it's a NULL
,[last_change_datetime] = GetUtcDate()
FROM [ProductCategory_tracking] [side]
JOIN INSERTED AS [i] ON [side].[ProductCategoryID] = [i].[ProductCategoryID] |
Beta Was this translation helpful? Give feedback.
-
WebServerAgentThe Dotmim.Sync.Web.Client has been refactored to replace Since this orchestrator is basically used on the client side as a "remote web proxy orchestrator" it's more appropriate to rename it to Just renaming // v0.94
// ------------------
var proxyOrchestrator = new WebClientOrchestrator("https://localhost:44342/api/sync");
var clientProvider = new SqlSyncProvider(clientConnectionString);
var agent = new SyncAgent(clientProvider, serverOrchestrator); // v0.95
// ------------------
var remoteOrchestrator = new WebRemoteOrchestrator("https://localhost:44342/api/sync");
var clientProvider = new SqlSyncProvider(clientConnectionString);
var agent = new SyncAgent(clientProvider, remoteOrchestrator); The Dotmim.Sync.Web.Server has been refactored to replace It basically changes the controller, but not too much. // v0.94
// ------------------
private WebServerOrchestrator orchestrator;
// Injected thanks to Dependency Injection
public SyncController(WebServerOrchestrator webServerOrchestrator)
=> this.orchestrator = webServerOrchestrator;
/// <summary>
/// This POST handler is mandatory to handle all the sync process
/// </summary>
/// <returns></returns>
[HttpPost]
public Task Post()
=> orchestrator.HandleRequestAsync(this.HttpContext);
/// <summary>
/// This GET handler is optional. It allows you to see the configuration hosted on the server
/// The configuration is shown only if Environmenent == Development
/// </summary>
[HttpGet]
public Task Get()
=> WebServerOrchestrator.WriteHelloAsync(this.HttpContext, orchestrator); // v0.95
// ------------------
private WebServerAgent webServerAgent;
private readonly IWebHostEnvironment env;
// Injected thanks to Dependency Injection
public SyncController(WebServerAgent webServerAgent, IWebHostEnvironment env)
{
this.webServerAgent = webServerAgent;
this.env = env;
}
/// <summary>
/// This POST handler is mandatory to handle all the sync process
/// </summary>
/// <returns></returns>
[HttpPost]
public Task Post()
=> webServerAgent.HandleRequestAsync(this.HttpContext);
/// <summary>
/// This GET handler is optional. It allows you to see the configuration hosted on the server
/// </summary>
[HttpGet]
public async Task Get()
{
if (env.IsDevelopment())
{
await this.HttpContext.WriteHelloAsync(webServerAgent);
}
else
{
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine("<!doctype html>");
stringBuilder.AppendLine("<html>");
stringBuilder.AppendLine("<title>Web Server properties</title>");
stringBuilder.AppendLine("<body>");
stringBuilder.AppendLine(" PRODUCTION. Write Whatever You Want Here ");
stringBuilder.AppendLine("</body>");
await this.HttpContext.Response.WriteAsync(stringBuilder.ToString());
}
} |
Beta Was this translation helpful? Give feedback.
-
Hi @Mimetis I'm going to put my team to test and see how this great improvement helps us. |
Beta Was this translation helpful? Give feedback.
-
InterceptorsThe interceptors are basically the objects in charge of all the Previously these interceptors were unique: For instance, this code will raise the // v0.94
// ------------------
// Creating an agent that will handle all the process
var agent = new SyncAgent(clientProvider, serverProvider, options);
agent.LocalOrchestrator.OnSessionBegin(sba => Console.WriteLine("Session begins 1"));
agent.LocalOrchestrator.OnSessionBegin(sba => Console.WriteLine("Session begins 2"));
agent.LocalOrchestrator.OnSessionBegin(sba => Console.WriteLine("Session begins 3"));
agent.LocalOrchestrator.OnSessionBegin(sba => Console.WriteLine("Session begins 4"));
var s = await agent.SynchronizeAsync(scopeName, setup, progress);*
Console.WriteLine("Synchronization done."); The output from this code will be: Sync start
Session begins 4
Synchronization done. Today, you can have multiple interceptors of the same type.
// v0.95
// ------------------
var id1 = agent.LocalOrchestrator.OnSessionBegin(sba => Console.WriteLine("Session begins 1"));
var id2 = agent.LocalOrchestrator.OnSessionBegin(sba => Console.WriteLine("Session begins 2"));
var id3 = agent.LocalOrchestrator.OnSessionBegin(sba => Console.WriteLine("Session begins 3"));
var id4 = agent.LocalOrchestrator.OnSessionBegin(sba => Console.WriteLine("Session begins 4"));
var s = await agent.SynchronizeAsync(scopeName, setup, progress);*
Console.WriteLine("Synchronization done."); The output from this code will be: Sync start
Session begins 1
Session begins 2
Session begins 3
Session begins 4
Synchronization done. As said, each call to the // v0.95
// ------------------
var id1 = agent.LocalOrchestrator.OnSessionBegin(sba => Console.WriteLine("Session begins 1"));
var id2 = agent.LocalOrchestrator.OnSessionBegin(sba => Console.WriteLine("Session begins 2"));
var id3 = agent.LocalOrchestrator.OnSessionBegin(sba => Console.WriteLine("Session begins 3"));
var id4 = agent.LocalOrchestrator.OnSessionBegin(sba => Console.WriteLine("Session begins 4"));
agent.LocalOrchestrator.ClearInterceptors(id3);
var s = await agent.SynchronizeAsync(scopeName, setup, progress);*
Console.WriteLine("Synchronization done."); The output from this code will be: Sync start
Session begins 1
Session begins 2
Session begins 4
Synchronization done. Obviously you can remove all interceptors from certain type: // v0.95
// ------------------
agent.LocalOrchestrator.ClearInterceptors<SessionBeginArgs>(); Or remove all interceptors: // v0.95
// ------------------
agent.LocalOrchestrator.ClearInterceptors(); |
Beta Was this translation helpful? Give feedback.
-
Overriding current operation from Server side: OnGettingOperationYou can now override the current synchronization in progress (called here operation) from your client, using the [Flags]
public enum SyncOperation
{
/// <summary>
/// Normal synchronization
/// </summary>
Normal = 0,
/// <summary>
/// Reinitialize the whole sync database, applying all rows from the server to the client
/// </summary>
Reinitialize = 1,
/// <summary>
/// Reinitialize the whole sync database, applying all rows from the server to the client, after trying a client upload
/// </summary>
ReinitializeWithUpload = 2,
/// <summary>
/// Drop all the sync metadatas even tracking tables and scope infos and make a full sync again
/// </summary>
DropAllAndSync = 4,
/// <summary>
/// Drop all the sync metadatas even tracking tables and scope infos and exit
/// </summary>
DropAllAndExit = 8,
/// <summary>
/// Deprovision stored procedures & triggers and sync again
/// </summary>
DeprovisionAndSync = 16,
} You can override the operation using the [HttpPost]
public async Task Post()
{
// Get scope name called by the client
var scopeName = HttpContext.GetScopeName();
// Get client scope id
var clientScopeId = context.GetClientScopeId();
// In a multi scopes scenario, get the correct WebServerAgent instance
var webserverAgent = webserverAgents.FirstOrDefault(c => c.ScopeName == scopeName);
webServerAgent.RemoteOrchestrator.OnGettingOperation(operationArgs=>
{
SyncOperation syncOperation = SyncOperation.Normal;
// Let's imagine you have a service class that get the operation needed
syncOperation = yourServiceClass.GetOperationForClientId(clientScopeId);
// if hard coded, can be :
// syncOperation = SyncOperation.Reinitialize;
// this operation will be applied for the current sync
operationArgs.Operation = syncOperation;
});
// Handle request
await webserverAgent.HandleRequestAsync(HttpContext).ConfigureAwait(false);
} |
Beta Was this translation helpful? Give feedback.
-
version V 0.95
Introduction
Beta nuget packages available : https://www.nuget.org/packages/Dotmim.Sync.Core/0.9.5-beta-0788
This new version
v0.95
is a major DMS update.The main purpose of this version is to allow any setup to be managed by DMS.
Beta Was this translation helpful? Give feedback.
All reactions