public virtual async Task CallSyncTo(DtoSetSyncConfig[] toServerDtoSetSyncMaterials, CancellationToken cancellationToken) { if (toServerDtoSetSyncMaterials.Any()) { using (TDbContext offlineContextForSyncTo = _dbContextProvider()) { ((IsSyncDbContext)offlineContextForSyncTo).IsSyncDbContext = true; ODataBatch onlineBatchContext = _oDataBatchProvider(); foreach (DtoSetSyncConfig toServerSyncConfig in toServerDtoSetSyncMaterials) { IQueryable <ISyncableDto> offlineSet = toServerSyncConfig.OfflineDtoSet(offlineContextForSyncTo); ISyncableDto[] recentlyChangedOfflineDtos = (await offlineSet.IgnoreQueryFilters().Where(s => EF.Property <bool>(s, "IsSynced") == false).AsNoTracking().ToArrayAsync(cancellationToken).ConfigureAwait(false)) .Cast <ISyncableDto>() .ToArray(); ISyncableDto firstRecentlyChangedOfflineDto = recentlyChangedOfflineDtos.FirstOrDefault(); if (firstRecentlyChangedOfflineDto == null) { continue; } TypeInfo dtoType = firstRecentlyChangedOfflineDto.GetType().GetTypeInfo(); PropertyInfo[] keyProps = offlineContextForSyncTo .Model .FindEntityType(dtoType) .FindPrimaryKey() .Properties .Select(x => dtoType.GetProperty(x.Name)) .ToArray(); foreach (ISyncableDto recentlyChangedOfflineDto in recentlyChangedOfflineDtos) { object[] keys = keyProps.Select(p => p.GetValue(recentlyChangedOfflineDto, null)).ToArray(); if (recentlyChangedOfflineDto.IsArchived == true) { onlineBatchContext += c => toServerSyncConfig.OnlineDtoSet(c).Key(keys).DeleteEntryAsync(); } else if (recentlyChangedOfflineDto.Version == 0) { onlineBatchContext += c => toServerSyncConfig.OnlineDtoSet(c).Set(recentlyChangedOfflineDto).InsertEntryAsync(); } else { onlineBatchContext += c => toServerSyncConfig.OnlineDtoSet(c).Key(keys).Set(recentlyChangedOfflineDto).UpdateEntryAsync(); } } } await onlineBatchContext.ExecuteAsync(cancellationToken).ConfigureAwait(false); } } }
public async Task SyncDtoSets(params string[] dtoSetNames) { if (dtoSetNames == null) { throw new ArgumentNullException(nameof(dtoSetNames)); } if (_connectivity.IsConnected == false) { return; } DtoSetSyncConfig[] toServerDtoSetSyncMaterials = _configs.Where(c => c.ToServerSync == true && c.ToServerSyncFunc() == true && dtoSetNames.Any(n => n == c.DtoSetName)).ToArray(); DtoSetSyncConfig[] fromServerDtoSetSyncMaterials = _configs.Where(c => c.FromServerSync == true && c.FromServerSyncFunc() == true && dtoSetNames.Any(n => n == c.DtoSetName)).ToArray(); if (toServerDtoSetSyncMaterials.Any()) { using (TDbContext offlineContextForSyncTo = _containerProvider.Resolve <TDbContext>()) { ((IsSyncDbContext)offlineContextForSyncTo).IsSyncDbContext = true; ODataBatch onlineBatchContext = _containerProvider.Resolve <ODataBatch>(); foreach (DtoSetSyncConfig toServerSyncConfig in toServerDtoSetSyncMaterials) { IQueryable <ISyncableDto> offlineSet = toServerSyncConfig.OfflineDtoSet(offlineContextForSyncTo); ISyncableDto[] recentlyChangedOfflineDtos = (await offlineSet.IgnoreQueryFilters().Where(s => EF.Property <bool>(s, "IsSynced") == false).AsNoTracking().ToArrayAsync().ConfigureAwait(false)) .Cast <ISyncableDto>() .ToArray(); ISyncableDto firstRecentlyChangedOfflineDto = recentlyChangedOfflineDtos.FirstOrDefault(); if (firstRecentlyChangedOfflineDto == null) { continue; } TypeInfo dtoType = firstRecentlyChangedOfflineDto.GetType().GetTypeInfo(); PropertyInfo[] keyProps = offlineContextForSyncTo .Model .FindEntityType(dtoType) .FindPrimaryKey() .Properties .Select(x => dtoType.GetProperty(x.Name)) .ToArray(); foreach (ISyncableDto recentlyChangedOfflineDto in recentlyChangedOfflineDtos) { object[] keys = keyProps.Select(p => p.GetValue(recentlyChangedOfflineDto, null)).ToArray(); if (recentlyChangedOfflineDto.IsArchived == true) { onlineBatchContext += c => toServerSyncConfig.OnlineDtoSet(c).Key(keys).DeleteEntryAsync(); } else if (recentlyChangedOfflineDto.Version == 0) { onlineBatchContext += c => toServerSyncConfig.OnlineDtoSet(c).Set(recentlyChangedOfflineDto).InsertEntryAsync(); } else { onlineBatchContext += c => toServerSyncConfig.OnlineDtoSet(c).Key(keys).Set(recentlyChangedOfflineDto).UpdateEntryAsync(); } } } await onlineBatchContext.ExecuteAsync().ConfigureAwait(false); } } if (fromServerDtoSetSyncMaterials.Any()) { using (TDbContext offlineContextForSyncFrom = _containerProvider.Resolve <TDbContext>()) { ((IsSyncDbContext)offlineContextForSyncFrom).IsSyncDbContext = true; ODataBatch onlineBatchContext = _containerProvider.Resolve <ODataBatch>(); List <DtoSyncConfigSyncFromResults> recentlyChangedOnlineDtos = new List <DtoSyncConfigSyncFromResults>(); foreach (DtoSetSyncConfig fromServerSyncConfig in fromServerDtoSetSyncMaterials) { IQueryable <ISyncableDto> offlineSet = fromServerSyncConfig.OfflineDtoSet(offlineContextForSyncFrom); var mostRecentOfflineDto = await offlineSet .IgnoreQueryFilters() .Select(e => new { e.Version }) .OrderByDescending(e => e.Version) .FirstOrDefaultAsync() .ConfigureAwait(false); long maxVersion = mostRecentOfflineDto?.Version ?? 0; onlineBatchContext += async c => recentlyChangedOnlineDtos.Add(new DtoSyncConfigSyncFromResults { DtoSetSyncConfig = fromServerSyncConfig, RecentlyChangedOnlineDtos = CreateSyncableDtoInstancesFromUnTypedODataResponse(offlineSet.ElementType.GetTypeInfo(), (await(fromServerSyncConfig.OnlineDtoSetForGet ?? fromServerSyncConfig.OnlineDtoSet)(c).Where($"Version gt {maxVersion}").FindEntriesAsync().ConfigureAwait(false)).ToList()), DtoType = offlineSet.ElementType.GetTypeInfo(), HadOfflineDtoBefore = mostRecentOfflineDto != null }); } await onlineBatchContext.ExecuteAsync().ConfigureAwait(false); foreach (DtoSyncConfigSyncFromResults result in recentlyChangedOnlineDtos.Where(r => r.RecentlyChangedOnlineDtos.Any())) { if (result.HadOfflineDtoBefore == false) { await offlineContextForSyncFrom.AddRangeAsync(result.RecentlyChangedOnlineDtos).ConfigureAwait(false); foreach (ISyncableDto r in result.RecentlyChangedOnlineDtos) { offlineContextForSyncFrom.Entry(r).Property("IsSynced").CurrentValue = true; } } else { PropertyInfo[] keyProps = offlineContextForSyncFrom .Model .FindEntityType(result.DtoType) .FindPrimaryKey() .Properties.Select(x => result.DtoType.GetProperty(x.Name)) .ToArray(); IQueryable <ISyncableDto> offlineSet = result.DtoSetSyncConfig.OfflineDtoSet(offlineContextForSyncFrom); string equivalentOfflineDtosQuery = ""; List <object> equivalentOfflineDtosParams = new List <object>(); int parameterIndex = 0; equivalentOfflineDtosQuery = string.Join(" || ", result.RecentlyChangedOnlineDtos.Select(s => { return($" ( {string.Join(" && ", keyProps.Select(k => { equivalentOfflineDtosParams.Add(k.GetValue(s)); return $"{k.Name} == @{parameterIndex++}"; }))} )"); })); List <ISyncableDto> equivalentOfflineDtos = await offlineSet.Where(equivalentOfflineDtosQuery, equivalentOfflineDtosParams.ToArray()).IgnoreQueryFilters().ToListAsync().ConfigureAwait(false); foreach (ISyncableDto recentlyChangedOnlineDto in result.RecentlyChangedOnlineDtos) { bool hasEquivalentInOfflineDb = equivalentOfflineDtos.Any(d => keyProps.All(k => k.GetValue(d).Equals(k.GetValue(recentlyChangedOnlineDto)))); if (recentlyChangedOnlineDto.IsArchived == false || hasEquivalentInOfflineDb == true) { if (recentlyChangedOnlineDto.IsArchived == true) { offlineContextForSyncFrom.Remove(recentlyChangedOnlineDto); } else if (hasEquivalentInOfflineDb == true) { offlineContextForSyncFrom.Update(recentlyChangedOnlineDto); offlineContextForSyncFrom.Entry(recentlyChangedOnlineDto).Property("IsSynced").CurrentValue = true; } else { await offlineContextForSyncFrom.AddAsync(recentlyChangedOnlineDto).ConfigureAwait(false); offlineContextForSyncFrom.Entry(recentlyChangedOnlineDto).Property("IsSynced").CurrentValue = true; } } } } } await offlineContextForSyncFrom.SaveChangesAsync().ConfigureAwait(false); } } }