// Why Add & Update in one method: because of the way how we don't delete translations
        // It could be that on adding a "new" resource, it already exists as translation from a previous commit & rollback
        public void AddOrUpdateTranslationsForResourceString(RftContext ctx, ResourceString dbRes)
        {
            int branchId = dbRes.FK_BranchId;
            int resourceFileId = dbRes.FK_ResourceFileId;
            string resourceIdentifier = dbRes.ResourceIdentifier;
            string resxValue = dbRes.ResxValue;

            foreach (var l in _languages)
            {
                string culture = l.Culture;

                var currentTranslation = ctx.Translations
                    .FirstOrDefault(t => t.FK_ResourceFileId == resourceFileId &&
                            t.ResourceIdentifier == resourceIdentifier &&
                            t.Culture == culture &&
                            t.FK_BranchId == branchId);

                int excludeTranslationId = -1;
                if (currentTranslation != null)
                {
                    // we are reviving an existing translation for this resource string
                    excludeTranslationId = currentTranslation.Id;
                }
                else
                {
                    currentTranslation = new Translation()
                    {
                        FK_BranchId = branchId,
                        FK_ResourceFileId = resourceFileId,
                        ResourceIdentifier = resourceIdentifier,
                        Culture = culture,
                        OriginalResxValueAtTranslation = resxValue,
                        LastUpdated = DateTime.UtcNow,
                        LastUpdatedBy = "sync task"
                    };
                    ctx.Translations.Add(currentTranslation);
                }

                var mergeFromTranslation = ctx.Translations
                          .Where(t => t.ResourceIdentifier == resourceIdentifier &&
                                  t.OriginalResxValueAtTranslation == resxValue &&    // NOTE: this is SQL Server comparing here!!!
                                  t.Culture == culture)
                          .OrderByDescending(t => t.LastUpdated)
                          .FirstOrDefault();
                // Note: mergeFromTranslation may be equal to currentTranslation when reviving a translation

                if (null == mergeFromTranslation)
                {
                    if (-1 == excludeTranslationId)
                    {
                        currentTranslation.TranslatedValue = null;
                    }
                    currentTranslation.OriginalResxValueChangedSinceTranslation = true;
                }
                else
                {
                    currentTranslation.OriginalResxValueChangedSinceTranslation = false;
                    currentTranslation.TranslatedValue = mergeFromTranslation.TranslatedValue;
                    currentTranslation.LastUpdated = mergeFromTranslation.LastUpdated;
                    currentTranslation.LastUpdatedBy = mergeFromTranslation.LastUpdatedBy;
                }
            }
        }
        public bool ProcessResourceFile(int branchId, int resourceFileId, List<ResxStringResource> stringResources)
        {
            using (var ctx = GetContext())  // Context is isolated per branch resource file
            {
                // Get all already in-db string resources in one call
                var dbStringResources = ctx.ResourceStrings
                    .Where(rs => rs.FK_BranchId == branchId && rs.FK_ResourceFileId == resourceFileId)
                    .ToDictionary(rs => rs.ResourceIdentifier);

                Trace.TraceInformation("# of existing db string resources: {0}", dbStringResources.Count);
                Trace.TraceInformation("# of resx string resources to process: {0}", stringResources.Count);
                int newlyAdded = 0, updated = 0;

                foreach (var res in stringResources)
                {
                    ResourceString dbRes;
                    if (!dbStringResources.TryGetValue(res.Name, out dbRes))
                    {
                        // newly add string resource that is being processed
                        dbRes = new ResourceString()
                        {
                            FK_BranchId = branchId,
                            FK_ResourceFileId = resourceFileId,
                            ResourceIdentifier = res.Name,
                            ResxComment = res.Comment,
                            ResxValue = res.Value,
                            LastUpdatedFromRepository = DateTime.UtcNow,
                        };
                        ctx.ResourceStrings.Add(dbRes);
                        newlyAdded++;

                        AddOrUpdateTranslationsForResourceString(ctx, dbRes);
                    }
                    else
                    {
                        if (dbRes.ResxComment != res.Comment)
                        {
                            dbRes.ResxComment = res.Comment;
                            dbRes.LastUpdatedFromRepository = DateTime.UtcNow;
                        }

                        if (dbRes.ResxValue != res.Value)
                        {
                            dbRes.ResxValue = res.Value;
                            dbRes.LastUpdatedFromRepository = DateTime.UtcNow;  // yes, potentially set twice w above comment section
                            updated++;

                            AddOrUpdateTranslationsForResourceString(ctx, dbRes);
                        }
                    }
                }

                // Delete string resources that no longer exist
                var resourceIdentifiers = new HashSet<string>(stringResources.Select(s => s.Name));
                var toRemoveFromDb = dbStringResources.Values
                    .Where(s => !resourceIdentifiers.Contains(s.ResourceIdentifier))
                    .ToList();

                Trace.TraceInformation("# of resources added to db: {0}", newlyAdded);
                Trace.TraceInformation("# of resources updated in db: {0}", updated);
                Trace.TraceInformation("# of db resource strings to remove: {0}", toRemoveFromDb.Count);

                ctx.ResourceStrings.RemoveRange(toRemoveFromDb);
                ctx.SaveChanges();
            }

            return true;
        }