如何在.NET Core中读取带有PROVIDER的connectionString?

8 c# asp.net-core-mvc .net-core asp.net-core asp.net-core-1.0

我补充道

.AddJsonFile("Connections.json", optional: true, reloadOnChange: true)
Run Code Online (Sandbox Code Playgroud)

 public Startup(IHostingEnvironment env)
Run Code Online (Sandbox Code Playgroud)

Connections.json包含:

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=DATABASE;Trusted_Connection=True;MultipleActiveResultSets=true",
    "COR-W81-101": "Data Source=DATASOURCE;Initial Catalog=P61_CAFM_Basic;User Id=USERID;Password=PASSWORD;Persist Security Info=False;MultipleActiveResultSets=False;Packet Size=4096;",
    "COR-W81-100": "Data Source=DATASOURCE;Initial Catalog=Post_PS;User Id=USERID;Password=PASSWORD;Persist Security Info=False;MultipleActiveResultSets=False;Packet Size=4096;",
    "MSEDGEWIN10": "Data Source=DATASOURCE; Initial Catalog=COR_Basic; Persist Security Info=False;Integrated Security=true;MultipleActiveResultSets=False;Packet Size=4096;Application Name=\"COR_Basic\"",

    "server": "Data Source=DATASOURCE; Initial Catalog=COR_Basic; Persist Security Info=False;User Id=USERID;Password=PASSWORD;MultipleActiveResultSets=False;Packet Size=4096;Application Name=\"COR_Basic\""
  },


  "conStrings": [
      {
        "name": "COR-W81-101",     
        "connectionString": "Data Source=DATASOURCE; Initial Catalog=COR_Basic; Persist Security Info=False;Integrated Security=true;MultipleActiveResultSets=False;Packet Size=4096;Application Name=\"COR_Basic\"",
        "providerName": "System.Data.SqlClient"
      }

    },

    {
      "name": "server",
      "connectionString": "Data Source=DATASOURCE; Initial Catalog=COR_Basic; Persist Security Info=False;Integrated Security=true;MultipleActiveResultSets=False;Packet Size=4096;Application Name=\"COR_Basic\"",
      "providerName": "System.Data.SqlClient"
    }
  ],



  "conStringDictionary": {
    "COR-W81-101": {
      "connectionString": "Data Source=DATASOURCE; Initial Catalog=COR_Basic; Persist Security Info=False;Integrated Security=true;MultipleActiveResultSets=False;Packet Size=4096;Application Name=\"COR_Basic\"",
      "providerName": "System.Data.SqlClient"
    },

    "server": {
      "connectionString": "Data Source=DATASOURCE; Initial Catalog=COR_Basic; Persist Security Info=False;Integrated Security=true;MultipleActiveResultSets=False;Packet Size=4096;Application Name=\"COR_Basic\"",
      "providerName": "System.Data.SqlClient"
    }

  }
}
Run Code Online (Sandbox Code Playgroud)

现在我想阅读connectionStrings:

public class ConnectionString
{
    public string name { get; set; }
    public string connectionString { get; set; }
    public string providerName { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

像这样:

//Microsoft.Extensions.DependencyInjection.OptionsConfigurationServiceCollectionExtensions.Configure<ConnectionString[]>(services, Configuration.GetSection("conStrings"));

// https://stackoverflow.com/questions/31929482/retrieve-sections-from-config-json-in-asp-net-5
//var objectSections = Configuration.GetSection("conStringDictionary").GetChildren();
//foreach (var x in objectSections)
//{
//    System.Console.WriteLine(x.Key);
//    var cs = new ConnectionString();
//    ConfigurationBinder.Bind(x, cs);
//    System.Console.WriteLine(cs);
//}

// http://andrewlock.net/how-to-use-the-ioptions-pattern-for-configuration-in-asp-net-core-rc2/
        Microsoft.Extensions.DependencyInjection.OptionsConfigurationServiceCollectionExtensions.Configure<Dictionary<string, ConnectionString>>(services, Configuration.GetSection("conStrings"));
Run Code Online (Sandbox Code Playgroud)

但我不能让它读取数组或字典.我需要每个connectionString的providerName,我希望它与连接字符串在同一条目中,但不是作为连接字符串.

Nic*_*oli 8

你基本上就在那里,你所要做的就是创建一些强类型的类来匹配旧的ConnectionStringSettings并利用一些集合序列化逻辑.

以下是我建议在json中格式化它们的方法.与您如何使用旧的XML app/web.config方式指定连接字符串类似.作为密钥的连接字符串的名称.

{
  "ConnectionStrings": {
    "Test1": {
      "ConnectionString": "server=localhost;database=db;username=user;password=pass;",
      "ProviderName": "MySql.Data.MySqlClient"
    },
    "Test2": {
      "ConnectionString": "server=localhost;database=db2;username=user2;password=pass2;",
      "ProviderName": "MySql.Data.MySqlClient"
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

现在为要绑定的类.首先是简单的ConnectionStringSettings类本身,实现你的基本相等/散列方法(因为我们打算将它粘在一个字典中,所以必须这样做).

public class ConnectionStringSettings
{
    public String Name { get; set; }
    public String ConnectionString { get; set; }
    public String ProviderName { get; set; }

    public ConnectionStringSettings()
    {
    }

    public ConnectionStringSettings(String name, String connectionString)
        : this(name, connectionString, null)
    {
    }

    public ConnectionStringSettings(String name, String connectionString, String providerName)
    {
        this.Name = name;
        this.ConnectionString = connectionString;
        this.ProviderName = providerName;
    }

    protected bool Equals(ConnectionStringSettings other)
    {
        return String.Equals(Name, other.Name) && String.Equals(ConnectionString, other.ConnectionString) && String.Equals(ProviderName, other.ProviderName);
    }

    public override bool Equals(Object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != this.GetType()) return false;
        return Equals((ConnectionStringSettings) obj);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            int hashCode = (Name != null ? Name.GetHashCode() : 0);
            hashCode = (hashCode * 397) ^ (ConnectionString != null ? ConnectionString.GetHashCode() : 0);
            hashCode = (hashCode * 397) ^ (ProviderName != null ? ProviderName.GetHashCode() : 0);
            return hashCode;
        }
    }

    public static bool operator ==(ConnectionStringSettings left, ConnectionStringSettings right)
    {
        return Equals(left, right);
    }

    public static bool operator !=(ConnectionStringSettings left, ConnectionStringSettings right)
    {
        return !Equals(left, right);
    }
}
Run Code Online (Sandbox Code Playgroud)

接下来是ConnectionStringSettings的集合.这只是必需的,因为连接字符串的名称是JSON表示法中的键.为了保持该名称的一致性,我们需要覆盖Dictionary的Add方法(但你不能这样做,因为它不是虚拟的).所以我们真正做的只是在内部用我们自己的Add实现中的额外位包装一个Dictionary.这看起来像很多代码,但你会发现它是非常单调无聊的东西.

public class ConnectionStringSettingsCollection : IDictionary<String, ConnectionStringSettings>
{
    private readonly Dictionary<String, ConnectionStringSettings> m_ConnectionStrings;

    public ConnectionStringSettingsCollection()
    {
        m_ConnectionStrings = new Dictionary<String, ConnectionStringSettings>();
    }

    public ConnectionStringSettingsCollection(int capacity)
    {
        m_ConnectionStrings = new Dictionary<String, ConnectionStringSettings>(capacity);
    }

    #region IEnumerable methods
    IEnumerator IEnumerable.GetEnumerator()
    {
        return ((IEnumerable)m_ConnectionStrings).GetEnumerator();
    }
    #endregion

    #region IEnumerable<> methods
    IEnumerator<KeyValuePair<String, ConnectionStringSettings>> IEnumerable<KeyValuePair<String, ConnectionStringSettings>>.GetEnumerator()
    {
        return ((IEnumerable<KeyValuePair<String, ConnectionStringSettings>>)m_ConnectionStrings).GetEnumerator();
    }
    #endregion

    #region ICollection<> methods
    void ICollection<KeyValuePair<String, ConnectionStringSettings>>.Add(KeyValuePair<String, ConnectionStringSettings> item)
    {
        ((ICollection<KeyValuePair<String, ConnectionStringSettings>>)m_ConnectionStrings).Add(item);
    }

    void ICollection<KeyValuePair<String, ConnectionStringSettings>>.Clear()
    {
        ((ICollection<KeyValuePair<String, ConnectionStringSettings>>)m_ConnectionStrings).Clear();
    }

    Boolean ICollection<KeyValuePair<String, ConnectionStringSettings>>.Contains(KeyValuePair<String, ConnectionStringSettings> item)
    {
        return ((ICollection<KeyValuePair<String, ConnectionStringSettings>>)m_ConnectionStrings).Contains(item);
    }

    void ICollection<KeyValuePair<String, ConnectionStringSettings>>.CopyTo(KeyValuePair<String, ConnectionStringSettings>[] array, Int32 arrayIndex)
    {
        ((ICollection<KeyValuePair<String, ConnectionStringSettings>>)m_ConnectionStrings).CopyTo(array, arrayIndex);
    }

    Boolean ICollection<KeyValuePair<String, ConnectionStringSettings>>.Remove(KeyValuePair<String, ConnectionStringSettings> item)
    {
        return ((ICollection<KeyValuePair<String, ConnectionStringSettings>>)m_ConnectionStrings).Remove(item);
    }

    public Int32 Count => ((ICollection<KeyValuePair<String, ConnectionStringSettings>>)m_ConnectionStrings).Count;
    public Boolean IsReadOnly => ((ICollection<KeyValuePair<String, ConnectionStringSettings>>)m_ConnectionStrings).IsReadOnly;
    #endregion

    #region IDictionary<> methods
    public void Add(String key, ConnectionStringSettings value)
    {
        // NOTE only slight modification, we add back in the Name of connectionString here (since it is the key)
        value.Name = key;
        m_ConnectionStrings.Add(key, value);
    }

    public Boolean ContainsKey(String key)
    {
        return m_ConnectionStrings.ContainsKey(key);
    }

    public Boolean Remove(String key)
    {
        return m_ConnectionStrings.Remove(key);
    }

    public Boolean TryGetValue(String key, out ConnectionStringSettings value)
    {
        return m_ConnectionStrings.TryGetValue(key, out value);
    }

    public ConnectionStringSettings this[String key]
    {
        get => m_ConnectionStrings[key];
        set => Add(key, value);
    }

    public ICollection<String> Keys => m_ConnectionStrings.Keys;
    public ICollection<ConnectionStringSettings> Values => m_ConnectionStrings.Values;
    #endregion
}
Run Code Online (Sandbox Code Playgroud)

一些简单的扩展方法使事情变得更简单.

public static class ConnectionStringSettingsExtensions
{
    public static ConnectionStringSettingsCollection ConnectionStrings(this IConfigurationRoot configuration, String section = "ConnectionStrings")
    {
        var connectionStringCollection = configuration.GetSection(section).Get<ConnectionStringSettingsCollection>();
        if (connectionStringCollection == null)
        {
            return new ConnectionStringSettingsCollection();
        }

        return connectionStringCollection;
    }

    public static ConnectionStringSettings ConnectionString(this IConfigurationRoot configuration, String name, String section = "ConnectionStrings")
    {
        ConnectionStringSettings connectionStringSettings;

        var connectionStringCollection = configuration.GetSection(section).Get<ConnectionStringSettingsCollection>();
        if (connectionStringCollection == null ||
            !connectionStringCollection.TryGetValue(name, out connectionStringSettings))
        {
            return null;
        }

        return connectionStringSettings;
    }
}
Run Code Online (Sandbox Code Playgroud)

最后的用法.

var configuration = new ConfigurationBuilder()
    .AddJsonFile("config.json")
    .Build();

var connectionStrings = configuration.ConnectionStrings();

foreach (var connectionString in connectionStrings.Values)
{
    Console.WriteLine(connectionString.Name);
    Console.WriteLine(connectionString.ConnectionString);
    Console.WriteLine(connectionString.ProviderName);
}

var specificConnStr1 = connectionStrings["Test1"];
Console.WriteLine(specificConnStr1.Name);
Console.WriteLine(specificConnStr1.ConnectionString);
Console.WriteLine(specificConnStr1.ProviderName);

var specificConnStr2 = configuration.ConnectionString("Test2");
Console.WriteLine(specificConnStr2.Name);
Console.WriteLine(specificConnStr2.ConnectionString);
Console.WriteLine(specificConnStr2.ProviderName);
Run Code Online (Sandbox Code Playgroud)


Rob*_*rto 7

这是一个老问题,但我今天正在研究这个问题,并认为我应该分享......

包含 ProviderName 的简单替代方案

这是一个简单的替代方案,可以避免自定义扩展和更改默认的 ConnectionStrings 配置结构。它基于 Microsoft 如何为 Azure 上的应用程序包含 ProviderName。

解决方案是在指定 ProviderName 的 ConnectionStrings 部分添加一个与上下文相关的键。

AppSettings.json与 SQLite 提供程序:

{  
  "ConnectionStrings": {
    "MyContext": "Data Source=c:\\MySqlite.db;Version=3;",
    "MyContext_ProviderName": "System.Data.SQLite",
  }
}
Run Code Online (Sandbox Code Playgroud)

在 C# 代码中使用GetConnectionString()方法读取值:

var connectionString = Configuration.GetConnectionString("MyContext");
var providerName = Configuration.GetConnectionString("MyContext_ProviderName") ?? "";

if (Regex.IsMatch(providerName, "SQLite", RegexOptions.IgnoreCase)) 
{
    builder.UseSqlite(connectionString);
}
else if (Regex.IsMatch(providerName, "Oracle", RegexOptions.IgnoreCase)) 
{    
    builder.AddOracle(connectionString);
}
else if (... 
Run Code Online (Sandbox Code Playgroud)

奖励 - 连接字符串前缀

Microsoft 包含 SQLClient 和 MySQL 的预定义前缀,它们将自动包含上述格式的提供程序名称。但是,这些前缀仅在作为环境变量添加时才起作用,即不在 appsettings.json 中。例如,在 launchSettings.json 中使用MYSQLCONNSTR_前缀定义连接字符串将填充连接字符串和提供程序名称。有关详细信息,请参阅ASP.NET Core 中的配置并向下滚动到连接字符串前缀

启动设置.json

{
  "profiles": {
    "Development": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "launchUrl": "",
      "environmentVariables": {
       "ASPNETCORE_ENVIRONMENT": "Development",

       // The prefix
       "MYSQLCONNSTR_MyContext": "Server=myServerAddress;Database=Green;Uid=myUsername;Pwd=myPassword;"

      }
    }
}
Run Code Online (Sandbox Code Playgroud)