protected virtual async Task BuildRetrieveDataTask(DtoSyncConfigSyncFromInformation dtoSyncConfigSyncFromInformation, CancellationToken cancellationToken)
        {
            if (dtoSyncConfigSyncFromInformation == null)
            {
                throw new ArgumentNullException(nameof(dtoSyncConfigSyncFromInformation));
            }

            try
            {
                IBoundClient <IDictionary <string, object> > query = (dtoSyncConfigSyncFromInformation.DtoSetSyncConfig.OnlineDtoSetForGet ?? dtoSyncConfigSyncFromInformation.DtoSetSyncConfig.OnlineDtoSet)(ODataClient);

                if (dtoSyncConfigSyncFromInformation.MaxVersion == 0)
                {
                    query = query.Where($"{nameof(ISyncableDto.IsArchived)} eq false");
                }
                else
                {
                    query = query.Where($"{nameof(ISyncableDto.Version)} gt {dtoSyncConfigSyncFromInformation.MaxVersion}");
                }

                string oDataGetAndVersionFilter = await query
                                                  .GetCommandTextAsync(cancellationToken)
                                                  .ConfigureAwait(false);

                string oDataUri = $"{ClientAppProfile.ODataRoute}{oDataGetAndVersionFilter}";

                using (HttpResponseMessage response = await HttpClient.GetAsync(oDataUri, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false))
                {
                    response.EnsureSuccessStatusCode();

#if DotNetStandard2_0 || UWP
                    using Stream stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
#elif Android || iOS || DotNetStandard2_1
                    await using Stream stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
#else
                    await using Stream stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
#endif
                    {
                        using (StreamReader reader = new StreamReader(stream))
                        {
                            using (JsonReader jsonReader = new JsonTextReader(reader))
                            {
                                JToken jToken = await JToken.LoadAsync(jsonReader, new JsonLoadSettings
                                {
                                    CommentHandling  = CommentHandling.Ignore,
                                    LineInfoHandling = LineInfoHandling.Ignore
                                }, cancellationToken).ConfigureAwait(false);

                                dtoSyncConfigSyncFromInformation.RecentlyChangedOnlineDtos = ((IEnumerable)(jToken)["value"] !.ToObject(typeof(List <>).MakeGenericType(dtoSyncConfigSyncFromInformation.DtoType)) !).Cast <ISyncableDto>().ToArray();
                            }
                        }
                    }
                }
            }
            catch (Exception exp)
            {
                dtoSyncConfigSyncFromInformation.RecentlyChangedOnlineDtos = Array.Empty <ISyncableDto>();
                ExceptionHandler.OnExceptionReceived(exp);
            }
        }
Example #2
0
        public virtual async Task CallSyncFrom(DtoSetSyncConfig[] fromServerDtoSetSyncMaterials, CancellationToken cancellationToken)
        {
            if (fromServerDtoSetSyncMaterials == null)
            {
                throw new ArgumentNullException(nameof(fromServerDtoSetSyncMaterials));
            }

            if (fromServerDtoSetSyncMaterials.Any())
            {
                await GetMetadataIfNotRetrievedAlready(cancellationToken).ConfigureAwait(false);

                await using (EfCoreDbContextBase offlineContextForSyncFrom = Container.Resolve <EfCoreDbContextBase>())
                {
                    ((IsSyncDbContext)offlineContextForSyncFrom).IsSyncDbContext = true;

                    List <DtoSyncConfigSyncFromInformation> dtoSyncConfigSyncFromInformationList = new List <DtoSyncConfigSyncFromInformation>();

                    int id = 0;

                    foreach (DtoSetSyncConfig fromServerSyncConfig in fromServerDtoSetSyncMaterials)
                    {
                        IQueryable <ISyncableDto> offlineSet = fromServerSyncConfig.OfflineDtoSet(offlineContextForSyncFrom);

                        var mostRecentOfflineDto = await offlineSet
                                                   .IgnoreQueryFilters()
                                                   .AsNoTracking()
                                                   .Select(e => new { e.Version })
                                                   .OrderByDescending(e => e.Version)
                                                   .FirstOrDefaultAsync(cancellationToken)
                                                   .ConfigureAwait(false);

                        long maxVersion = mostRecentOfflineDto?.Version ?? 0;

                        DtoSyncConfigSyncFromInformation dtoSyncConfigSyncFromInformation = new DtoSyncConfigSyncFromInformation
                        {
                            Id = id++,
                            DtoSetSyncConfig    = fromServerSyncConfig,
                            DtoType             = offlineSet.ElementType.GetTypeInfo(),
                            HadOfflineDtoBefore = mostRecentOfflineDto != null,
                            MaxVersion          = maxVersion
                        };

                        IBoundClient <IDictionary <string, object> > query = (dtoSyncConfigSyncFromInformation.DtoSetSyncConfig.OnlineDtoSetForGet ?? dtoSyncConfigSyncFromInformation.DtoSetSyncConfig.OnlineDtoSet)(ODataClient);

                        if (dtoSyncConfigSyncFromInformation.MaxVersion == 0)
                        {
                            query = query.Where($"{nameof(ISyncableDto.IsArchived)} eq false");
                        }
                        else
                        {
                            query = query.Where($"{nameof(ISyncableDto.Version)} gt {dtoSyncConfigSyncFromInformation.MaxVersion}");
                        }

                        string oDataGetAndVersionFilter = await query
                                                          .GetCommandTextAsync(cancellationToken)
                                                          .ConfigureAwait(false);

                        dtoSyncConfigSyncFromInformation.ODataGetUri = $"{ClientAppProfile.HostUri}{ClientAppProfile.ODataRoute}{oDataGetAndVersionFilter}";

                        dtoSyncConfigSyncFromInformationList.Add(dtoSyncConfigSyncFromInformation);
                    }

                    StringBuilder batchRequests = new StringBuilder();

                    batchRequests.AppendLine(@"
{
    ""requests"": [");

                    foreach (DtoSyncConfigSyncFromInformation?dtoSyncConfigSyncFromInformation in dtoSyncConfigSyncFromInformationList)
                    {
                        batchRequests.AppendLine(@$ "
        {{
            " "id" ": " "{dtoSyncConfigSyncFromInformation.Id}" ",
            " "method" ": " "GET" ",
            " "url" ": " "{dtoSyncConfigSyncFromInformation.ODataGetUri}" "
        }}{(dtoSyncConfigSyncFromInformation != dtoSyncConfigSyncFromInformationList.Last() ? ", " : " ")}");
                    }

                    batchRequests.AppendLine(@"
                  ]
}");

                    using (HttpResponseMessage batchResponbse = await HttpClient.PostAsync($"{ClientAppProfile.ODataRoute}$batch", new StringContent(batchRequests.ToString(), Encoding.UTF8, "application/json"), cancellationToken).ConfigureAwait(false))
                    {
                        batchResponbse.EnsureSuccessStatusCode();

                        if (batchResponbse.Content.Headers.ContentType.MediaType != "application/json")
                        {
                            throw new InvalidOperationException($"{batchResponbse.Content.Headers.ContentType.MediaType} content type is not supported.");
                        }

#if UWP || DotNetStandard2_0
                        using (Stream stream = await batchResponbse.Content.ReadAsStreamAsync().ConfigureAwait(false))
#else
                        await using (Stream stream = await batchResponbse.Content.ReadAsStreamAsync().ConfigureAwait(false))
#endif
                        {
                            using (StreamReader reader = new StreamReader(stream))
                            {
                                using (JsonReader jsonReader = new JsonTextReader(reader))
                                {
                                    JToken jToken = await JToken.LoadAsync(jsonReader, new JsonLoadSettings
                                    {
                                        CommentHandling  = CommentHandling.Ignore,
                                        LineInfoHandling = LineInfoHandling.Ignore
                                    }, cancellationToken).ConfigureAwait(false);

                                    foreach (JToken response in jToken["responses"] !)
                                    {
                                        int responseId = response.Value <int>("id");

                                        DtoSyncConfigSyncFromInformation?dtoSyncConfigSyncFromInformation = dtoSyncConfigSyncFromInformationList.ExtendedSingle($"Getting dtoSyncConfigSyncFromInformation with id {responseId}", item => item.Id == responseId);

                                        dtoSyncConfigSyncFromInformation.RecentlyChangedOnlineDtos = ((IEnumerable)response["body"] !["value"] !.ToObject(typeof(List <>).MakeGenericType(dtoSyncConfigSyncFromInformation.DtoType)) !).Cast <ISyncableDto>().ToArray();
        public virtual async Task CallSyncFrom(DtoSetSyncConfig[] fromServerDtoSetSyncMaterials, CancellationToken cancellationToken)
        {
            if (fromServerDtoSetSyncMaterials == null)
            {
                throw new ArgumentNullException(nameof(fromServerDtoSetSyncMaterials));
            }

            if (fromServerDtoSetSyncMaterials.Any())
            {
                await GetMetadataIfNotRetrievedAlready(cancellationToken).ConfigureAwait(false);

                await using (EfCoreDbContextBase offlineContextForSyncFrom = Container.Resolve <EfCoreDbContextBase>())
                {
                    ((IsSyncDbContext)offlineContextForSyncFrom).IsSyncDbContext = true;

                    List <DtoSyncConfigSyncFromInformation> dtoSyncConfigSyncFromInformationList = new List <DtoSyncConfigSyncFromInformation>();

                    foreach (DtoSetSyncConfig fromServerSyncConfig in fromServerDtoSetSyncMaterials)
                    {
                        IQueryable <ISyncableDto> offlineSet = fromServerSyncConfig.OfflineDtoSet(offlineContextForSyncFrom);

                        var mostRecentOfflineDto = await offlineSet
                                                   .IgnoreQueryFilters()
                                                   .AsNoTracking()
                                                   .Select(e => new { e.Version })
                                                   .OrderByDescending(e => e.Version)
                                                   .FirstOrDefaultAsync(cancellationToken)
                                                   .ConfigureAwait(false);

                        long maxVersion = mostRecentOfflineDto?.Version ?? 0;

                        DtoSyncConfigSyncFromInformation dtoSyncConfigSyncFromInformation = new DtoSyncConfigSyncFromInformation
                        {
                            DtoSetSyncConfig    = fromServerSyncConfig,
                            DtoType             = offlineSet.ElementType.GetTypeInfo(),
                            HadOfflineDtoBefore = mostRecentOfflineDto != null,
                            MaxVersion          = maxVersion
                        };

                        dtoSyncConfigSyncFromInformation.RetrieveDataTask = BuildRetrieveDataTask(dtoSyncConfigSyncFromInformation, cancellationToken);

                        dtoSyncConfigSyncFromInformationList.Add(dtoSyncConfigSyncFromInformation);
                    }

                    await Task.WhenAll(dtoSyncConfigSyncFromInformationList.Select(r => r.RetrieveDataTask)).ConfigureAwait(false);

                    foreach (DtoSyncConfigSyncFromInformation dtoSyncConfigSyncFromInformation in dtoSyncConfigSyncFromInformationList.Where(r => r.RecentlyChangedOnlineDtos.Any()))
                    {
                        if (dtoSyncConfigSyncFromInformation.HadOfflineDtoBefore == false)
                        {
                            foreach (ISyncableDto r in dtoSyncConfigSyncFromInformation.RecentlyChangedOnlineDtos)
                            {
                                offlineContextForSyncFrom.Add(r).Property("IsSynced").CurrentValue = true;
                            }
                        }
                        else
                        {
                            PropertyInfo[] keyProps = offlineContextForSyncFrom
                                                      .Model
                                                      .FindEntityType(dtoSyncConfigSyncFromInformation.DtoType)
                                                      .FindPrimaryKey()
                                                      .Properties.Select(x => dtoSyncConfigSyncFromInformation.DtoType.GetProperty(x.Name))
                                                      .ToArray() !;

                            IQueryable <ISyncableDto> offlineSet = dtoSyncConfigSyncFromInformation.DtoSetSyncConfig.OfflineDtoSet(offlineContextForSyncFrom);

                            string        equivalentOfflineDtosQuery  = "";
                            List <object> equivalentOfflineDtosParams = new List <object>();
                            int           parameterIndex = 0;

                            equivalentOfflineDtosQuery = string.Join(" || ", dtoSyncConfigSyncFromInformation.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()
                                                                        .AsNoTracking()
                                                                        .ToListAsync(cancellationToken)
                                                                        .ConfigureAwait(false);

                            foreach (ISyncableDto recentlyChangedOnlineDto in dtoSyncConfigSyncFromInformation.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).Property("IsSynced").CurrentValue = true;
                                    }
                                    else
                                    {
                                        offlineContextForSyncFrom.Add(recentlyChangedOnlineDto).Property("IsSynced").CurrentValue = true;
                                    }
                                }
                            }
                        }
                    }

                    await offlineContextForSyncFrom.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
                }
            }
        }