.net 基础类库.实列.System.Transactions.自定义事物 (对2.0 加的事物类进行讲解)
.net 基础类库.实列.System.Transactions.自定义事物 (对新正加的事物类进行讲解)
涉及技术
自定义事物类,以及 System.Transactions 下的一些类库的使用
线程级别静态变量(这个子要在变量上加个 [ThreadStatic] 就可高定)
文件io访问(这个就不讲解了太没意识了MSDN说的很清楚)
自定义事物类,以及 System.Transactions 下的一些类库的使用
线程级别静态变量(这个子要在变量上加个 [ThreadStatic] 就可高定)
文件io访问(这个就不讲解了太没意识了MSDN说的很清楚)
上篇文章 “状态机工作流.实列.报销审批流程(三) ” 发表之后发现IPendingWork 接口会传入一个 System.Transactions.Transaction 对象于是产生了兴趣研究了2个小时
写了一个小程序和大家分享一下成果 2006-10-09 23:28
写了一个小程序和大家分享一下成果 2006-10-09 23:28
可能很多人已经知道很多数据库操作对象都已经支持 “事物性代码”如果不知道去看看 TransactionScope 类的msdn的帮助去,难道只有数据库对象会自动支持这种事物吗?
那当然是不可能的,不过作者找了半天也没有发现那个类的帮助上写了会支持“事物性代码”、子找到如下字样
System.Transactions 基础结构通过支持在 SQL Server、ADO.NET、MSMQ 和 Microsoft 分布式事务协调器 (MSDTC) 中启动的事务,使事务编程在整个平台上变得简单和高效、看来没别的对象了。
自己能做一个吗?答案是肯定的,继续搜寻MSDN 发现一个 IEnlistmentNotification 接口子要实现他,在用 Transaction.Current.EnlistVolatile 登记一下就应该好使了, MSDN上有一个简单的列子在 IEnlistmentNotification 接口的下面,不过那个列子太简单了几乎是简单到啥用都没有的地步了
还是自己给自己搞个需求吧要不没法做的!
需求如下:
- 要做一个多文件读写删除,保存如果失败就回滚的例子
- 在多个函数里写不同的文件最后回滚
- 适合在ASP.net那种多线程访问的情况下使用没有问题
哎文件 io里的那些流都不支持 这个郁闷还真得好好设计一下,怎么自持多个文件的回滚那当然是放到列表里的了!怎么在多个函数里都可以使用哪?只能提供一个单列的对象了看来
,不过存静态的单列有线程问题,不能用哎!.
对了用那个 [ThreadStatic] 标签,这个标签我用了好久很爽尤其在Web那种多线程可能并发的情况下...
可以了啥都不缺了开始做吧
[程序下载 / download],是一个命令行程序演示没图
类设计/使用方法说明
最后
- transFileModel
/// <summary>
/// 文件路经模块类,用于保存一些路径信息
/// </summary>
internal class transFileModel
{
//备份文件目录
public string BakFileDir = string.Empty;
//文件全路径
public string FileAllPath = string.Empty;
//备份全路径
public string BakGuidAllPath = string.Empty;
}
- ContextData
/// <summary>
/// 对象保存类,保存要进行事物处理的文件路经
/// </summary>
internal class ContextData
{
public static object _olock = new object();
/// <summary>
/// 文件路径模块列表
/// </summary>
public List<transFileModel> FileList = new List<transFileModel>();
static ContextData()
{
}
/// <summary>
/// 注意:线程级别静态变量,否则这个类会有线程冲突
/// </summary>
[ThreadStatic]
public static ContextData staticData = new ContextData();
/// <summary>
/// 文件路径模块列表对应的属性
/// </summary>
public static List<transFileModel> BakList
{
get
{
return staticData.FileList;
}
}
/// <summary>
/// 添加一个 文件到 BakList
/// </summary>
/// <param name="bakDir"></param>
/// <param name="filePath"></param>
public static void AddFile(string bakDir, string filePath)
{
bakDir = Path.GetFullPath(bakDir);
filePath = Path.GetFullPath(filePath);
lock (_olock)
{//安全起见还是 lock 一下
if (!Directory.Exists(bakDir))
Directory.CreateDirectory(bakDir);
}
string GuidFileName = string.Empty;
string bakGuidAllPath = string.Empty;
if (File.Exists(filePath))
{//文件如果存在就备份到备份目录,并已Guid.bak方式存储
GuidFileName = Guid.NewGuid().ToString() + ".bak";
bakGuidAllPath = Path.Combine(bakDir, GuidFileName);
File.Copy(filePath, bakGuidAllPath, true);
Console.WriteLine("文件以成功copy");
}
transFileModel model = new transFileModel();
model.BakFileDir = bakDir;
model.BakGuidAllPath = bakGuidAllPath;
model.FileAllPath = filePath;
staticData.FileList.Add(model);
}
public static void ClearFileList()
{
BakList.Clear();
}
}
- TransactionTools
public class TransactionTools : System.Transactions.IEnlistmentNotification
{
#region IEnlistmentNotification 成员
public static object _olock = new object();
public TransactionTools()
{
if (Transaction.Current== null ) throw new ApplicationException("靠!没开事物那");
Transaction.Current.EnlistVolatile(this,EnlistmentOptions.None);
}
~TransactionTools()
{
}
public static void RegFile(string BakDir,string filePath)
{
ContextData.AddFile(BakDir,filePath);
}
public void Commit(System.Transactions.Enlistment enlistment)
{
Console.WriteLine("通知登记的对象事务正在提交。:");
ContextData.ClearFileList();
enlistment.Done();
}
public void InDoubt(System.Transactions.Enlistment enlistment)
{
Console.WriteLine("通知登记的对象事务的状态不确定。:");
throw new Exception("The method or operation is not implemented.");
}
public void Prepare(System.Transactions.PreparingEnlistment preparingEnlistment)
{
//throw new Exception("The method or operation is not implemented.");
//在这里应该判断,原始文件是否可写,备份文件是否可读
Console.WriteLine("通知登记的对象事务正在为提交做准备。:");
preparingEnlistment.Prepared();
}
public void Rollback(System.Transactions.Enlistment enlistment)
{
Console.WriteLine("通知登记的对象事务正在回滚。:");
List<transFileModel> transLsit = ContextData.BakList;
if(transLsit.Count > 0)
{
foreach(transFileModel mod in transLsit)
{
if(mod.BakGuidAllPath.Length == 0 && File.Exists(mod.FileAllPath))
{//文件是新建立的,而且存在的话删除
Console.WriteLine("正在回滚删除文件:", mod.FileAllPath);
File.Delete(mod.FileAllPath);
}
else if(mod.BakGuidAllPath.Length != 0)
{//文件有bak路径证明,他不是新建了的需要回滚
Console.WriteLine("正在会滚文件:", mod.FileAllPath);
File.Copy(mod.BakGuidAllPath,mod.FileAllPath,true);
File.Delete(mod.BakGuidAllPath);
}
}
}
ContextData.ClearFileList();
enlistment.Done();
}
#endregion
}- 使用演示
static class Program
{
static readonly string BakPath = Path.Combine(Application.StartupPath, "Bak");
//测试函数一
static void writerFile1()
{
string fpath = Path.Combine(Application.StartupPath, "a.txt");
TransactionTools.RegFile(BakPath, fpath);
using (StreamWriter sw = new StreamWriter(fpath, true))
{
Console.WriteLine("正写入:{0}", fpath);
sw.WriteLine("A:{0:yyyy-MM-dd HH:mm:ss.ffff}",DateTime.Now);
}
}
//测试函数二
static void writerFile2()
{
string fpath = Path.Combine(Application.StartupPath, "b.txt");
TransactionTools.RegFile(BakPath, fpath);
using (StreamWriter sw = new StreamWriter(fpath , true))
{
Console.WriteLine("正写入:{0}", fpath);
sw.WriteLine("B:{0:yyyy-MM-dd HH:mm:ss.ffff}", DateTime.Now);
}
}
static void DBUpdate1()
{//数据库操作对象支持 事物性代码,起码,sqlserver 的一系列、对象支持这里我就不写更新数据库的了,自己加上吧
}
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main()
{
using(TransactionScope ts = new System.Transactions.TransactionScope())
{
Transaction.Current.TransactionCompleted+=new TransactionCompletedEventHandler(Current_TransactionCompleted);
TransactionTools tt = new TransactionTools();
//调用好多个函数有数据库的和文件的
writerFile1();
writerFile2();
DBUpdate1();
Console.Write("输入[Y] 提交否则会滚");
char c = (char)Console.Read();
if(c.ToString().ToUpper()=="Y")
ts.Complete();
}
Console.Read();
//Application.EnableVisualStyles();
//Application.SetCompatibleTextRenderingDefault(false);
//Application.Run(new Form1());
}
static void Current_TransactionCompleted(object sender, TransactionEventArgs e)
{
Console.WriteLine("事物状态:{0}" , e.Transaction.TransactionInformation.Status);
//throw new Exception("The method or operation is not implemented.");
}
}这个文档写的比较草、也许2.0里真得有可以支持事物的 文件读写类笔者没有发现,如果谁发现了记得通知一下啊...