.net 基础类库.实列.System.Transactions.自定义事物 (对新正加的事物类进行讲解)
自定义事物类,以及 System.Transactions 下的一些类库的使用
线程级别静态变量(这个子要在变量上加个 [ThreadStatic] 就可高定)
文件io访问(这个就不讲解了太没意识了MSDN说的很清楚)
写了一个小程序和大家分享一下成果 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里真得有可以支持事物的 文件读写类笔者没有发现,如果谁发现了记得通知一下啊...