Beispiel #1
0
        /// <summary>
        /// 将为指定的 Key 清除。
        /// </summary>
        /// <param name="key">指定项的 Key。</param>
        protected override async Task RemoveValueCoreAsync(string key)
        {
            await LoadFromFileTask.ConfigureAwait(false);

            CT.Debug($"{key} = null", "Set");
            KeyValues.TryRemove(key, out _);
        }
Beispiel #2
0
        /// <summary>
        /// 为指定的 Key 存储指定的值。
        /// </summary>
        /// <param name="key">指定项的 Key。</param>
        /// <param name="value">要存储的值。</param>
        protected override async Task WriteValueCoreAsync(string key, string value)
        {
            value = value ?? throw new ArgumentNullException(nameof(value));
            value = value.Replace(Environment.NewLine, "\n");
            await LoadFromFileTask.ConfigureAwait(false);

            CT.Debug($"{key} = {value}", "Set");
            KeyValues[key] = value;
        }
Beispiel #3
0
        /// <summary>
        /// 获取指定 Key 的值,如果不存在,需要返回 null。
        /// </summary>
        /// <param name="key">指定项的 Key。</param>
        /// <returns>
        /// 执行项的 Key,如果不存在,则为 null / Task&lt;string&gt;.FromResult(null)"/>。
        /// </returns>
        protected override async Task <string?> ReadValueCoreAsync(string key)
        {
            await LoadFromFileTask.ConfigureAwait(false);

            var value = KeyValues.TryGetValue(key, out var v) ? v : null;

            CT.Debug($"{key} = {value ?? "null"}", "Get");
            return(value);
        }
Beispiel #4
0
        /// <summary>
        /// 反序列化文件
        /// </summary>
        /// <param name="file"></param>
        /// <returns></returns>
        private async Task DeserializeFile(FileInfo file)
        {
            CT.Debug($"读取 {file.FullName}", "File");

            if (!File.Exists(file.FullName))
            {
                UpdateMemoryValuesFromExternalValues(KeyValues, OriginalKeyValues, new Dictionary <string, string>());
                OriginalKeyValues.Clear();
                return;
            }

            const int retryCount = 100;

            for (var i = 0; i < retryCount; i++)
            {
                try
                {
                    // 一次性读取完的性能最好
                    var str = File.ReadAllText(file.FullName);
                    Deserialize(str);
                    _lastDeserializeTime = DateTimeOffset.Now;
                    return;
                }
                catch (IOException)
                {
                    const int waitTime = 10;
                    // 读取配置文件出现异常,忽略所有异常
                    await Task.Delay(waitTime).ConfigureAwait(false);

                    // 通过测试发现在我的设备,写入平均时间是 6 毫秒,也就是如果存在多实例写入,也不会是 waitTime*retryCount 毫秒这么久,等待 waitTime*retryCount 毫秒也是可以接受最大的值
                }
#pragma warning disable CA1031 // Do not catch general exception types
                catch
#pragma warning restore CA1031 // Do not catch general exception types
                {
                    // 这里的代码因为从一个 IO 线程调进来,所以当调用方使用 await 等待,会使得这里抛出的异常回到 IO 线程,导致应用程序崩溃。
                    // 这里可能的异常有:
                    //   - UnauthorizedAccessException 在文件只读、文件实际上是一个文件夹、没有读权限或者平台不支持时引发。
                    const int waitTime = 10;
                    await Task.Delay(waitTime).ConfigureAwait(false);
                }
            }
        }
Beispiel #5
0
        /// <summary>
        /// 在配置文件改变的时候,重新读取文件。
        /// </summary>
        private async void OnFileChanged(object?sender, EventArgs e)
        {
            CT.Debug($"检测到文件被改变...", "File");
            var isPending = _isPendingReread;

            if (isPending)
            {
                // 如果发现已经在准备读取文件了,那么就告诉他又进来了一次,他可能还需要读。
                _isPendingRereadReentered = true;
                return;
            }

            _isPendingReread = true;

            try
            {
                do
                {
                    _isPendingRereadReentered = false;
                    // 等待时间为预期等待时间的 1/2,因为多数情况下,一次文件的改变会收到两次 Change 事件。
                    // 第一次是文件内容的写入,第二次是文件信息(如最近写入时间)的写入。
                    await Task.Delay((int)DelayReadTime.TotalMilliseconds / 2).ConfigureAwait(false);
                } while (_isPendingRereadReentered);

                // 如果之前正在读取文件,则等待文件读取完成。
                await LoadFromFileTask.ConfigureAwait(false);

                // 现在重新读取。
                // - ~~重新读取文件时不影响对键值对的访问,所以不要求其他地方等待 LoadFromFileTask。~~
                // - 但是,如果正在序列化和保存文件,为了避免写入时覆盖未读取完的键值对,需要等待读取完毕。
                // !特别注意!:外部写完文件后配置立刻读,读不到新值;需要调用 ReloadExternalChangesAsync 方法强制加载外部修改;否则将等待自动更新修改。
                _ = RequestReloadingFile();
            }
            finally
            {
                _isPendingReread = false;
            }
        }