/// <summary> /// 打开文件进行读写,以将内存中的键值集合同步到文件中。 /// 此方法保证跨进程/线程的安全执行。 /// </summary> /// <param name="context">用于合并文件与内存中的键值集合。</param> /// <param name="lastWriteTime">文件的上一次修改时间。</param> /// <returns>修改了文件后,新的文件修改时间(如果内容不变,则时间也不变)。</returns> private DateTimeOffset SyncWhenFileHasBeenUpdated(ICriticalReadWriteContext <TKey, TValue> context, DateTimeOffset lastWriteTime) { // 在会打开文件流的地方自增。 Interlocked.Increment(ref _fileSyncingCount); // 读取文件。 var text = ReadAllText(); _lastSyncedFileContent = text; // 将文件中的键值集合与内存中的键值集合合并。 var newText = MergeFileTextAndKeyValueText(context, lastWriteTime, text, out var updatedTime, out var hasChanged); // 将合并后的键值集合写回文件。 if (hasChanged) { WriteAllText(newText); _lastSyncedFileContent = newText; return(updatedTime); } else { return(lastWriteTime); } }
/// <summary> /// 将内存中的键值集合同步到文件中,会根据键值的修改时间来修改文件的修改时间。 /// 此方法保证跨进程/线程的安全执行。 /// </summary> /// <param name="context">用于合并文件与内存中的键值集合。</param> private void SynchronizeCore(ICriticalReadWriteContext <TKey, TValue> context) { // 获取文件的外部更新时间。 _file.Refresh(); var utcNow = DateTimeOffset.UtcNow; var lastWriteTime = _file.Exists ? FixFileTime(_file.LastWriteTimeUtc, utcNow) : utcNow; if (_file.Exists && _lastFileExists is false) { // 如果本此触发同步是因为文件新创建,那么无论此文件是新是旧,都视其为最新。 // 我们认定,如果文件上次不存在而这次存在,即使文件时间是旧的(例如用户从回收站将其恢复),我们也应该视其为最新。 lastWriteTime = utcNow; } DateTimeOffset newLastWriteTime; if (SupportsHighResolutionFileTime && lastWriteTime == _fileLastWriteTime && _file.Exists == _lastFileExists) { // 在支持高精度时间的文件系统上: // 自上次同步文件以来,文件从未发生过更改(无需提前打开文件)。 CT.Log($"准备同步时,发现文件时间未改变 {_fileLastWriteTime.LocalDateTime:O}", _file.Name, "Sync"); newLastWriteTime = SyncWhenFileHasNotBeenUpdated(context, lastWriteTime); } else { // 文件已经发生了更改。 if (_file.Exists) { if (SupportsHighResolutionFileTime) { CT.Log($"准备同步时({FileDriveFormat}),发现文件时间改变 {_fileLastWriteTime.LocalDateTime:O} -> {lastWriteTime.LocalDateTime:O}", _file.Name, "Sync"); } else { CT.Log($"准备同步时,发现文件系统({FileDriveFormat ?? "null"})不支持高精度时间,强制完全同步 {lastWriteTime.LocalDateTime:O}", _file.Name, "Sync"); } } else { CT.Log($"准备同步时,发现文件不存在", _file.Name, "Sync"); } newLastWriteTime = SyncWhenFileHasBeenUpdated(context, lastWriteTime); } if (lastWriteTime != newLastWriteTime.UtcDateTime) { CT.Log($"正在更新文件时间 {lastWriteTime.LocalDateTime:O} -> {newLastWriteTime.LocalDateTime:O}", _file.Name, "Sync"); _file.LastWriteTimeUtc = newLastWriteTime.UtcDateTime; } // 重新更新文件的信息,因为前面可能发生了更改。 _lastFileExists = _file.Exists; _fileLastWriteTime = _file.Exists ? newLastWriteTime : DateTimeOffset.MinValue; }
private string MergeFileTextAndKeyValueText( ICriticalReadWriteContext <TKey, TValue> context, DateTimeOffset lastWriteTime, string text, out DateTimeOffset updatedWriteTime, out bool hasChanged) { var externalKeyValues = _deserializer(text); var timedMerging = context.MergeExternalKeyValues(externalKeyValues, lastWriteTime); var mergedKeyValues = timedMerging.KeyValues.ToDictionary(x => x.Key, x => x.Value); var newText = _serializer(mergedKeyValues); updatedWriteTime = timedMerging.Time; if (_fileEqualsComparison == FileEqualsComparison.KeyValueEquals) { hasChanged = !((ICollection <KeyValuePair <TKey, TValue> >)externalKeyValues).SequenceEqualsIgnoringOrder(mergedKeyValues); } else { hasChanged = !string.Equals(text, newText, StringComparison.Ordinal); } CT.Log($"合并键值集合:从文件 {{ {string.Join(", ", externalKeyValues.Keys)} }} 到新 {{ {string.Join(", ", mergedKeyValues.Keys)} }}", _file.Name, "Sync"); return(newText); }