文件配置提供者,支持监听文件修改,然后重新加载配置,并绑定对象
大石头 authored at 2020-03-31 23:27:41
4.27 KiB
X
using System;
using System.IO;
using System.Reflection;
using System.Xml.Serialization;
using NewLife.Log;
using NewLife.Threading;

namespace NewLife.Configuration
{
    /// <summary>配置文件基类</summary>
    /// <remarks>
    /// 标准用法:TConfig.Current
    /// 
    /// 配置实体类通过<see cref="ConfigAttribute"/>特性指定配置文件路径。
    /// Current将加载配置文件,如果文件不存在或者加载失败,将实例化一个对象返回。
    /// </remarks>
    /// <typeparam name="TConfig"></typeparam>
    public class Config<TConfig> where TConfig : Config<TConfig>, new()
    {
        #region 静态
        /// <summary>当前使用的提供者</summary>
        public static IConfigProvider Provider { get; set; }

        private static Boolean _loading;
        private static TConfig _Current;
        /// <summary>当前实例。通过置空可以使其重新加载。</summary>
        public static TConfig Current
        {
            get
            {
                if (_Current != null) return _Current;

                // 如果正在加载中,需要返回默认值。因为在加载配置的过程中,可能循环触发导致再次加载配置
                var config = new TConfig();
                if (_Current == null) _Current = config;
                if (_loading) return _Current;
                _loading = true;

                try
                {
                    var flag = false;
                    var prv = Provider;
                    if (prv == null)
                    {
                        lock (typeof(Config<TConfig>))
                        {
                            if (prv == null)
                            {
                                // 创建提供者
                                var att = typeof(TConfig).GetCustomAttribute<ConfigAttribute>(true);
                                prv = ConfigProvider.Create(att?.Provider);

                                if (prv is ConfigProvider prv2)
                                {
                                    var value = att?.Name;
                                    if (value.IsNullOrEmpty())
                                    {
                                        value = typeof(TConfig).Name;
                                        if (value.EndsWith("Config") && value != "Config") value = value.TrimEnd("Config");
                                        if (value.EndsWith("Setting") && value != "Setting") value = value.TrimEnd("Setting");
                                    }

                                    prv2.Init(value);
                                }

                                Provider = prv;
                                flag = true;
                            }
                        }
                    }

                    if (!flag) return _Current;

                    // 加载配置数据到提供者
                    if (!prv.LoadAll())
                    {
                        XTrace.WriteLine("初始化{0}的配置 {1}", typeof(TConfig).FullName, prv);
                        //prv.Save(config);
                    }

                    // 绑定提供者数据到配置对象
                    prv.Bind(config, true);

                    config.OnLoaded();

                    // OnLoad 中可能有变化,存回去
                    prv.Save(config);

                    return _Current = config;
                }
                finally { _loading = false; }
            }
            set { _Current = value; }
        }
        #endregion

        #region 属性
        /// <summary>是否新的配置文件</summary>
        [XmlIgnore]
        //[Obsolete("=>_Provider.IsNew")]
        public Boolean IsNew => Provider is FileConfigProvider fprv && fprv.IsNew;
        #endregion

        #region 成员方法
        /// <summary>从配置文件中读取完成后触发</summary>
        protected virtual void OnLoaded() { }

        /// <summary>保存到配置文件中去</summary>
        //[Obsolete("=>Provider.Save")]
        public virtual void Save() => Provider.Save(this);

        /// <summary>异步保存</summary>
        [Obsolete("=>Provider.Save")]
        public virtual void SaveAsync() => ThreadPoolX.QueueUserWorkItem(() => Provider.Save(this));
        #endregion
    }
}