WebApi 插件式构建方案:集成加载数据库连接字符串

对服务来说,一般都会用到数据库。而今,在微软的大环境下,使用 EF 的人肯定会越来越多。但是,使用 EF 有个问题,一个是使用缺省的构造函数,缺省从 ConfigurationManager.ConnectionStrings 中获取数据库连接;另外一种就是在构造的时候,手工指定数据库连接字符串。

对开发者来说,最好的办法就是不去管它,直接用缺省的构造函数就好。但是插件式的开发,系统怎么知道你数据库的连接字符串放在哪呀?主要的问题就在于其数据库连接字符串,并没有添加到 Web.Config 文件中,所以使用缺省构造函数,会出现无法找到配置的错误。

有个最简单的解决办法:可以选择把数据库连接字符串放到 Web.config 中,这样就能解决所有问题。可这样做,插件的配置侵入到主站了!但是,话说回来,我相信大部分用 WebApi 框架的人,都是这样干的。这样用,日后模块自己的数据库连接字符串增删改升级的时候,还得更改主站的配置。

这是绕不过去的一个坎!把本来一个的配置分散到两个地方,每次变动就必须修改这两处地方。日后维护的时候,这就是个坑!在知道的人离职后,后续的人根本就找不到问题原因。

插件管理自己的数据库连接字符串

理想的情况下,我们可以在第一次系统初始化的时候,给 ConfigurationManager.ConnectionStrings 这个集合中添加我们的数据库配置,这样就能在解析的时候找到配置了。顺着这个思路继续想,微软的反射很强大,可以更改本来不可以更新的数据。所以,就有了下面这段代码:

public void Configurate(System.Configuration.Configuration[] configurations)
{
    var meta = ((TypeX)ConfigurationManager.ConnectionStrings.GetType()).GetField("bReadOnly");
    meta.SetValue(ConfigurationManager.ConnectionStrings, false);

    configurations.SelectMany(p => p.ConnectionStrings.ConnectionStrings.OfType<ConnectionStringSettings>())
                  .Where(p => ConfigurationManager.ConnectionStrings.IndexOf(p) < 0)
                  .ForEach(ConfigurationManager.ConnectionStrings.Add);

    meta.SetValue(ConfigurationManager.ConnectionStrings, true);
}

这段代码的意思是,把各个模块的数据库连接字符串文件加载到列表中,然后通过反射开启赋值,加到 ConfigurationManager.ConnectionStrings 集合中。

要完成这个功能,我们尚需做的就是找到每个模块的数据库连接字符串文件,然后加载获得上面这个函数的参数。考虑到我们为每个模块定义了一个配置文件,所以这里为其添加一个配置就好了:

<?xml version="1.0" encoding="UTF-8"?>
  <configuration enabled="true">
    <description>授权支持插件</description>
    <assemblies>
      <add type="relative">bin/Intime.AuthorizationService.dll</add>
      <add type="relative">bin/Intime.AuthorizationService.Services.dll</add>
      <add type="relative">bin/Intime.AuthorizationService.Data.dll</add>
      <add type="relative">bin/Intime.AuthorizationService.Data.Repository.dll</add>
    </assembiles>
    <appConfig type="relative">bin/Intime.AuthorizationService.Data.Repository.dll.config</appConfig>
</configuration>

参考 appConfig 配置节,我们可以得到模块的配置文件绝对路径,再和 DynamicModules 配合,用下面这段代码就可以得到 System.Configuration.Configuration[] configurations 这个参数了:

public void Configurate(HttpConfiguration configuration)
{
    var items = ServiceLocator.Current.GetAllInstances<IAppConfigHandler>().ToArray();
    if (items.Any())
    {
        var data = DynamicModules.Instance
            .Modules
            .Where(p => !string.IsNullOrWhiteSpace(p.Configuration.AppConfig))
            .Select(p =>
            {
                var fullFilePath = Path.Combine(p.Path, p.Configuration.AppConfig);

                return ConfigurationManager.OpenMappedExeConfiguration(new ExeConfigurationFileMap { ExeConfigFilename = fullFilePath }, ConfigurationUserLevel.None);
            })
            .ToArray();

        items.ForEach(p => p.Configurate(data));
    }
}

可以看到,在这里我用了 IAppConfigHandler 接口,这样就可以扩展其他的配置了,不仅限于 ConnectionStrings

另外,上面这段代码缺点东西,自行脑补吧,很容易就看明白的。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。