近日,做一个分布式数据库定时同步的项目,也就是说有多个物理节点上的数据库需要在每天某时来同步表中的数据,对于某个指定节点上的某个表发生的变化(增量)可以通过对该表执行的SqlCommand来记录,当同步过程发生时,需要对其它所有节点上的同名表执行相同的SqlCommand。由于,同步是定时发生的,所以增量SqlCommand就需要首先被保存起来,开始我们计划将其序列化后保存在数据库中,但是到运行时,问题来了,SqlCommand是不可序列化的!!!所有继承自IDbCommand的类都是不可序列化的,所有继承自IDbParameter的实现类都是不可序列化的。
遇到了这个问题,是我们当初没有料想到的,看来要妥善的解决这个问题,否则,我们的设计和实现不可避免的要进行大量的修改!我们最后采用的解决方案是自己手写一个可以序列化的ESqlCommand,它可以和SqlCommand相互转换,就像这样:
SqlCommand command = new SqlCommand(this.cmdText ,new SqlConnection(connStr) ,null) ;
//正向
ESqlCommand esCommand = new ESqlCommand(command) ;
//反向
SqlCommand command2 = esCommand.ToSqlCommand(connStr) ;
//正向
ESqlCommand esCommand = new ESqlCommand(command) ;
//反向
SqlCommand command2 = esCommand.ToSqlCommand(connStr) ;
有了ESqlCommand之后,我们的设计几乎不需要修改,仅仅在增量序列化保存到数据库之前转换为ESqlCommand,而读出增量字段反序列化之后再转换为SqlCommand即可。
下面给出ESqlCommand的实现(当然,不可避免的也要包含可序列化的ESqlParameter实现):
#region ESqlCommand
/// <summary>
/// ESqlCommand 可序列化的SqlCommand。System.Data.SqlClient.SqlCommand是不可序列化的
/// </summary>
[Serializable]
public class ESqlCommand
{
private ESqlParameter[] esParas ;
private string cmdText ;
private CommandType cmdType ;
public ESqlCommand(SqlCommand command)
{
this.cmdText = command.CommandText ;
this.cmdType = command.CommandType ;
this.esParas = new ESqlParameter[command.Parameters.Count] ;
int index = 0 ;
foreach(SqlParameter para in command.Parameters)
{
this.esParas[index] = new ESqlParameter(para) ;
index++ ;
}
}
public SqlCommand ToSqlCommand(string connStr)
{
SqlCommand command = new SqlCommand(this.cmdText ,new SqlConnection(connStr) ,null) ;
for(int i=0 ;i<this.esParas.Length ;i++)
{
command.Parameters.Add(this.esParas[i].ToSqlParameter()) ;
}
command.CommandType = this.cmdType ;
return command ;
}
}
[Serializable]
public class ESqlParameter
{
public ESqlParameter(SqlParameter sPara)
{
this.paraName = sPara.ParameterName ;
this.paraLen = sPara.Size ;
this.paraVal = sPara.Value ;
this.sqlDbType= sPara.SqlDbType ;
}
public SqlParameter ToSqlParameter()
{
SqlParameter para = new SqlParameter(this.paraName ,this.sqlDbType ,this.paraLen) ;
para.Value = this.paraVal ;
return para ;
}
#region ParaName
private string paraName = "" ;
public string ParaName
{
get
{
return this.paraName ;
}
set
{
this.paraName = value ;
}
}
#endregion
#region ParaLen
private int paraLen = 0 ;
public int ParaLen
{
get
{
return this.paraLen ;
}
set
{
this.paraLen = value ;
}
}
#endregion
#region ParaVal
private object paraVal = null ;
public object ParaVal
{
get
{
return this.paraVal ;
}
set
{
this.paraVal = value ;
}
}
#endregion
#region SqlDbType
private SqlDbType sqlDbType = SqlDbType.NVarChar ;
public SqlDbType SqlDbType
{
get
{
return this.sqlDbType ;
}
set
{
this.sqlDbType = value ;
}
}
#endregion
}
#endregion
/// <summary>
/// ESqlCommand 可序列化的SqlCommand。System.Data.SqlClient.SqlCommand是不可序列化的
/// </summary>
[Serializable]
public class ESqlCommand
{
private ESqlParameter[] esParas ;
private string cmdText ;
private CommandType cmdType ;
public ESqlCommand(SqlCommand command)
{
this.cmdText = command.CommandText ;
this.cmdType = command.CommandType ;
this.esParas = new ESqlParameter[command.Parameters.Count] ;
int index = 0 ;
foreach(SqlParameter para in command.Parameters)
{
this.esParas[index] = new ESqlParameter(para) ;
index++ ;
}
}
public SqlCommand ToSqlCommand(string connStr)
{
SqlCommand command = new SqlCommand(this.cmdText ,new SqlConnection(connStr) ,null) ;
for(int i=0 ;i<this.esParas.Length ;i++)
{
command.Parameters.Add(this.esParas[i].ToSqlParameter()) ;
}
command.CommandType = this.cmdType ;
return command ;
}
}
[Serializable]
public class ESqlParameter
{
public ESqlParameter(SqlParameter sPara)
{
this.paraName = sPara.ParameterName ;
this.paraLen = sPara.Size ;
this.paraVal = sPara.Value ;
this.sqlDbType= sPara.SqlDbType ;
}
public SqlParameter ToSqlParameter()
{
SqlParameter para = new SqlParameter(this.paraName ,this.sqlDbType ,this.paraLen) ;
para.Value = this.paraVal ;
return para ;
}
#region ParaName
private string paraName = "" ;
public string ParaName
{
get
{
return this.paraName ;
}
set
{
this.paraName = value ;
}
}
#endregion
#region ParaLen
private int paraLen = 0 ;
public int ParaLen
{
get
{
return this.paraLen ;
}
set
{
this.paraLen = value ;
}
}
#endregion
#region ParaVal
private object paraVal = null ;
public object ParaVal
{
get
{
return this.paraVal ;
}
set
{
this.paraVal = value ;
}
}
#endregion
#region SqlDbType
private SqlDbType sqlDbType = SqlDbType.NVarChar ;
public SqlDbType SqlDbType
{
get
{
return this.sqlDbType ;
}
set
{
this.sqlDbType = value ;
}
}
#endregion
}
#endregion