public EntitySync ForEntity <T>(SynchronizationParameters synchronizationParameters) where T : AbstractEntity, new() { Type type = typeof(T); string typeName = type.Name; EntitySync entitySync = new EntitySync { Type = type, Getter = sql => DatabaseConnection.Connection.Query <T>(sql).Cast <AbstractEntity>().ToList(), }; if (typeName != "User" && synchronizationParameters.DontUpload.Contains(typeName)) { entitySync.Up = () => entitySync.PostUp(); } else { entitySync.Up = () => UploadAsync <T>(synchronizationParameters, entitySync.PostUp); } if (synchronizationParameters.DontDownload.Contains(typeName)) { entitySync.Down = () => entitySync.PostDown(); } else { entitySync.Down = () => DownloadAsync <T>(synchronizationParameters, entitySync.PostDown); } entitySync.CreateTable = connection => connection.CreateTable <T>(); entitySync.DropTable = connection => connection.DropTable <T>(); return(entitySync); }
public RestService(SynchronizationParameters synchronizationParameters) { Server = synchronizationParameters.Server; var authData = string.Format("{0}:{1}", synchronizationParameters.Username, synchronizationParameters.Password); var authHeaderValue = Convert.ToBase64String(Encoding.UTF8.GetBytes(authData)); client = new HttpClient(); client.MaxResponseContentBufferSize = 256 * 100000; client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authHeaderValue); }
public void Synchronize(SynchronizationParameters synchronizationParameters) { NotificationService.Send(NotificationEvent.PreSynchronization); List <EntitySync> syncTables = synchronizationParameters.EntitiesInSynchronization = Setup(synchronizationParameters); Debug.Assert(syncTables.Count >= 2, "syncEntities.Count >= 2"); Debug.Assert(syncTables[0].Type == typeof(DeletedRecord), "syncEntities[0].Type == typeof(DeletedRecord)"); Debug.Assert(syncTables[1].Type == typeof(User), "syncEntities[1].Type == typeof(User)"); //synchronizationParameters.Notif((syncTables.Count >= 2) + "syncEntities.Count >= 2"); //synchronizationParameters.Notif((syncTables[0].Type == typeof(DeletedRecord)) + "syncEntities[0].Type == typeof(DeletedRecord)"); //synchronizationParameters.Notif((syncTables[1].Type == typeof(User)) + "syncEntities[1].Type == typeof(User)"); for (int i = 0; i < syncTables.Count; i++) { EntitySync sync = syncTables[i]; // Handle each PostXxx Action different for last SyncEntity if (sync == syncTables.Last()) { // After last upload start with first SyncEntity download sync.PostUp = syncTables[0].Down; // After last download invoke finalAction sync.PostDown = () => { NotificationService.Send(NotificationEvent.Synchronized); synchronizationParameters.FinalAction(); }; } else { // after up-/download start up-/download of next SyncEntity sync.PostUp = syncTables[i + 1].Up; sync.PostDown = syncTables[i + 1].Down; } } synchronizationParameters.EntitiesInSynchronization[1].PostUp = synchronizationParameters.EntitiesInSynchronization[0].Down; synchronizationParameters.Downloaded = synchronizationParameters.Uploaded = synchronizationParameters.RecordsToDelete = synchronizationParameters.RecordsDeleted = synchronizationParameters.RecordsDeletedAtServer = 0; synchronizationParameters.EntitiesInSynchronization[0].Up(); }
private LastEntitySyncTime GetLastSyncTime <T>(SynchronizationParameters synchronizationParameters) where T : AbstractEntity, new() { string entityName = typeof(T).Name; LastEntitySyncTime lastSyncObj = DatabaseConnection.Connection.Query <LastEntitySyncTime>("select * from [LastEntitySyncTime] " + "where [EntityName] = ?", new[] { entityName }).FirstOrDefault(); if (lastSyncObj == null) { List <T> entities = DatabaseConnection.Connection.Query <T>("select * from [" + entityName + "]"); DateTime lastSyncTime = entities.Count == 0 ? default(DateTime) : entities.Select(o => o.ModifiedDate).Max(); DatabaseConnection.Connection.Insert(lastSyncObj = new LastEntitySyncTime { EntityName = entityName, LastDownloadTime = lastSyncTime }); } return(lastSyncObj); }
private async void DeleteAsync(SynchronizationParameters synchronizationParameters, Action finishedNotification) { IRestService <User> restService = new RestService <User>(synchronizationParameters); try { await restService.UploadAsync(new List <ToDelete>(QueryEntities <DeletedRecord>(synchronizationParameters) .Select(d => new ToDelete { Entity = d.EntityName.Split('.').Last(), Pk = d.EntityPk })), synchronizationParameters.ExceptionHandler, "users/singleRequestDelete"); finishedNotification(); } catch (Exception exception) { synchronizationParameters.ExceptionHandler(exception); } }
private void UploadAsync <T>(SynchronizationParameters synchronizationParameters, Action finishedNotification) where T : AbstractEntity, new() { if (typeof(T) == typeof(DeletedRecord)) { //synchronizationParameters.Notif("UploadAsync(DeletedRecord)"); //DeleteAsync(synchronizationParameters, finishedNotification); finishedNotification(); //synchronizationParameters.Notif("UploadAsync(DeletedRecord) finished"); } else if (typeof(T) == typeof(User)) { //synchronizationParameters.Notif("UploadAsync(User)"); UploadAsyncSingleRequest(synchronizationParameters, finishedNotification); //synchronizationParameters.Notif("UploadAsync(User) finished"); } else { Debug.Assert(false, "Unexpected type in upload: " + typeof(T).Name); } }
private List <T> QueryEntities <T>(SynchronizationParameters synchronizationParameters) where T : AbstractEntity, new() { return(DatabaseConnection.Connection.Query <T>("select * from [" + typeof(T).Name + "] where IsPending = 1 order by ModifiedDate")); }
private async void UploadAsyncSingleRequest(SynchronizationParameters synchronizationParameters, Action finishedNotification) { Stopwatch watch = new Stopwatch(); watch.Start(); IRestService <User> restService = new RestService <User>(synchronizationParameters); IDictionary <string, object> underlyingRoot = new ExpandoObject(); bool exceptionOccured = false; List <AbstractEntity> allEntities = new List <AbstractEntity>(); int entitiesInRequest = 0; int requestCount = 0; foreach (EntitySync entity in synchronizationParameters.EntitiesInSynchronization) { string entityName = entity.Type.Name; if (synchronizationParameters.DontUpload.Contains(entityName)) { continue; } List <AbstractEntity> entities = entity.Getter("select * from " + entityName + " where IsPending=1 order by ModifiedDate"); if (entities.Count == 0) { continue; } allEntities.AddRange(entities); Dictionary <string, PropertyInfo> foreignKeys = GetForeignKeyProperties(entity.Type); var grouped = entities .GroupBy(e => foreignKeys.Count == 0 ? "" : foreignKeys.Select(f => f.Value.GetValue(e)).Aggregate((i, j) => i + "," + j)) .ToDictionary(g => { Dictionary <string, string> keys = new Dictionary <string, string>(); string[] values = g.Key.ToString().Split(','); int index = 0; foreach (KeyValuePair <string, PropertyInfo> kvp in foreignKeys) { keys.Add(kvp.Key, values[index++]); } return(keys); }, g => g); Debug.WriteLine("now " + entityName + ", until now " + allEntities.Count + " entities"); if (entitiesInRequest + entities.Count > GeneratedConstants.UploadPageSize) { List <object> l = new List <object>(); underlyingRoot.Add(restService.ListProperty(entity.Type), l); foreach (var group in grouped) { int groupCount = group.Value.Count(); List <int[]> pieces = CutIntoPieces(entitiesInRequest, groupCount); if (pieces.Count == 0) { l.Add(new { ForeignKeys = group.Key, Values = group.Value }); entitiesInRequest += group.Value.Count(); } else { foreach (int[] piece in pieces) { l.Add(new { ForeignKeys = group.Key, Values = group.Value.Skip(piece[0]).Take(piece[1]) }); await UploadAsync( restService, underlyingRoot, exception => { exceptionOccured = true; ExceptionHandler(synchronizationParameters, exception); }, requestCount ++); if (exceptionOccured) { return; } underlyingRoot = new ExpandoObject(); underlyingRoot.Add(restService.ListProperty(entity.Type), l = new List <object>()); } entitiesInRequest = pieces.Last()[1]; } } } else { underlyingRoot.Add(restService.ListProperty(entity.Type), grouped.Select(g => new { ForeignKeys = g.Key, Values = g.Value })); entitiesInRequest += entities.Count; } } if (!exceptionOccured) { await UploadAsync( restService, underlyingRoot, exception => { exceptionOccured = true; ExceptionHandler(synchronizationParameters, exception); }, requestCount ++); } if (!exceptionOccured) { Stopwatch watchUpdatePending = new Stopwatch(); watchUpdatePending.Start(); foreach (var group in allEntities.GroupBy(e => e.GetType())) { DatabaseConnection.Connection.Execute("update " + group.Key.Name + " set IsPending = 0 where Pk in (" + group.Select(e => "'" + e.Pk + "'").Aggregate((i, j) => i + "," + j) + ")"); } //foreach (AbstractEntity entity in allEntities) //{ // entity.IsPending = false; // db.Update(entity); //} Debug.WriteLine("watchUpdatePending needed: " + watchUpdatePending.Elapsed); synchronizationParameters.Uploaded = allEntities.Count; finishedNotification(); return; } }
private void ExceptionHandler(SynchronizationParameters synchronizationParameters, Exception exception) { NotificationService.Send(NotificationEvent.SynchronizationFailed); synchronizationParameters.ExceptionHandler(exception); }
private async void DownloadAsync <T>(SynchronizationParameters synchronizationParameters, Action finishedNotification) where T : AbstractEntity, new() { LastEntitySyncTime lastSyncRecord = GetLastSyncTime <T>(synchronizationParameters); RestService <T> restService = new RestService <T>(synchronizationParameters); #if DEBUG Stopwatch watch = new Stopwatch(); TimeSpan elapsed; watch.Start(); #endif try { int pageCount = 0; DownloadResult <T> downloadResult; do { downloadResult = await restService.DownloadAsync(lastSyncRecord.LastDownloadTime, pageCount, GeneratedConstants.DownloadPageSize); #if DEBUG if (GeneratedConstants.LogDebug && typeof(T).Name == "GlucoseValue") { elapsed = watch.Elapsed; watch.Restart(); Debug.WriteLine("Download " + typeof(T).Name + " page=" + pageCount + ", size=" + GeneratedConstants.DownloadPageSize + " needed " + elapsed); } #endif pageCount++; if (previousTask != null) { await previousTask; } if (downloadResult.Items.Count > 0) { previousTask = Task.Factory.StartNew(() => { Stopwatch refreshWatch = new Stopwatch(); refreshWatch.Start(); List <T> objectsForUpdateLocal = new List <T>(downloadResult.Items); if (typeof(T) == typeof(DeletedRecord) && objectsForUpdateLocal.Count > 0) { foreach (T obj in objectsForUpdateLocal) { synchronizationParameters.RecordsToDelete++; DeletedRecord deletedRecord = obj as DeletedRecord; int deletedRecords = DatabaseConnection .Connection .Execute("delete from [" + deletedRecord.EntityName + "] where [Pk] = ?", new[] { deletedRecord.EntityPk }); if (synchronizationParameters.Refresh != null && refreshWatch.Elapsed >= TimeSpan.FromSeconds(1)) { synchronizationParameters.Refresh(); refreshWatch.Restart(); } synchronizationParameters.RecordsDeleted += deletedRecords; } DatabaseConnection.Connection.Execute("delete from DeletedRecord where EntityPk in (" + objectsForUpdateLocal.Select(o => "'" + (o as DeletedRecord).EntityPk + "'").Aggregate((i, j) => i + "," + j) + ")"); if (synchronizationParameters.Refresh != null && refreshWatch.Elapsed >= TimeSpan.FromSeconds(1)) { synchronizationParameters.Refresh(); refreshWatch.Restart(); } #if DEBUG if (GeneratedConstants.LogDebug) { elapsed = watch.Elapsed; watch.Restart(); Debug.WriteLine("Performing delete actions, count=" + objectsForUpdateLocal.Count + " needed " + elapsed); } #endif } else { foreach (T obj in objectsForUpdateLocal) { try { DatabaseConnection.Connection.Insert(obj); } catch (Exception) { DatabaseConnection.Connection.GetChildren(obj); obj.IsPending = false; DatabaseConnection.Connection.Update(obj); } synchronizationParameters.Downloaded++; if (synchronizationParameters.Refresh != null && refreshWatch.Elapsed >= TimeSpan.FromSeconds(1)) { synchronizationParameters.Refresh(); refreshWatch.Restart(); } } #if DEBUG if (GeneratedConstants.LogDebug && typeof(T).Name == "GlucoseValue") { elapsed = watch.Elapsed; watch.Restart(); Debug.WriteLine("Insert/update actions, count=" + objectsForUpdateLocal.Count + " needed " + elapsed); } #endif } if (!downloadResult.FetchNextPage) { lastSyncRecord.LastDownloadTime = objectsForUpdateLocal.Last().ModifiedDate; DatabaseConnection.Connection.Update(lastSyncRecord); } }); } } while (downloadResult.FetchNextPage); finishedNotification(); } catch (Exception exception) { Debug.WriteLine(exception.StackTrace); ExceptionHandler(synchronizationParameters, exception); } finally { synchronizationParameters.Refresh?.Invoke(); } }