最近新接手了一项业务,其中有一个方法,需要对业务表进行写入数据,之后记录到日志表中。这部分代码原先是前人写的,他没有采用任何方案,只是简单的调用Ado.net执行了两次写库操作。因此经常出现系统使用者不断发邮件说数据有问题,经过查看原因就是在于写库操作中,有某个表写入失败,但是其他表写入成功,导致出现了数据不一致的问题。后来本想改用事务,但发现日志表和业务表不在同一个数据库下,甚至不在同一个IP下,对于这个问题,我想到了有以下解决方案。

由ado.net管理的事务改为自己手动提交事务和Commit或者RollBack操作:

step1:按照连接字符串和sql分类,存入Dictionary<string,string>中,Key为连接字符串,Value为针对此数据库的Sql语句,多条用分号隔开;

step2:遍历此Dictionary,打开这些连接;

step3:对于每个连接,打开事务;

step4:执行针对每个连接的sql,出现错误则全部rollback,否则全部commit;

step5:关闭连接,记录运行情况,记录日志。

具体代码如下:

         //提交事务用的sql
public const string MultiTran = @"BEGIN TRAN
{0}"; /// <summary>
/// 事务返回的信息
/// </summary>
public struct TransInfo
{
/// <summary>
/// sql总条数
/// </summary>
public int Total;
/// <summary>
/// 事务执行是否成功
/// </summary>
public bool IsSuccess;
/// <summary>
/// 失败时的sql
/// </summary>
public string WrongMessage;
} /// <summary>
/// 跨库事务异常对象
/// </summary>
public class TransException : Exception
{
public TransException(string message) : base(message)
{
} public string wrongSQL { get; set; }
public string wrongAt { get; set; }
/// <summary>
/// 已经打开的连接
/// </summary>
public List<SqlConnection> DoneConnection = new List<SqlConnection>();
/// <summary>
/// 出现错误的连接
/// </summary>
public SqlConnection CurrentConnection;
/// <summary>
/// 覆盖Exception中的Message字段,使其可写
/// </summary>
public new string Message { get; set; }
} /// <summary>
/// 多操作sql,使用事务,用于多库事务
/// <para>
/// 返回值TransInfo字段:IsSuccess 是否成功,
/// Total sql总条数,
/// WrongAt 失败的sql语句
/// </para>
/// </summary>
/// <param name="sqlwithconn">执行的sql和连接字符串列表key:sql,value:连接字符串</param>
/// <param name="connectionString">连接字符串</param>
/// <returns>sadf</returns>
public static TransInfo RunSqlInTrans(Dictionary<string, string> sqlwithconn)
{
var sqltable = new Dictionary<string, string>();
var conntable = new Dictionary<string, SqlConnection>(); foreach (var i in sqlwithconn)
{
if (!sqltable.Keys.Contains(i.Value))
{
sqltable.Add(i.Value, i.Key); //sqltable的key是连接字符串,value是sql语句
conntable.Add(i.Value, new SqlConnection(i.Value)); //key是连接字符串,value是连接对象
}
else
{
sqltable[i.Value] += ";" + i.Key;
}
} try
{
var wrongEx = new TransException("");
foreach (var i in sqltable)
{
//遵照晚开早关原则,在此处打开数据库连接
conntable[i.Key].Open();
//连接打开后,将连接对象放入异常处理对象中做记录
wrongEx.DoneConnection.Add(conntable[i.Key]);
var dc = new SqlCommand(string.Format(MultiTran, i.Value), conntable[i.Key]);
try
{
dc.ExecuteNonQuery();
}
catch (Exception ex)
{
//出现异常,抛出异常处理对象
wrongEx.CurrentConnection = conntable[i.Key];
wrongEx.wrongAt = i.Key;
wrongEx.wrongSQL = sqltable[i.Key];
wrongEx.Message = ex.Message;
throw wrongEx;
}
}
//全部执行完毕没有发现错误,提交事务
foreach (var i in conntable)
{
var dc = new SqlCommand("COMMIT TRAN", i.Value);
dc.ExecuteNonQuery();
i.Value.Close();
}
return new TransInfo()
{
IsSuccess = true,
Total = sqlwithconn.Count,
WrongMessage = ""
}; }
catch (TransException e) //1.回滚所有操作2.关闭所有已经打开的数据库连接4.生成错误对象
{
foreach (var i in e.DoneConnection)
{
if (!i.Equals(e.CurrentConnection))
{
var dc = new SqlCommand("ROLLBACK TRAN", i);
dc.ExecuteNonQuery();
}
i.Close();
}
return new TransInfo()
{
IsSuccess = false,
Total = sqlwithconn.Count,
WrongMessage = string.Format("在连接{0}中,操作{1}出现错误,错误信息:{2}", e.wrongAt, e.wrongSQL, e.Message)
};
}
}

这样解决了跨库数据表处理有时因为网络问题或其他偶然性问题导致的数据不一致的问题。但是这个解决方案最大的问题就是在于性能问题上,比如如果有多个库假设为A,B,C,D,其中C库的数据修改写入比较复杂,那么在A,B库开启事务后,必须等待C和D库完成或失败后,事务才可以结束,连接才能释放,这个时候,A库和B库就是处于挂起状态,如果处于高IO的生产环境中的话,这个性能的损失可能是致命的,所以这个方案只能用于简单的sql处理,而且处理sql不能太多或者太复杂。而且出现网络波动的话,损失会更大。幸运的是我所接手的这个业务,是在内网环境中,同时只用两句sql在两个库中,所以用这个方案问题不大。

总结:针对这个问题,我认为当初设计数据库时,能避免跨库就一定要避免。

如果大家有什么更好的解决方案的话,希望和大家多多交流和指教。

C#中跨库事务处理解决方案的更多相关文章

  1. PostgreSQL数据库中跨库访问解决方案

    PostgreSQL跨库访问有3种方法:Schema,dblink,postgres_fdw. 方法A:在PG上建立不同SCHEMA,将数据和存储过程分别放到不同的schema上,经过权限管理后进行访 ...

  2. jquery 与其他库冲突解决方案

    var j = jQuery.noConflict(); j("div p").hide(); // 基于 jQuery 的代码 $("content").st ...

  3. postgreSQL中跨库查询在windows下的实现方法

    以下是在postgreSQL 8.1版本中的实践,其他版本类似: 1.将C:\Program Files\PostgreSQL\8.1\share\contrib下的dblink.sql复制到C:\P ...

  4. Python pip包管理器安装第三方库超时解决方案

    一.国内镜像安装 使用方法:pip install --index 镜像网站 第三方库名 二.镜像网站 http://pypi.douban.com/simple/ 豆瓣 http://mirrors ...

  5. Vue中跨域问题解决方案1

    我们需要配置代理.代理可以解决的原因:因为客户端请求服务端的数据是存在跨域问题的,而服务器和服务器之间可以相互请求数据,是没有跨域的概念(如果服务器没有设置禁止跨域的权限问题),也就是说,我们可以配置 ...

  6. SQLServer中跨库复制数据

    SQLServer中把某个表里的记录复制到另一个数据库的表中的操作方法. 场景 现有数据库a和数据库b,数据库a里有表table1,数据库b里有表table2.现在要把表table1里的记录复制到ta ...

  7. 详解EBS接口开发之库事务处理带提前发运通知(ASN)采购接收入库-补充

     A)   Via ROI Create a ASN [ship,ship]  for a quantity =3 on STANDARD PURCHASE ORDER Create  via R ...

  8. C/C++ 跨平台交叉编译、静态库/动态库编译、MinGW、Cygwin、CodeBlocks使用原理及链接参数选项

    目录 . 引言 . 交叉编译 . Cygwin简介 . 静态库编译及使用 . 动态库编译及使用 . MinGW简介 . CodeBlocks简介 0. 引言 UNIX是一个注册商标,是要满足一大堆条件 ...

  9. Android JNI如何调用第三方库

    http://www.2cto.com/kf/201504/388764.html Android JNI找不到第三方库的解决方案 cannot load library 最近做一个jni项目,拿到的 ...

随机推荐

  1. 如何使用C#操作WinAPI

    Windows API是对Windows操作系统的API函数,在C#中调用Windows API的实质是托管代码对非托管代码的调用. 主要使用的格式就是: using System.Runtime.I ...

  2. Java基础数据类型的默认值

    1.整数类型(byte.short.int.long)的基本类型变量的默认值为0. 2.单精度浮点型(float)的基本类型变量的默认值为0.0f. 3.双精度浮点型(double)的基本类型变量的默 ...

  3. python 的时间复杂度

    Python内置方法的时间复杂度 本文翻译自Python Wiki 本文基于GPL v2协议,转载请保留此协议. 本页面涵盖了Python中若干方法的时间复杂度(或者叫“大欧”,“Big O”).该时 ...

  4. 【BZOJ3931】[CQOI2015]网络吞吐量 最大流

    [BZOJ3931][CQOI2015]网络吞吐量 Description 路由是指通过计算机网络把信息从源地址传输到目的地址的活动,也是计算机网络设计中的重点和难点.网络中实现路由转发的硬件设备称为 ...

  5. 开源平台ghost博客系统

    http://docs.ghost.org/installation/windows/ 不会安装的童鞋可以到这里论坛看看 此教程只说windows下的安装: mac的安装教程请移步这里 mac版安装 ...

  6. Android自定义上拉控件SpringView

    Demo 先看一下SpringView的效果图: 1.拖动灰色部分可拖动下方视图,点击jump按钮可让下方视图自行滑动. 使用方法 布局文件: <com.zql.android.springvi ...

  7. 关于 Intellij IDEA Ultimate Edition 14.1控制台中文乱码 解决

    经过尝试,我发现,乱码主要是跟控制台右下角的编码有关  如下图 当然IDE Encoding 和 Project Encoding 你可以都设置位UTF-8 或者都设置为GBK    如下图:

  8. python - 常用的小方法

    常用的小方法: bin() oct() hex() chr() ord() dir() id() len() int() str() type() help() range(10)   /  rang ...

  9. HTTP 协议介绍

    HTTP 协议规定了浏览器和服务器之间互相通信的规则. 请求协议: 规定了客户端发送给服务器的内容格式 响应协议: 服务器发送给客户端的内容格式 请求协议 请求协议格式: 请求行 多个请求头信息(属性 ...

  10. 原!tomcat启动超时(打印了几行日志,后面没了。也不报错,处于启动状态,直到超时)

    项目框架:spring+struts2+mybatis 今天优化代码,改了一堆mybatis dao和xml文件,启动项目时,就出现如标题描述的状况:打印了几行日志,后面就不打印了,也不报错,处于启动 ...