示例#1
0
        /// <summary>
        /// 反序列化的核心实现,反序列化字符串
        /// </summary>
        /// <param name="str"></param>
        private void Deserialize(string str)
        {
            var keyValue = CoinConfigurationSerializer.Deserialize(str);

            UpdateMemoryValuesFromExternalValues(KeyValues, OriginalKeyValues, keyValue);

            OriginalKeyValues = keyValue;
        }
示例#2
0
        /// <summary>
        /// <para/> ```fkv
        /// <para/> > 凡是 “>” 开头的行都是分隔符,如果后面有内容,将被忽略。
        /// <para/> > 于是这就可以写注释用以说明其含义
        /// <para/> > 多行的注释只需要打多行 “>” 即可
        /// <para/> key0
        /// <para/> value0
        /// <para/> >
        /// <para/> key1
        /// <para/> ?>value1
        /// <para/> ??value1
        /// <para/> > key 一定只有一行,在遇到下一个 “>” 之前,都是 value
        /// <para/> > key/value 存储时,每行一定不会 “>” 开头,如果遇到,则转义为 “?>”,原来的 “?” 转义为 “??”
        /// <para/> > 转义仅发生在行首。
        /// <para/> > 遇到空行,则依然识别为 key,或者 value 的一部分
        /// <para/>
        /// <para/> value
        /// <para/>
        /// <para/> > 以上 key 为空字符串,value 为 包含空行的 value(一般禁止写入空字符串作为 key)
        /// <para/> > 配置文件末尾不包含空行(因为这会识别为 value 的一部分)
        /// <para/> key0
        /// <para/> value9
        /// <para/> > 如果存在相同的 key,则处于文件后面的会覆盖文件前面的值。
        /// <para/> ```
        /// </summary>
        /// <returns></returns>
        private async Task Serialize()
        {
            // 重写尝试 10 次
            Exception?exception       = null;
            const int retryWriteCount = 10;

            for (var i = 0; i < retryWriteCount; i++)
            {
                try
                {
                    // 如果文件夹不存在,则创建一个新的。
                    var directory = _file.Directory;
                    if (directory != null && !Directory.Exists(directory.FullName))
                    {
                        directory.Create();
                    }

                    try
                    {
                        if (_watcher != null)
                        {
                            await _watcher.StopAsync().ConfigureAwait(false);
                        }

                        // 在每次尝试写入到文件之前都将内存中的键值对序列化一次,避免过多的等待导致写入的数据过旧。
                        var text = CoinConfigurationSerializer.Serialize(KeyValues);

                        // 将所有的配置写入文件。
                        using (var fileStream = File.Open(_file.FullName, FileMode.Create, FileAccess.Write))
                        {
                            using var stream = new StreamWriter(fileStream, Encoding.UTF8);
                            await stream.WriteAsync(text).ConfigureAwait(false);
                        }

                        OriginalKeyValues = new Dictionary <string, string>(KeyValues);
                    }
                    finally
                    {
                        if (_watcher != null)
                        {
                            await _watcher.WatchAsync().ConfigureAwait(false);
                        }
                    }

                    return;
                }
#pragma warning disable CA1031 // Do not catch general exception types
                catch (Exception ex)
#pragma warning restore CA1031 // Do not catch general exception types
                {
                    // 如果这里吞掉了所有的异常,那么将没有任何途径可以得知为什么存储会失败。
                    exception = exception ?? ex;
                    Trace.WriteLine(exception);
                }

                // 在每次失败重试的时候,都需要等待指定的保存延迟时间。
                await Task.Delay(DelaySaveTime).ConfigureAwait(false);
            }

            // 记录保存失败时的异常,并抛出。
            if (exception != null)
            {
                ExceptionDispatchInfo.Capture(exception).Throw();
            }
        }
        public FileConfigurationRepo(string fileName)
        {
            if (fileName == null)
            {
                throw new ArgumentNullException(nameof(fileName));
            }

            var fullPath = Path.GetFullPath(fileName);

            _file                 = new FileInfo(fullPath);
            _saveLoop             = new PartialAwaitableRetry(LoopSyncTask);
            _keyValueSynchronizer = new FileDictionarySynchronizer <string, string>(_file,
#pragma warning disable CS8620 // 由于引用类型的可为 null 性差异,实参不能用于形参。
                                                                                    x => CoinConfigurationSerializer.Serialize(x),
#pragma warning restore CS8620 // 由于引用类型的可为 null 性差异,实参不能用于形参。
                                                                                    x => CoinConfigurationSerializer.Deserialize(x),
                               // 因为 COIN 格式的序列化器默认会写“文件头”,导致即使是构造函数也会和原始文件内容不同,于是会写入文件,导致写入次数比预期多一些。
                               // 所以,比较差异时使用 KeyValueEquals 而不是 WholeTextEquals,这可以在目前对注释不敏感的时候提升一些性能。
                                                                                    FileEqualsComparison.KeyValueEquals);

            // 监视文件改变。
            _watcher = new FileWatcher(_file);
            _currentReadingFileTask = FastSynchronizeAsync();
            _watcher.Changed       += OnFileChanged;
            _ = _watcher.WatchAsync();
        }