In the real business system, when the single database can not carry load pressure, generally we use separate read and write database database load sharing manner. Bear the main library and write transaction operations, bear a read operation from the library.
In order to support a variety of database we first define a data type dictionary. a connection string for the key, value for the database type:
/// <summary> /// 数据库方言集合 /// </summary> private readonly Dictionary<string, DatabaseDialectEnum> DialectDictionary = new Dictionary<string, DatabaseDialectEnum> { ["sqlconnection"] = DatabaseDialectEnum.MSSQL, ["sqlceconnection"] = DatabaseDialectEnum.SQLCE, ["npgsqlconnection"] = DatabaseDialectEnum.POSTGRES, ["sqliteconnection"] = DatabaseDialectEnum.SQLLITE, ["mysqlconnection"] = DatabaseDialectEnum.MYSQL, ["fbconnection"] = DatabaseDialectEnum.FIREBASE };
We switch to a different database only need to configure the database connection string can be.
In mssql an example, the configuration database connection string
"ConnectionString": { "sqlconnection": "Data Source=.;Initial Catalog=Db;User ID=sa;Password=**;Enlist=false;Max Pool SIZE=500;Min Pool SIZE=50;MultipleActiveResultSets=True", "sqlconnection_slaver_1": "Data Source=.;Initial Catalog=Db;User ID=sa;Password=**;Enlist=false;Max Pool SIZE=500;Min Pool SIZE=50;MultipleActiveResultSets=True", "sqlconnection_slaver_2": "Data Source=.;Initial Catalog=Db;User ID=sa;Password=**;Enlist=false;Max Pool SIZE=500;Min Pool SIZE=50;MultipleActiveResultSets=True" }
Key: sqlconnection primary library (master) connection string, Key: sqlconnection_slaver_1 sqlconnection_slaver_2 and two connection strings from the library (slaver). More from the library (slaver) random access can be achieved. Other algorithms can also be used for load balancing.
The connection configuration string we get master database connection string, the connection string and set from the library. Simultaneously determining the type of the database according to the connection string Key . code show as below:
/// <Summary> /// primary database connection string /// </ Summary> Private String MasterConnectionString { GET ; SET ;} /// <Summary> /// from the database a set of strings /// </ Summary> Private List < String > SlaverConnectionStrings { GET ; SET ;} = new new List < String > (); public the ConnectionFactory (Configuration IConfiguration, ILoggerFactory LoggerFactory) { _logger= loggerFactory.CreateLogger<ConnectionFactory>(); var connectionKeys = configuration.GetSection("ConnectionString").GetChildren().Select(s => s.Key).ToArray(); foreach (var connKey in connectionKeys) { var connSplit = connKey.Split('_'); if (connSplit.Length == 1) { MasterConnectionString = configuration[$"ConnectionString:{connKey}"];
// The connecting string agreed to determine the type of database DatabaseDialect = DialectDictionary [connKey]; } the else { SlaverConnectionStrings.Add (Configuration [$ " the ConnectionString: connKey {} " ]); } } }
/// <Summary> /// database type /// </ Summary> public DatabaseDialectEnum DatabaseDialect { GET ; Private SET ;}
Get connected to the main library
private IDbConnection GetMasterConnection() { return GetConnection(MasterConnectionString); }
Get the connection from the library, where the random algorithm, if no configuration from the library, this library will return to the main connection.
Private the IDbConnection GetSlaverConnection () { int SC = SlaverConnectionStrings.Count (); IF (SC> 0 ) { the Random Random = new new the Random (); int index = Random.Next ( 0 , SC); return the GetConnection (SlaverConnectionStrings [index]) ; } the else { _logger.LogInformation ( " not set from the library, the library will establish the main connection " ); return GetMasterConnection (); } }
private IDbConnection GetConnection(string connectionString) => DatabaseDialect switch { DatabaseDialectEnum.MSSQL =>new ProfiledDbConnection(new SqlConnection(connectionString),MiniProfiler.Current), DatabaseDialectEnum.MYSQL => new ProfiledDbConnection(new MySqlConnection(connectionString), MiniProfiler.Current), _ => throw new NotImplementedException() };
NOTE: MiniProfiler used here to monitor the performance of the database connection, the connection was returned packaging ProfiledDbConnection.
Master from the data source types are as follows:
public enum DataSourceEnum { MASTER, SLAVE }
ConnectionFactory present embodiment is a single mode, there is a case of multi-threaded access, so the data source is set to ThreadLocal <DataSourceEnum>, the threads share.
private static ThreadLocal<DataSourceEnum> threadLocal = new ThreadLocal<DataSourceEnum>(); /// <summary> /// 当前线程数据源 /// </summary> /// <param name="sourceEnum"></param> public DataSourceEnum DataSource { set { threadLocal.Value = value; } get { return threadLocal.Value; } }
The following formal acquisition IDbConnection
public IDbConnection GetDbConnection() { if (DataSource == DataSourceEnum.MASTER) { return GetMasterConnection(); } else { return GetSlaverConnection(); } }
use:
According to the actual beginning of the article described the operation to be accessed from the main repository for.
private IDbConnection GetDbConnection(DataSourceEnum dataSource) { ConnectionFactory.DataSource = dataSource; return ConnectionFactory.GetDbConnection(); }
using var connection = GetDbConnection(DataSourceEnum.MASTER); connection.Execute(sql, param, CurrentTransaction, null, commandType)
using var connection = GetDbConnection(DataSourceEnum.SLAVE); connection.Get<T>(id, CurrentTransaction, CommandTimeout)
Offer all the code
public class ConnectionFactory : IConnectionFactory { private readonly ILogger _logger; private static ThreadLocal<DataSourceEnum> threadLocal = new ThreadLocal<DataSourceEnum>(); static ConnectionFactory() { //设置dapper的tableName取值 SqlMapperExtensions.TableNameMapper = (type) => type.Name; } /// <summary> /// 当前线程数据源 /// </summary> /// <param name="sourceEnum"></param> public DataSourceEnum DataSource { SET {threadLocal.Value = value;} GET { return threadLocal.Value;} } /// <Summary> /// primary database connection string /// </ Summary> Private String MasterConnectionString { GET ; SET ;} // / <Summary> /// from the database a set of strings /// </ Summary> Private List < String > SlaverConnectionStrings { GET ; sET ;} = new new List < String >(); public ConnectionFactory(IConfiguration configuration, ILoggerFactory loggerFactory) { _logger = loggerFactory.CreateLogger<ConnectionFactory>(); var connectionKeys = configuration.GetSection("ConnectionString").GetChildren().Select(s => s.Key).ToArray(); foreach (var connKey in connectionKeys) { var connSplit = connKey.Split('_'); if (connSplit.Length == 1) { MasterConnectionString = configuration[$"ConnectionString:{connKey}"]; DatabaseDialect = DialectDictionary[connKey]; } else { SlaverConnectionStrings.Add(configuration[$"ConnectionString:{connKey}"]); } } } /// <summary> /// 数据库方言集合 /// </summary> private readonly Dictionary<string, DatabaseDialectEnum> DialectDictionary = new Dictionary<string, DatabaseDialectEnum> { ["sqlconnection"] = DatabaseDialectEnum.MSSQL, ["sqlceconnection"] = DatabaseDialectEnum.SQLCE, ["npgsqlconnection"] = DatabaseDialectEnum.POSTGRES, ["sqliteconnection"] = DatabaseDialectEnum.SQLLITE, ["mysqlconnection"] = DatabaseDialectEnum.MYSQL, ["fbconnection"] = DatabaseDialectEnum.FIREBASE }; /// <summary> /// 数据库方言 /// </summary> public DatabaseDialectEnum DatabaseDialect { get; private set; } private IDbConnection GetConnection(string connectionString) => DatabaseDialect switch { DatabaseDialectEnum.MSSQL =>new ProfiledDbConnection(new SqlConnection(connectionString),MiniProfiler.Current), DatabaseDialectEnum.MYSQL => new ProfiledDbConnection(new MySqlConnection(connectionString), MiniProfiler.Current), _ => throw new NotImplementedException() }; public IDbConnection GetDbConnection() { if (DataSource == DataSourceEnum.MASTER) { return GetMasterConnection(); } else { return GetSlaverConnection(); } } private IDbConnection GetMasterConnection() { return GetConnection(MasterConnectionString); } private IDbConnection GetSlaverConnection() { int sc = SlaverConnectionStrings.Count(); if (sc > 0) { Random random = new Random(); int index = random.Next(0, sc); return GetConnection(SlaverConnectionStrings[index]); } else { _logger.LogInformation(" It is not provided from the library, from the library to establish the main connection " ); return GetMasterConnection (); } } } public enum DataSourceEnum { the MASTER, SLAVE }