EF架构~通过EF6的DbCommand拦截器来实现数据库读写分离~终结~配置的优化和事务里读写的统一

时间:2022-08-23 02:39:21

回到目录

本讲是通过DbCommand拦截器来实现读写分离的最后一讲,对之前几篇文章做了一个优化,无论是程序可读性还是实用性上都有一个提升,在配置信息这块,去除了字符串方式的拼接,取而代之的是section数组,这样在修改配置时更加清晰了;而实用性上,彻底改变了读和写不能共用一个仓储对象的缺点,并且在一个事务里可以读写并存,并为了数据的一致性,使事务里的curd操作指向主库,这一点很重要!

前几篇文章的目录

EF架构~通过EF6的DbCommand拦截器来实现数据库读写分离~再续~添加对各只读服务器的心跳检测 (2015-01-09 17:52)

EF架构~通过EF6的DbCommand拦截器来实现数据库读写分离~续~添加事务机制 (2015-01-08 14:08)

EF架构~通过EF6的DbCommand拦截器来实现数据库读写分离 (2015-01-07 17:31)

功能架构图如下

aaarticlea/png;base64,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" alt="" />

下面我们来分块看一下这次的修改

一 配置文件的修改

  <configSections>
<section name="DistributedReadWriteSection" type="Project.DistributedReadWriteForEF.DistributedReadWriteSectionHandler, Project.DistributedReadWriteForEF"/>
</configSections>
<DistributedReadWriteSection>
<add key="readDb1" Ip="192.168.2.71" Port="" DbName="background_read1" UserId="sa" Password="zzl123" />
<add key="readDb2" Ip="192.168.2.71" Port="" DbName="TestWrite_Read_Zzl" UserId="sa" Password="zzl123" />
<add key="readDb3" Ip="192.168.2.29" Port="" DbName="TestWrite_Read_Zzl" UserId="sa" Password="" />
</DistributedReadWriteSection>
<appSettings>
<!-- 只读服务器的sql连接串配置模版-->
<add key ="readDbConnection" value="data source={0};initial catalog={1};persist security info=True;user id={2};password={3};multipleactiveresultsets=True;application name=EntityFramework"/>
<add key ="writeDbConnection" value="data source=.;initial catalog=background;persist security info=True;user id=sa;password=zzl123;multipleactiveresultsets=True;application name=EntityFramework"/>
</appSettings>
  /// <summary>
/// redis配置信息加载
/// </summary>
internal class DistributedReadWriteManager
{
/// <summary>
/// 配置信息实体
/// </summary>
public static IList<DistributedReadWriteSection> Instance
{
get
{
return GetSection();
}
} private static IList<DistributedReadWriteSection> GetSection()
{
var dic = ConfigurationManager.GetSection("DistributedReadWriteSection") as Dictionary<string, DistributedReadWriteSection>;
return dic.Values.ToList();
}
}
    /// <summary>
/// DistributedReadWriteForEFSection块,在web.config中提供DistributedReadWriteForEFSection块定义
/// </summary>
internal class DistributedReadWriteSection : ConfigurationSection
{ /// <summary>
/// 主机地址
/// </summary>
[ConfigurationProperty("Ip", DefaultValue = "127.0.0.1")]
public string Ip
{
get { return (string)this["Ip"]; }
set { this["Ip"] = value; }
}
/// <summary>
/// 端口号
/// </summary>
[ConfigurationProperty("Port", DefaultValue = "")]
public int Port
{
get { return (int)this["Port"]; }
set { this["Port"] = value; }
} /// <summary>
/// 数据库名称
/// </summary>
[ConfigurationProperty("DbName", DefaultValue = "Test")]
public string DbName
{
get { return (string)this["DbName"]; }
set { this["DbName"] = value; }
} /// <summary>
/// 数据库账号
/// </summary>
[ConfigurationProperty("UserId", DefaultValue = "sa")]
public string UserId
{
get { return (string)this["UserId"]; }
set { this["UserId"] = value; }
} /// <summary>
/// 数据库账号
/// </summary>
[ConfigurationProperty("Password", DefaultValue = "sa")]
public string Password
{
get { return (string)this["Password"]; }
set { this["Password"] = value; }
}
}
    internal class DistributedReadWriteSectionHandler : IConfigurationSectionHandler
{
#region IConfigurationSectionHandler 成员 public object Create(object parent, object configContext, System.Xml.XmlNode section)
{
Dictionary<string, DistributedReadWriteSection> names = new Dictionary<string, DistributedReadWriteSection>(); string _key = string.Empty;
string _ip = string.Empty;
string _dbName = string.Empty;
string _userId = string.Empty;
string _password = string.Empty;
int _port = ; foreach (XmlNode childNode in section.ChildNodes)
{
if (childNode.Attributes["key"] != null)
{
_key = childNode.Attributes["key"].Value; if (childNode.Attributes["Ip"] != null)
{
_ip = childNode.Attributes["Ip"].Value;
}
if (childNode.Attributes["Port"] != null)
{
_port = Convert.ToInt32(childNode.Attributes["Port"].Value);
}
if (childNode.Attributes["DbName"] != null)
{
_dbName = childNode.Attributes["DbName"].Value;
}
if (childNode.Attributes["UserId"] != null)
{
_userId = childNode.Attributes["UserId"].Value;
}
if (childNode.Attributes["Password"] != null)
{
_password = childNode.Attributes["Password"].Value;
}
names.Add(_key, new DistributedReadWriteSection { Ip = _ip, Port = _port, DbName = _dbName, UserId = _userId, Password = _password });
}
}
return names;
} #endregion
}

二 仓储大叔事务块修改

   public static void UsingNoMsdtc(IUnitOfWork db, bool isOutest, Action action)
{
var objectContext = ((System.Data.Entity.Infrastructure.IObjectContextAdapter)db).ObjectContext;
try
{ objectContext.Connection.Close();
//强制将所有curd操作维持到主库
Project.DistributedReadWriteForEF.CommandInterceptor.IsTransactionScope = true;
//重新设置链接串
if (System.Configuration.ConfigurationManager.AppSettings["writeDbConnection"] != null)
objectContext.TransactionHandler.DbContext.Database.Connection.ConnectionString = System.Configuration.ConfigurationManager.AppSettings["writeDbConnection"]; objectContext.Connection.Open(); using (TransactionScope trans = new TransactionScope())
{
action();
trans.Complete();
Project.DistributedReadWriteForEF.CommandInterceptor.IsTransactionScope = false;//事务结束将走读写分离
}
}
finally
{
if (isOutest)//如果是最外层事务,再将连接关闭!内部事务与外部事务需要共用一个Connection的连接
objectContext.Connection.Close(); //只能关闭,不能dispose,因为dispose之后,上下文就无法得到链接串了
}
}

三 DbCommand拦截器的修改

    /// <summary>
/// SQL命令拦截器
/// 主要实现EF的读写分离
/// </summary>
public class CommandInterceptor : DbCommandInterceptor
{
static CommandInterceptor()
{
readConnList = DistributedReadWriteManager.Instance; sysTimer.Enabled = true;
sysTimer.Elapsed += sysTimer_Elapsed;
sysTimer.Start();
}
/// <summary>
/// 是否在一个事务中,如果是select,insert,update,delete都走主库
/// ThreadStatic标识它只在当前线程有效
/// </summary>
[ThreadStatic]
public static bool IsTransactionScope = false;
/// <summary>
/// 锁住它
/// </summary>
private static object lockObj = new object();
/// <summary>
/// 定期找没有在线的数据库服务器
/// </summary>
private static Timer sysTimer = new Timer();
/// <summary>
/// 读库,从库集群,写库不用设置走默认的EF框架
/// </summary>
private static IList<DistributedReadWriteSection> readConnList; #region Private Methods
private static void sysTimer_Elapsed(object sender, ElapsedEventArgs e)
{
if (readConnList != null && readConnList.Any())
{
foreach (var item in readConnList)
{
//心跳测试,将死掉的服务器IP从列表中移除
var client = new TcpClient();
try
{
client.Connect(new IPEndPoint(IPAddress.Parse(item.Ip), item.Port));
}
catch (SocketException)
{
//异常,没有连接上
readConnList.Remove(item);
}
if (!client.Connected)
{
readConnList.Remove(item);
}
}
}
} /// <summary>
/// 处理读库字符串
/// </summary>
/// <returns></returns>
private string GetReadConn()
{
if (readConnList != null && readConnList.Any())
{
var resultConn = readConnList[Convert.ToInt32(Math.Floor((double)new Random().Next(, readConnList.Count)))];
return string.Format(System.Configuration.ConfigurationManager.AppSettings["readDbConnection"]
, resultConn.Ip
, resultConn.DbName
, resultConn.UserId
, resultConn.Password);
}
return string.Empty;
}
/// <summary>
/// 只读库的选择,加工command对象
/// 说明:事务中,所有语句都走主库,事务外select走读库,insert,update,delete走主库
/// 希望:一个WEB请求中,读与写的仓储使用一个,不需要在程序中去重新定义
/// </summary>
/// <param name="command"></param>
private void ReadDbSelect(DbCommand command)
{
if (!string.IsNullOrWhiteSpace(GetReadConn()))//如果配置了读写分离,就去实现
{
command.Connection.Close();
if (!command.CommandText.StartsWith("insert", StringComparison.InvariantCultureIgnoreCase) && !IsTransactionScope)
command.Connection.ConnectionString = GetReadConn();
command.Connection.Open();
}
}
#endregion #region Override Methods
/// <summary>
/// Linq to Entity生成的update,delete
/// </summary>
/// <param name="command"></param>
/// <param name="interceptionContext"></param>
public override void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
{
base.NonQueryExecuting(command, interceptionContext);//update,delete等写操作直接走主库
}
/// <summary>
/// 执行sql语句,并返回第一行第一列,没有找到返回null,如果数据库中值为null,则返回 DBNull.Value
/// </summary>
/// <param name="command"></param>
/// <param name="interceptionContext"></param>
public override void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
{
ReadDbSelect(command);
base.ScalarExecuting(command, interceptionContext);
}
/// <summary>
/// Linq to Entity生成的select,insert
/// 发送到sqlserver之前触发
/// warning:在select语句中DbCommand.Transaction为null,而ef会为每个insert添加一个DbCommand.Transaction进行包裹
/// </summary>
/// <param name="command"></param>
/// <param name="interceptionContext"></param>
public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
ReadDbSelect(command);
base.ReaderExecuted(command, interceptionContext);
}
/// <summary>
/// 发送到sqlserver之后触发
/// </summary>
/// <param name="command"></param>
/// <param name="interceptionContext"></param>
public override void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
base.ReaderExecuted(command, interceptionContext);
} #endregion
}

好了,到这里,通过拦截器来实现数据库读写分离的方案就彻底完成了,这个版本应该算是个终级了吧,呵呵!感谢您的阅读!

回到目录