如何在 .NET Core 中读取 connectionString WITH PROVIDER?
How to read a connectionString WITH PROVIDER in .NET Core?
我加了
.AddJsonFile("Connections.json", optional: true, reloadOnChange: true)
在
public Startup(IHostingEnvironment env)
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"
}
}
}
现在我想读取 connectionStrings:
public class ConnectionString
{
public string name { get; set; }
public string connectionString { get; set; }
public string providerName { get; set; }
}
像这样:
//Microsoft.Extensions.DependencyInjection.OptionsConfigurationServiceCollectionExtensions.Configure<ConnectionString[]>(services, Configuration.GetSection("conStrings"));
//
//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"));
但我无法让它读取数组或字典。我需要每个 connectionString 的 providerName,并且我希望它与连接字符串位于同一条目中,但不作为连接字符串。
类似于:
var configurationRoot = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", false)
.Build();
var conString = configurationRoot["ConnectionStrings:MyConnection"]);
你基本上在那里,你所要做的就是制作一些强类型的 classes 来匹配旧的 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"
}
}
}
现在要绑定 classes。首先是简单的 ConnectionStringSettings class 本身,它实现了您的基本 equality/hashing 方法(这是必要的,因为我们打算将其粘贴到字典中)。
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);
}
}
接下来是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
}
几个简单的扩展方法,让事情变得更简单。
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;
}
}
终于说到用法了。
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);
首先,Nicholi 的回答启发了我!谢谢 Nicholi。
其次,我有一个 "List" 解决方案而不是 IDictionary 解决方案。它不像 IDictionary 解决方案那样流畅。
这也可以称为"how to create a collection list for dot net core configuration"
我们开始:
先是无耻的盗窃!
public class ConnectionStringEntry
{
public String Name { get; set; }
public String ConnectionString { get; set; }
public String ProviderName { get; set; }
public ConnectionStringEntry()
{
}
public ConnectionStringEntry(String name, String connectionString)
: this(name, connectionString, null)
{
}
public ConnectionStringEntry(String name, String connectionString, String providerName)
{
this.Name = name;
this.ConnectionString = connectionString;
this.ProviderName = providerName;
}
}
秒,一个"wrapper"。我想跟踪 DefaultConnectionStringName...以及我的条目列表(集合)。
public class ConnectionStringWrapper
{
public string DefaultConnectionStringName { get; set; } = "";
public List<ConnectionStringEntry> ConnectionStringEntries { get; set; } = new List<ConnectionStringEntry>();
//public Dictionary<string, ConnectionStringEntry> ConnectionStringEntries { get; set; } = new Dictionary<string, ConnectionStringEntry>();
public ConnectionStringEntry GetDefaultConnectionStringEntry()
{
ConnectionStringEntry returnItem = this.GetConnectionStringEntry(this.DefaultConnectionStringName);
return returnItem;
}
public ConnectionStringEntry GetConnectionStringEntry(string name)
{
ConnectionStringEntry returnItem = null;
if (null != this.ConnectionStringEntries && this.ConnectionStringEntries.Any())
{
returnItem = this.ConnectionStringEntries.FirstOrDefault(ce => ce.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
}
if (null == returnItem)
{
throw new ArgumentOutOfRangeException(string.Format("No default ConnectionStringEntry found. (ConnectionStringEntries.Names='{0}', Search.Name='{1}')", this.ConnectionStringEntries == null ? string.Empty : string.Join(",", this.ConnectionStringEntries.Select(ce => ce.Name)), name));
}
return returnItem;
}
}
现在,我阅读 json 并映射到具体的设置对象代码:
IConfiguration config = new ConfigurationBuilder()
.SetBasePath(System.IO.Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.Build();
ConnectionStringWrapper settings = new ConnectionStringWrapper();
config.Bind("ConnectionStringWrapperSettings", settings);
Console.WriteLine("{0}, {1}", settings.DefaultConnectionStringName, settings.ConnectionStringEntries.Count);
ConnectionStringEntry cse = settings.GetDefaultConnectionStringEntry();
我的 nuget 包:
\.nuget\packages\microsoft.extensions.configuration.1.1
\.nuget\packages\microsoft.extensions.configuration.binder.1.1
\.nuget\packages\microsoft.extensions.configuration.json.1.1
奖金 MATERIAL 下面:
我正在(尝试)支持可以部署为 DotNet 4.x("classic" ?? 作为现在的术语??)和 dotnet 核心的代码库。
为此,我写了上面的内容以提供 DotNet(Classic) 处理连接字符串的方式的抽象(xml,我们的老朋友)现在块:带有 json.
的 DotNetCore
为此,我编写了一个接口:
public interface IConnectionStringWrapperRetriever
{
ConnectionStringWrapper RetrieveConnectionStringWrapper();
}
我有一个 dotnetcore 的实现:
public class ConnectionStringWrapperDotNetCoreRetriever : IConnectionStringWrapperRetriever
{
public const string ConnectionStringWrapperSettingsJsonElementName = "ConnectionStringWrapperSettings";
private readonly IConfiguration config;
public ConnectionStringWrapperDotNetCoreRetriever(IConfiguration cnfg)
{
this.config = cnfg;
}
public ConnectionStringWrapper RetrieveConnectionStringWrapper()
{
ConnectionStringWrapper settings = new ConnectionStringWrapper();
this.config.Bind(ConnectionStringWrapperSettingsJsonElementName, settings);
return settings;
}
}
哦,是的,所有重要的 JSON 设置:
{
"ConnectionStringWrapperSettings": {
"DefaultConnectionStringName": "abc",
"ConnectionStringEntries": [
{
"Name": "abc",
"ConnectionString": "Server=myserver;Database=mydatabase;Trusted_Connection=True;MultipleActiveResultSets=true",
"ProviderName": "SomeProvider"
},
{
"Name": "def",
"ConnectionString": "server=localhost;database=db2;username=user2;password=pass2;",
"ProviderName": "SomeProvider"
}
]
}
}
..............
对于 DotNet(经典),您需要做的就是为 IConnectionStringWrapperRetriever 实现第二个具体,然后施展魔法。
还记得下面的xml吗? (哈哈,还没那么老呢!)
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<connectionStrings>
<add name="ConnStr1" connectionString="LocalSqlServer: data source=127.0.0.1;Integrated Security=SSPI;Initial Catalog=aspnetdb"
providerName="System.Data.SqlClient" />
</connectionStrings>
</configuration>
还记得 EnterpriseLibrary 中的这个东西吗?
<configSections>
<section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</configSections>
<dataConfiguration defaultDatabase="ConnStr1"/>
我将把 DotNet(Classic) 实现留给 reader。
但现在我将 IConnectionStringWrapperRetriever 注入到我的 DataLayer 类。
我正在使用 Dapper,因此我可以使用 IConnectionStringWrapperRetriever 获取连接字符串。
如果我的项目是"house" DotNet(Classic),我会注入一个版本的IConnectionStringWrapperRetriever(此处未见,留给reader)。如果我的项目是 DotNetCore 中的 "housed",我会注入第二个(如上所示)版本的 IConnectionStringWrapperRetriever。
超出了这个 post 的范围,但是 "housed" 我的意思是我有 2 个 csproj 并排坐着。
MyApp.DataLayer.classic.csproj
和
MyApp.DataLayer.csproj
我发现保留默认的 csproj 来存放 DotNetCore 的东西更容易。我使用 "classic.csproj" 文件来存放 DotNet(classic)。我的程序集名称和默认命名空间保持 "MyApp.Datalayer".......classic 仅用于区分 csrproj 文件名。
我也创建了两个解决方案 sln 文件。 MySolution.classic.sln 和 MySolution.sln.
它似乎正在工作.....使用我上面写的这个 ConnectionString 抽象。
我唯一的条件是(经典)AssemblyInfo.cs 文件。
#if(!NETCOREAPP2_1)
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
/* all the other stuff removed here */
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
#endif
追加:
好的,这是 DotNet(Classic) 版本:
public class ConnectionStringWrapperDotNetClassicRetriever : IConnectionStringWrapperRetriever
{
public ConnectionStringWrapper RetrieveConnectionStringWrapper()
{
ConnectionStringWrapper returnItem = new ConnectionStringWrapper();
foreach(ConnectionStringSettings css in System.Configuration.ConfigurationManager.ConnectionStrings)
{
ConnectionStringEntry cse = new ConnectionStringEntry(css.Name, css.ConnectionString, css.ProviderName);
returnItem.ConnectionStringEntries.Add(cse);
}
if(returnItem.ConnectionStringEntries.Count == 1)
{
/* if there is only one, set the default name to that one */
returnItem.DefaultConnectionStringName = returnItem.ConnectionStringEntries.First().Name;
}
else
{
/*
<packages>
<package id="EnterpriseLibrary.Common" version="6.0.1304.0" targetFramework="net45" />
<package id="EnterpriseLibrary.Data" version="6.0.1304.0" targetFramework="net45" />
</packages>
*/
/* using Microsoft.Practices.EnterpriseLibrary.Data.Configuration; */
/* You can write you own way to handle a default database, or piggyback off of EnterpriseLibrary. You don't necessarily have to use EnterpriseLibrary.Data, you are simply piggybacking on their xml/configuration setup */
DatabaseSettings dbSettings = (DatabaseSettings)ConfigurationManager.GetSection("dataConfiguration");
returnItem.DefaultConnectionStringName = dbSettings.DefaultDatabase;
}
return returnItem;
}
}
和 app.config xml:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data"/>
</configSections>
<connectionStrings>
<clear/>
<add name="MyFirstConnectionStringName" connectionString="Server=.\MyServerOne;Database=OneDB;Trusted_Connection=True;MultipleActiveResultSets=true"
providerName="System.Data.SqlClient" />
<add name="MySecondConnectionStringName" connectionString="Server=.\MyServerTwo;Database=TwoDB;Trusted_Connection=True;MultipleActiveResultSets=true"
providerName="System.Data.SqlClient" />
</connectionStrings>
<dataConfiguration defaultDatabase="MyFirstConnectionStringName" />
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
</configuration>
关键词:
DotNet DotNet .Net Core Classic Json 配置 ICollection 标量和集合同时支持 dotnet 和 dotnetcore
一个老问题,但我今天正在看这个问题,想分享一下...
包含 ProviderName 的简单替代方法
这是一个简单的替代方法,可以避免自定义扩展和更改默认的 ConnectionStrings 配置结构。它基于 Microsoft 如何为 Azure 上的应用程序包含 ProviderName。
解决方案是在指定 ProviderName 的 ConnectionStrings 部分添加上下文相关键。
AppSettings.json 使用 SQLite 提供程序:
{
"ConnectionStrings": {
"MyContext": "Data Source=c:\MySqlite.db;Version=3;",
"MyContext_ProviderName": "System.Data.SQLite",
}
}
并在 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 (...
奖励 - 连接字符串前缀
Microsoft 包含 SQLClient 的预定义前缀,MySQL 将自动包含上述格式的提供程序名称。但是,这些前缀仅在作为环境变量添加时才有效,即不在 appsettings.json 中。例如,使用 MYSQLCONNSTR_ 前缀在 launchSettings.json 中定义连接字符串将填充连接字符串和提供程序名称。有关详细信息,请参阅 Configuration in ASP.NET Core 并向下滚动到 连接字符串前缀
launchSettings.json
{
"profiles": {
"Development": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
// The prefix
"MYSQLCONNSTR_MyContext": "Server=myServerAddress;Database=Green;Uid=myUsername;Pwd=myPassword;"
}
}
}
我加了
.AddJsonFile("Connections.json", optional: true, reloadOnChange: true)
在
public Startup(IHostingEnvironment env)
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"
}
}
}
现在我想读取 connectionStrings:
public class ConnectionString
{
public string name { get; set; }
public string connectionString { get; set; }
public string providerName { get; set; }
}
像这样:
//Microsoft.Extensions.DependencyInjection.OptionsConfigurationServiceCollectionExtensions.Configure<ConnectionString[]>(services, Configuration.GetSection("conStrings"));
//
//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"));
但我无法让它读取数组或字典。我需要每个 connectionString 的 providerName,并且我希望它与连接字符串位于同一条目中,但不作为连接字符串。
类似于:
var configurationRoot = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", false)
.Build();
var conString = configurationRoot["ConnectionStrings:MyConnection"]);
你基本上在那里,你所要做的就是制作一些强类型的 classes 来匹配旧的 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"
}
}
}
现在要绑定 classes。首先是简单的 ConnectionStringSettings class 本身,它实现了您的基本 equality/hashing 方法(这是必要的,因为我们打算将其粘贴到字典中)。
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);
}
}
接下来是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
}
几个简单的扩展方法,让事情变得更简单。
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;
}
}
终于说到用法了。
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);
首先,Nicholi 的回答启发了我!谢谢 Nicholi。
其次,我有一个 "List" 解决方案而不是 IDictionary 解决方案。它不像 IDictionary 解决方案那样流畅。
这也可以称为"how to create a collection list for dot net core configuration"
我们开始:
先是无耻的盗窃!
public class ConnectionStringEntry
{
public String Name { get; set; }
public String ConnectionString { get; set; }
public String ProviderName { get; set; }
public ConnectionStringEntry()
{
}
public ConnectionStringEntry(String name, String connectionString)
: this(name, connectionString, null)
{
}
public ConnectionStringEntry(String name, String connectionString, String providerName)
{
this.Name = name;
this.ConnectionString = connectionString;
this.ProviderName = providerName;
}
}
秒,一个"wrapper"。我想跟踪 DefaultConnectionStringName...以及我的条目列表(集合)。
public class ConnectionStringWrapper
{
public string DefaultConnectionStringName { get; set; } = "";
public List<ConnectionStringEntry> ConnectionStringEntries { get; set; } = new List<ConnectionStringEntry>();
//public Dictionary<string, ConnectionStringEntry> ConnectionStringEntries { get; set; } = new Dictionary<string, ConnectionStringEntry>();
public ConnectionStringEntry GetDefaultConnectionStringEntry()
{
ConnectionStringEntry returnItem = this.GetConnectionStringEntry(this.DefaultConnectionStringName);
return returnItem;
}
public ConnectionStringEntry GetConnectionStringEntry(string name)
{
ConnectionStringEntry returnItem = null;
if (null != this.ConnectionStringEntries && this.ConnectionStringEntries.Any())
{
returnItem = this.ConnectionStringEntries.FirstOrDefault(ce => ce.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
}
if (null == returnItem)
{
throw new ArgumentOutOfRangeException(string.Format("No default ConnectionStringEntry found. (ConnectionStringEntries.Names='{0}', Search.Name='{1}')", this.ConnectionStringEntries == null ? string.Empty : string.Join(",", this.ConnectionStringEntries.Select(ce => ce.Name)), name));
}
return returnItem;
}
}
现在,我阅读 json 并映射到具体的设置对象代码:
IConfiguration config = new ConfigurationBuilder()
.SetBasePath(System.IO.Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.Build();
ConnectionStringWrapper settings = new ConnectionStringWrapper();
config.Bind("ConnectionStringWrapperSettings", settings);
Console.WriteLine("{0}, {1}", settings.DefaultConnectionStringName, settings.ConnectionStringEntries.Count);
ConnectionStringEntry cse = settings.GetDefaultConnectionStringEntry();
我的 nuget 包:
\.nuget\packages\microsoft.extensions.configuration.1.1
\.nuget\packages\microsoft.extensions.configuration.binder.1.1
\.nuget\packages\microsoft.extensions.configuration.json.1.1
奖金 MATERIAL 下面:
我正在(尝试)支持可以部署为 DotNet 4.x("classic" ?? 作为现在的术语??)和 dotnet 核心的代码库。
为此,我写了上面的内容以提供 DotNet(Classic) 处理连接字符串的方式的抽象(xml,我们的老朋友)现在块:带有 json.
的 DotNetCore为此,我编写了一个接口:
public interface IConnectionStringWrapperRetriever
{
ConnectionStringWrapper RetrieveConnectionStringWrapper();
}
我有一个 dotnetcore 的实现:
public class ConnectionStringWrapperDotNetCoreRetriever : IConnectionStringWrapperRetriever
{
public const string ConnectionStringWrapperSettingsJsonElementName = "ConnectionStringWrapperSettings";
private readonly IConfiguration config;
public ConnectionStringWrapperDotNetCoreRetriever(IConfiguration cnfg)
{
this.config = cnfg;
}
public ConnectionStringWrapper RetrieveConnectionStringWrapper()
{
ConnectionStringWrapper settings = new ConnectionStringWrapper();
this.config.Bind(ConnectionStringWrapperSettingsJsonElementName, settings);
return settings;
}
}
哦,是的,所有重要的 JSON 设置:
{
"ConnectionStringWrapperSettings": {
"DefaultConnectionStringName": "abc",
"ConnectionStringEntries": [
{
"Name": "abc",
"ConnectionString": "Server=myserver;Database=mydatabase;Trusted_Connection=True;MultipleActiveResultSets=true",
"ProviderName": "SomeProvider"
},
{
"Name": "def",
"ConnectionString": "server=localhost;database=db2;username=user2;password=pass2;",
"ProviderName": "SomeProvider"
}
]
}
}
..............
对于 DotNet(经典),您需要做的就是为 IConnectionStringWrapperRetriever 实现第二个具体,然后施展魔法。
还记得下面的xml吗? (哈哈,还没那么老呢!)
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<connectionStrings>
<add name="ConnStr1" connectionString="LocalSqlServer: data source=127.0.0.1;Integrated Security=SSPI;Initial Catalog=aspnetdb"
providerName="System.Data.SqlClient" />
</connectionStrings>
</configuration>
还记得 EnterpriseLibrary 中的这个东西吗?
<configSections>
<section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</configSections>
<dataConfiguration defaultDatabase="ConnStr1"/>
我将把 DotNet(Classic) 实现留给 reader。
但现在我将 IConnectionStringWrapperRetriever 注入到我的 DataLayer 类。
我正在使用 Dapper,因此我可以使用 IConnectionStringWrapperRetriever 获取连接字符串。
如果我的项目是"house" DotNet(Classic),我会注入一个版本的IConnectionStringWrapperRetriever(此处未见,留给reader)。如果我的项目是 DotNetCore 中的 "housed",我会注入第二个(如上所示)版本的 IConnectionStringWrapperRetriever。
超出了这个 post 的范围,但是 "housed" 我的意思是我有 2 个 csproj 并排坐着。
MyApp.DataLayer.classic.csproj 和 MyApp.DataLayer.csproj
我发现保留默认的 csproj 来存放 DotNetCore 的东西更容易。我使用 "classic.csproj" 文件来存放 DotNet(classic)。我的程序集名称和默认命名空间保持 "MyApp.Datalayer".......classic 仅用于区分 csrproj 文件名。
我也创建了两个解决方案 sln 文件。 MySolution.classic.sln 和 MySolution.sln.
它似乎正在工作.....使用我上面写的这个 ConnectionString 抽象。
我唯一的条件是(经典)AssemblyInfo.cs 文件。
#if(!NETCOREAPP2_1)
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
/* all the other stuff removed here */
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
#endif
追加:
好的,这是 DotNet(Classic) 版本:
public class ConnectionStringWrapperDotNetClassicRetriever : IConnectionStringWrapperRetriever
{
public ConnectionStringWrapper RetrieveConnectionStringWrapper()
{
ConnectionStringWrapper returnItem = new ConnectionStringWrapper();
foreach(ConnectionStringSettings css in System.Configuration.ConfigurationManager.ConnectionStrings)
{
ConnectionStringEntry cse = new ConnectionStringEntry(css.Name, css.ConnectionString, css.ProviderName);
returnItem.ConnectionStringEntries.Add(cse);
}
if(returnItem.ConnectionStringEntries.Count == 1)
{
/* if there is only one, set the default name to that one */
returnItem.DefaultConnectionStringName = returnItem.ConnectionStringEntries.First().Name;
}
else
{
/*
<packages>
<package id="EnterpriseLibrary.Common" version="6.0.1304.0" targetFramework="net45" />
<package id="EnterpriseLibrary.Data" version="6.0.1304.0" targetFramework="net45" />
</packages>
*/
/* using Microsoft.Practices.EnterpriseLibrary.Data.Configuration; */
/* You can write you own way to handle a default database, or piggyback off of EnterpriseLibrary. You don't necessarily have to use EnterpriseLibrary.Data, you are simply piggybacking on their xml/configuration setup */
DatabaseSettings dbSettings = (DatabaseSettings)ConfigurationManager.GetSection("dataConfiguration");
returnItem.DefaultConnectionStringName = dbSettings.DefaultDatabase;
}
return returnItem;
}
}
和 app.config xml:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data"/>
</configSections>
<connectionStrings>
<clear/>
<add name="MyFirstConnectionStringName" connectionString="Server=.\MyServerOne;Database=OneDB;Trusted_Connection=True;MultipleActiveResultSets=true"
providerName="System.Data.SqlClient" />
<add name="MySecondConnectionStringName" connectionString="Server=.\MyServerTwo;Database=TwoDB;Trusted_Connection=True;MultipleActiveResultSets=true"
providerName="System.Data.SqlClient" />
</connectionStrings>
<dataConfiguration defaultDatabase="MyFirstConnectionStringName" />
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
</configuration>
关键词:
DotNet DotNet .Net Core Classic Json 配置 ICollection 标量和集合同时支持 dotnet 和 dotnetcore
一个老问题,但我今天正在看这个问题,想分享一下...
包含 ProviderName 的简单替代方法
这是一个简单的替代方法,可以避免自定义扩展和更改默认的 ConnectionStrings 配置结构。它基于 Microsoft 如何为 Azure 上的应用程序包含 ProviderName。
解决方案是在指定 ProviderName 的 ConnectionStrings 部分添加上下文相关键。
AppSettings.json 使用 SQLite 提供程序:
{
"ConnectionStrings": {
"MyContext": "Data Source=c:\MySqlite.db;Version=3;",
"MyContext_ProviderName": "System.Data.SQLite",
}
}
并在 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 (...
奖励 - 连接字符串前缀
Microsoft 包含 SQLClient 的预定义前缀,MySQL 将自动包含上述格式的提供程序名称。但是,这些前缀仅在作为环境变量添加时才有效,即不在 appsettings.json 中。例如,使用 MYSQLCONNSTR_ 前缀在 launchSettings.json 中定义连接字符串将填充连接字符串和提供程序名称。有关详细信息,请参阅 Configuration in ASP.NET Core 并向下滚动到 连接字符串前缀
launchSettings.json
{
"profiles": {
"Development": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
// The prefix
"MYSQLCONNSTR_MyContext": "Server=myServerAddress;Database=Green;Uid=myUsername;Pwd=myPassword;"
}
}
}