ABP开发框架前后端开发系列---(12)配置模块的管理

一般来说,一个系统或多或少都会涉及到一些系统参数或者用户信息的配置,而ABP框架也提供了一套配置信息的管理模块,ABP框架的配置信息,必须提前定义好配置的各项内容,然后才能在系统中初始化或者通过接口查询来使用,本篇随笔引入了另外一种配置信息的定义,实现更加简化的处理,本篇随笔着重介绍两者之间的差异和不同的地方。

1、ABP框架的配置管理

如下面是邮件配置信息,配置信息一般先继承自SettingProvider,初始化定义后,才能被系统所使用。

EmailSettingProvider:继承自SettingProvider, 将SMTP的各项设置封装成SettingDefinition,并以数组形式返回

配置的管理类,主要通过接口ISettingManager来进行统一管理的,底层协同了SettingStore配置存储和SetttingDefinitionMananger的配置定义管理两个部分。

这种方式的配置信息,糅合了配置项的定义(强制性),以及多语言特性的处理,根据不同的语言返回不同的配置名称,同时也整合了缓存信息的处理,以减少系统的一些消耗。 

不过从上面的图示我们也可以看到,整个配置模块由于引入这些内容,导致处理起来必须按部就班的创建配置管理类,定义配置信息,重新编译系统后,然后才能进行信息的调用,因此这些配置信息必须预定义。而且管理起来协同这些类的处理,也略显得有点复杂化。

在ABP核心模块的启动过程中,会预先初始化这些配置管理类,如下代码所示

然后在AddSettingProviders中加入预先定义好的配置类。

接着在完成初始化过程中,有配置定义类统一根据这些配置对象,进行定义的初始化,这样才能在系统中进行使用。

配置定义的管理类接口,可以用下面这个图示进行说明。

以上就是在ABP框架中,基于配置模块的管理过程。

一般情况下,如果我们需要在Web API端中对这些接口进行调用管理,如对用户或者系统Email配置信息的获取和修改,那么我们需要定义一个配置接口服务(默认下载的ABP框架中没有公布这个接口定义和实现)。

如下我们定义一个SettingsAppService和他的接口

然后我们可以实现它的获取信息和修改信息的接口,如下所示是对系统级别的邮件参数进行配置管理。

        /// <summary>
        /// 获取应用程序级别的邮件配置(系统邮件配置)
        /// </summary>
        /// <returns></returns>
        public async Task<EmailSettingsEditDto> GetEmailSettingsForApplication()
        {
            var smtpPassword = await SettingManager.GetSettingValueForApplicationAsync(EmailSettingNames.Smtp.Password);

            return new EmailSettingsEditDto
            {
                DefaultFromAddress = await SettingManager.GetSettingValueForApplicationAsync(EmailSettingNames.DefaultFromAddress),
                DefaultFromDisplayName = await SettingManager.GetSettingValueForApplicationAsync(EmailSettingNames.DefaultFromDisplayName),
                SmtpHost = await SettingManager.GetSettingValueForApplicationAsync(EmailSettingNames.Smtp.Host),
                SmtpPort = await SettingManager.GetSettingValueForApplicationAsync<int>(EmailSettingNames.Smtp.Port),
                SmtpUserName = await SettingManager.GetSettingValueForApplicationAsync(EmailSettingNames.Smtp.UserName),
                SmtpPassword = SimpleStringCipher.Instance.Decrypt(smtpPassword),
                SmtpDomain = await SettingManager.GetSettingValueForApplicationAsync(EmailSettingNames.Smtp.Domain),
                SmtpEnableSsl = await SettingManager.GetSettingValueForApplicationAsync<bool>(EmailSettingNames.Smtp.EnableSsl),
                SmtpUseDefaultCredentials = await SettingManager.GetSettingValueForApplicationAsync<bool>(EmailSettingNames.Smtp.UseDefaultCredentials)
            };
        }

        /// <summary>
        /// 更新应用程序级别的邮件配置(系统邮件配置)
        /// </summary>
        /// <returns></returns>
        public async Task UpdateEmailSettingsForApplication(EmailSettingsEditDto input)
        {
            await SettingManager.ChangeSettingForApplicationAsync(EmailSettingNames.DefaultFromAddress, input.DefaultFromAddress);
            await SettingManager.ChangeSettingForApplicationAsync(EmailSettingNames.DefaultFromDisplayName, input.DefaultFromDisplayName);
            await SettingManager.ChangeSettingForApplicationAsync(EmailSettingNames.Smtp.Host, input.SmtpHost);
            await SettingManager.ChangeSettingForApplicationAsync(EmailSettingNames.Smtp.Port, input.SmtpPort.ToString(CultureInfo.InvariantCulture));
            await SettingManager.ChangeSettingForApplicationAsync(EmailSettingNames.Smtp.UserName, input.SmtpUserName);
            await SettingManager.ChangeSettingForApplicationAsync(EmailSettingNames.Smtp.Password, SimpleStringCipher.Instance.Encrypt(input.SmtpPassword));
            await SettingManager.ChangeSettingForApplicationAsync(EmailSettingNames.Smtp.Domain, input.SmtpDomain);
            await SettingManager.ChangeSettingForApplicationAsync(EmailSettingNames.Smtp.EnableSsl, input.SmtpEnableSsl.ToString().ToLowerInvariant());
            await SettingManager.ChangeSettingForApplicationAsync(EmailSettingNames.Smtp.UseDefaultCredentials, input.SmtpUseDefaultCredentials.ToString().ToLowerInvariant());
        }

 

2、使用自定义的参数配置管理

我在较早的随笔《Winform开发框架之参数配置管理功能实现-基于SettingsProvider.net的构建》中介绍过对配置信息的管理实现,这种配置参数方式一直很好的应用在我的各个框架上,定义和使用都相对比较简单,能够满足绝大多数的应用场景,相对ABP框架的配置模块来说,简单易用。

首先我们定义一个用来存储通用配置信息的表,如下所示。

这个配置表的主要特点也是以键为操作对象,然后内容是JSON序列化后的内容,可以存储用户自定义的类的序列号字符串,这个是它的灵魂所在。和ABP框架仅仅存储简单类型的值有所不同。

和其他模块的定义一样,我们可以先根据常规表的方式,使用代码快速生成类的结构,如下所示。

    /// <summary>
    /// 用户参数配置,应用层服务接口实现
    /// </summary>
    [AbpAuthorize]
    public class UserParameterAppService : MyAsyncServiceBase<UserParameter, UserParameterDto, string, UserParameterPagedDto, CreateUserParameterDto, UserParameterDto>, IUserParameterAppService
    {
        private readonly IRepository<UserParameter, string> _repository;

        public UserParameterAppService(IRepository<UserParameter, string> repository) : base(repository)
        {
            _repository = repository;
        }

然后定义几个用于用户级别和系统程序级别的接口实现,如获取信息,修改信息等。

然后,在生成的Caller层类里面,增加以上的Web API接口调用的实现代码,如下所示

 

    /// <summary>
    /// 用户参数配置的Web API调用处理
    /// </summary>
    public class UserParameterApiCaller : AsyncCrudApiCaller<UserParameterDto, string, UserParameterPagedDto, CreateUserParameterDto, UserParameterDto>, IUserParameterAppService
    {
        /// <summary>
        /// 提供单件对象使用
        /// </summary>
        public static UserParameterApiCaller Instance
        {
            get
            {
                return Singleton<UserParameterApiCaller>.Instance;
            }
        }

        /// <summary>
        /// 默认构造函数
        /// </summary>
        public UserParameterApiCaller()
        {
            this.DomainName = "UserParameter";//指定域对象名称,用于组装接口地址
        }

        public async Task<UserParameterDto> GetSettingForUser(NameInputDto input)
        {
            return await DoActionAsync<UserParameterDto>(MethodBase.GetCurrentMethod(), input);
        }

        public async Task ChangeSettingForUser(NameValueDto input)
        {
            await DoActionAsync(MethodBase.GetCurrentMethod(), input);
        }

        public async Task<UserParameterDto> GetSettingForApplication(NameInputDto input)
        {
            return await DoActionAsync<UserParameterDto>(MethodBase.GetCurrentMethod(), input);
        }

        public async Task ChangeSettingForApplication(NameValueDto input)
        {
            await DoActionAsync(MethodBase.GetCurrentMethod(), input);
        }
    }

如果对于上面的DoActionAsyn的处理有疑问,可以参考之前随笔《ABP开发框架前后端开发系列---(10)Web API调用类的简化处理》进行了解。

我在之前介绍过的配置模块里面,结合过FireFoxDialog界面效果,实现较好的参数配置管理功能,如下界面所示。

 我们本次使用这两个不同的配置模块,也希望使用这个来展现一下,以便更好的理解。

由于整合了SettingsProvider.net组件,我们只需要封装一下对数据库的存储获取方式就可以了。

    /// <summary>
    /// 数据库参数存储设置
    /// </summary>
    public class DatabaseStorage : JsonSettingsStoreBase
    {
        /// <summary>
        /// 配置级别
        /// </summary>
        public SettingScopes Scope { get; set; }

        /// <summary>
        /// 构造函数
        /// </summary>
        public DatabaseStorage()
        {
            this.Scope = SettingScopes.User;
        }

        /// <summary>
        /// 参数构造函数
        /// </summary>
        /// <param name="scope">配置级别</param>
        public DatabaseStorage(SettingScopes scope)
        {
            this.Scope = scope;
        }

        /// <summary>
        /// 保存到数据库
        /// </summary>
        /// <param name="filename">文件名称(类型名称)</param>
        /// <param name="fileContents">参数内容</param>
        protected override void WriteTextFile(string filename, string fileContents)
        {
            var info = new NameValueDto(filename, fileContents);
            if (this.Scope == SettingScopes.Application)
            {
                AsyncContext.Run(()=> UserParameterApiCaller.Instance.ChangeSettingForApplication(info));
            }
            else
            {
                AsyncContext.Run(() => UserParameterApiCaller.Instance.ChangeSettingForUser(info));
            }
        }

        /// <summary>
        /// 从数据库读取
        /// </summary>
        /// <param name="filename">文件名称(类型名称)</param>
        /// <returns></returns>
        protected override string ReadTextFile(string filename)
        {
            var info = new NameInputDto(filename);

            UserParameterDto result = null;
            if (this.Scope == SettingScopes.Application)
            {
                result = AsyncContext.Run(() => UserParameterApiCaller.Instance.GetSettingForApplication(info));
            }
            else
            {
                result = AsyncContext.Run(() => UserParameterApiCaller.Instance.GetSettingForUser(info));
            }

            return result != null ? result.Content : null;
        }
    }

有了这个实现,这样在操作上,就不用管理这些内容如何获取和更新了,和之前的使用配置管理方式一致了。可以处理各种不同的配置对象信息。

先来看看默认ABP的配置处理方式,管理界面如下所示。

这里的配置存储咋ABP的AbpSettings表里面,如下所示,每项内容是以字符串方式独立存储的。

它的调用主要就是SettingsApiCaller的内容了,注意这个邮件配置,必须在EmailSettingProvider中提前定义好对象的信息。

        private EmailSettingsEditDto GetParameter()
        {
            EmailSettingsEditDto param =  AsyncContext.Run(() => SettingsApiCaller.Instance.GetEmailSettingsForApplication());
            if(param == null)
            {
                param = new EmailSettingsEditDto();
            }
            return param;
        }

        public override void OnInit()
        {
            var parameter = GetParameter();
            if (parameter != null)
            {
                this.txtEmail.Text = parameter.DefaultFromAddress;
                this.txtLoginId.Text = parameter.SmtpUserName;
                this.txtPassword.Text = parameter.SmtpPassword;
                this.txtPassword.Tag = parameter.SmtpPassword;
                this.txtSmtpPort.Value = parameter.SmtpPort;
                this.txtSmtpServer.Text = parameter.SmtpHost;
                this.txtUseSSL.Checked = parameter.SmtpEnableSsl;
            }
        }

下面我们再来看看自定义的配置管理方式。如下是自定义配置模块获取显示的内容。

这个配置是系统级别的,它的获取方式如下所示。

    public partial class PageEmailApplication : PropertyPage
    {
        private SettingsProvider settings;
        private ISettingsStorage store;

        public PageEmailApplication()
        {
            InitializeComponent();

            if (!this.DesignMode)
            {
                store = new DatabaseStorage(SettingScopes.Application);
                settings = new SettingsProvider(store);
            }
        }

        public override void OnInit()
        {
            EmailParameter parameter = settings.GetSettings<EmailParameter>();
            if (parameter != null)
            {
                this.txtEmail.Text = parameter.Email;
                this.txtLoginId.Text = parameter.LoginId;
                this.txtPassword.Text = parameter.Password;
                this.txtPassword.Tag = parameter.Password;
                this.txtPop3Port.Value = parameter.Pop3Port;
                this.txtPop3Server.Text = parameter.Pop3Server;
                this.txtSmtpPort.Value = parameter.SmtpPort;
                this.txtSmtpServer.Text = parameter.SmtpServer;
                this.txtUseSSL.Checked = parameter.UseSSL;
            }
        }

以上是标准的SettingsProvider.net的组件调用方式,我们不用知道具体的数据存储,只需要把内容直接GetSetting方式获取出来即可。

而保存内容,直接通过使用SaveSettings保存即可。

                EmailParameter parameter = settings.GetSettings<EmailParameter>();
                if (parameter != null)
                {                    
                    parameter.Email = this.txtEmail.Text;
                    parameter.LoginId = this.txtLoginId.Text;
                    parameter.Password = this.txtPassword.Text;
                    parameter.Pop3Port = Convert.ToInt32(this.txtPop3Port.Value);
                    parameter.Pop3Server = this.txtPop3Server.Text;
                    parameter.SmtpPort = Convert.ToInt32(this.txtSmtpPort.Value);
                    parameter.SmtpServer = this.txtSmtpServer.Text;
                    parameter.UseSSL = this.txtUseSSL.Checked;

                    settings.SaveSettings<EmailParameter>(parameter);
                }

其中 EmailParameter 类是我们定义的一个类,用来承载相关的配置信息,如下所示。它支持默认值,加密处理等设置。

    /// <summary>
    /// 邮箱设置
    /// </summary>
    public class EmailParameter
    {
        /// <summary>
        /// 邮件账号
        /// </summary>
        //[DefaultValue("wuhuacong@163.com")]
        public string Email { get; set; }

        /// <summary>
        /// POP3服务器
        /// </summary>
        [DefaultValue("pop.163.com")]
        public string Pop3Server { get; set; }

        /// <summary>
        /// POP3端口
        /// </summary>
        [DefaultValue(110)]
        public int Pop3Port { get; set; }

        /// <summary>
        /// SMTP服务器
        /// </summary>
        [DefaultValue("smtp.163.com")]
        public string SmtpServer { get; set; }

        /// <summary>
        /// SMTP端口
        /// </summary>
        [DefaultValue(25)]
        public int SmtpPort { get; set; }

        /// <summary>
        /// 登陆账号
        /// </summary>
        public string LoginId { get; set; }

        /// <summary>
        /// 登陆密码
        /// </summary>
        [ProtectedString]
        public string Password { get; set; }

        /// <summary>
        /// 使用SSL加密
        /// </summary>
        [DefaultValue(false)]
        public bool UseSSL { get; set; }
    }

 

由于SettingsProvider.net组件的支持,我们还可以把配置信息当成本地文件存储起来,对于一些需要存为文件的方式的配置,非常不错。

    public partial class PageReport : PropertyPage
    {
        private SettingsProvider settings;
        private ISettingsStorage store;

        public PageReport()
        {
            InitializeComponent();

            if (!this.DesignMode)
            {
                // PortableStorage: 在运行程序目录创建一个setting的文件记录参数数据
                store = new PortableStorage();
                settings = new SettingsProvider(store);
            }
        }

以上就是介绍了ABP配置管理模块的实现原理和客户端的调用,以及使用自定义配置管理模块的方式进行处理更加动态化或者灵活一点的配置信息,使用自定义配置信息管理服务,整合了SettingProvider.net的支持,可以实现更好的参数配置管理体验。 

posted on 2019-07-01 12:24  伍华聪  阅读(1696)  评论(1编辑  收藏

导航