不为"事务"而"事务"

背景:
    最近在做一个项目,需要用到两个第三方组件:北京莲塘语音组件和CMailSever
前者作为语音聊天室的二次开发组件,后者用于网站的小型邮件系统二次开发组件

需求:
    用户在主程序登陆后,无须再次登陆聊天室和邮件系统,即实现一次登陆,全站通行

下面将给出对应方案以及其对事务处理的依赖程度,并给出解决方案

方案:
    用户在注册时,同时向聊天室和邮件系统注册一个用户,在用户修改密码时对应修改
聊天室和邮件系统,这样当用户进入聊天室和邮件系统,主系统只需要向其传递用户和密码
即可满足需求

该方案存在的问题及其解决方案:
    在用户注册或者修改密码信息时,如果有一个聊天室和邮件系统出现错误,那么用户
将有可能不能登陆其中一个系统,这就提出一个原子事务的问题,为注册和修改密码加上
原子事务处理,C#伪代码如下:

None.gif using  System.EnterpriseServices ;
None.gif[Transaction(TransactionOption.Required)]
None.gif
public   class  Users : System.EnterpriseServices.ServicedComponent
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    [AutoComplete]
InBlock.gif    
public static void ModifyPassword(string userName , string olePassword , string newPassword)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif         ModifyMainSystemPassword(userName,newPassword) ; 
//修改主程序数据库密码
InBlock.gif
         ModifyVoiceChatPassword(userName,newPassword) ;  //修改聊天室密码
InBlock.gif
         ModifyMailSystemPassword(userName,newPassword) ; //修改邮件系统密码
ExpandedSubBlockEnd.gif
    }

ExpandedBlockEnd.gif}

这样就可以实现当有一个系统修改密码出错时都会自动回滚到原来的状态了,OK,问题解决了!

进一步思考:
但事情却没有想象的简单,使用以上代码必须存在一个前提条件:聊天室和邮件系统组件必须支持事务性,
这样才能在应用程序之间共享事务,达到事务提交/回滚的目的

思路暂时中断.......

在CSDN论坛得到思归的帮助:如何对非事务性组件实现事务处理
即使用所谓的资源补偿器(CRMs),下面简要说说实现的步骤
对于第1,2,4步可以在SDK中搜索System.EnterpriseServices.CompensatingResourceManager命名空间得到更
详细的信息,或则参见Compensating Resource Managers (CRMs)

这里贴出代码是为了实现的完整性,第3步是我本人扩充的,SDK无此描述

1,实现一个CRM类

ContractedBlock.gif ExpandedBlockStart.gif using namespace #region using namespace
InBlock.gif
using System;
InBlock.gif
using System.IO;
InBlock.gif
using System.Reflection;
InBlock.gif
using System.EnterpriseServices;
InBlock.gif
using System.EnterpriseServices.CompensatingResourceManager;
InBlock.gif
InBlock.gif[assembly: ApplicationActivation(ActivationOption.Server)]
InBlock.gif[assembly: ApplicationCrmEnabled]
InBlock.gif[assembly: AssemblyKeyFile(
"crm.key")]
ExpandedBlockEnd.gif
#endregion

None.gif
None.gif
namespace  CrmServer 
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
ContractedSubBlock.gifExpandedSubBlockStart.gif
实现代码#region 实现代码
InBlock.gif      [Transaction]
InBlock.gif      
// Create a Worker class.
InBlock.gif
      public class CRMWorker:ServicedComponent
ExpandedSubBlockStart.gifContractedSubBlock.gif      
dot.gif{
InBlock.gif            
public void CRMMethod(string fileName, bool bCommit)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                  
// Create clerk object.
InBlock.gif
                  Clerk clerk = new Clerk(typeof(CRMCompensator), "CRMCompensator", CompensatorOptions.AllPhases);
InBlock.gif                  clerk.WriteLogRecord(fileName);
InBlock.gif                  clerk.ForceLog();
InBlock.gif                  
if (bCommit)
InBlock.gif                        ContextUtil.SetComplete();
InBlock.gif                  
else
InBlock.gif                        ContextUtil.SetAbort();
ExpandedSubBlockEnd.gif            }

InBlock.gif      
ExpandedSubBlockEnd.gif      }

InBlock.gif      
// Create class derived from Compensator class.
InBlock.gif
      public class CRMCompensator:Compensator
ExpandedSubBlockStart.gifContractedSubBlock.gif      
dot.gif{
InBlock.gif            
bool bBeginPrepareCalled = false;
InBlock.gif            
bool bPrepareRecordCalled = false;
InBlock.gif            
bool bBeginCommitCalled = false;
InBlock.gif            
bool bCommitRecordCalled = false;
InBlock.gif            
bool bBeginAbortCalled = false;
InBlock.gif            
bool bAbortRecordCalled = false;
InBlock.gif      
InBlock.gif            String _fileName;
InBlock.gif      
InBlock.gif            
public override void BeginPrepare()
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                  bBeginPrepareCalled 
= true;
ExpandedSubBlockEnd.gif            }

InBlock.gif      
InBlock.gif            
public override bool PrepareRecord(LogRecord rec)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                  Object o 
= rec.Record;
InBlock.gif                  _fileName 
= o.ToString();
InBlock.gif                  bPrepareRecordCalled 
= true;
InBlock.gif                  
return false;
ExpandedSubBlockEnd.gif            }

InBlock.gif      
InBlock.gif            
public override bool EndPrepare()
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                  
if (!bBeginPrepareCalled)
ExpandedSubBlockStart.gifContractedSubBlock.gif                  
dot.gif{return false;}   
InBlock.gif                  
if (!bPrepareRecordCalled)
ExpandedSubBlockStart.gifContractedSubBlock.gif                  
dot.gif{return false;}   
InBlock.gif                  
if (_fileName==null)
ExpandedSubBlockStart.gifContractedSubBlock.gif                  
dot.gif{return false;}
InBlock.gif                  
// This is a Prepare Phase success.
InBlock.gif
                  return true;
ExpandedSubBlockEnd.gif            }

InBlock.gif      
InBlock.gif            
public override void BeginCommit(bool fRecovery)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                  bBeginCommitCalled 
= true;
ExpandedSubBlockEnd.gif            }

InBlock.gif      
InBlock.gif            
public override bool CommitRecord(LogRecord rec)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                  bCommitRecordCalled 
= true;
InBlock.gif                  
return true;
ExpandedSubBlockEnd.gif            }

InBlock.gif      
InBlock.gif            
public override void EndCommit()
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                  
if (!bBeginCommitCalled)
ExpandedSubBlockStart.gifContractedSubBlock.gif                  
dot.gif{return;}   
InBlock.gif                  
if (!bCommitRecordCalled)
ExpandedSubBlockStart.gifContractedSubBlock.gif                  
dot.gif{return;}
InBlock.gif                  
if (_fileName==null)
ExpandedSubBlockStart.gifContractedSubBlock.gif                  
dot.gif{return;}
InBlock.gif                  
// This is a Commit Phase success.
ExpandedSubBlockEnd.gif
            }

InBlock.gif      
InBlock.gif            
public override void BeginAbort(bool fRecovery)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                  bBeginAbortCalled 
= true;
ExpandedSubBlockEnd.gif            }

InBlock.gif      
InBlock.gif            
public override bool AbortRecord(LogRecord rec)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                  bAbortRecordCalled 
= true;
InBlock.gif                  Object o 
= rec.Record;
InBlock.gif                  _fileName 
= o.ToString();
InBlock.gif                  
return true;
ExpandedSubBlockEnd.gif            }

InBlock.gif      
InBlock.gif            
public override void EndAbort()
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                  
if (!bBeginAbortCalled)
ExpandedSubBlockStart.gifContractedSubBlock.gif                  
dot.gif{return;}   
InBlock.gif                  
if (!bAbortRecordCalled)
ExpandedSubBlockStart.gifContractedSubBlock.gif                  
dot.gif{return;}               
InBlock.gif                  
if (_fileName==null)
ExpandedSubBlockStart.gifContractedSubBlock.gif                  
dot.gif{return;}
InBlock.gif                  
// This is an Abort Phase success.
ExpandedSubBlockEnd.gif
            }

InBlock.gif      
ExpandedSubBlockEnd.gif      }

ExpandedSubBlockEnd.gif
#endregion

ExpandedBlockEnd.gif}


2,生成强名,编译成托管dll
[C#]
sn –k crm.key
csc /t:library /r:System.EnterpriseServices.dll crm.cs

3,使用命令行把托管dll注册为com+服务
RegSvcs /:appname : CrmServer CrmServer.dll

4,在应用程序中调用com+服务
[示例代码]

ContractedBlock.gif ExpandedBlockStart.gif using namespace #region using namespace
InBlock.gif
using System;
InBlock.gif
using System.IO;
InBlock.gif
using System.EnterpriseServices;
InBlock.gif
using CrmServer;
InBlock.gif
using System.Runtime.InteropServices;
ExpandedBlockEnd.gif
#endregion

None.gif
class  CRM
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif      
public static int Main()
ExpandedSubBlockStart.gifContractedSubBlock.gif      
dot.gif{           
InBlock.gif            
string logfilename = "crm.log";
InBlock.gif            Console.WriteLine(
"Creating a managed CRM worker objectdot.gif");
InBlock.gif            CRMWorker crmworker 
= new CRMWorker();
InBlock.gif            Console.WriteLine(
"Demonstrating a worker commitdot.gif");
InBlock.gif            crmworker.CRMMethod(logfilename, 
true);
InBlock.gif            Console.WriteLine(
"Demonstrating a worker abortdot.gif");
InBlock.gif            crmworker.CRMMethod(logfilename, 
false);
InBlock.gif            Console.WriteLine(
"DONE!");
InBlock.gif            
return 0;   
ExpandedSubBlockEnd.gif      }

ExpandedBlockEnd.gif}
 

哇哈!终于可以交一份满意的差了!

准备在项目上实现上面的方案了,但是快乐之后仍需理智的思考,这样真的可以实现我们的要求吗 ?
这个方案至少有以下问题:
1,需要在服务器注册com+服务,部署不方便 ;
2,代码结构变复杂了
3,如果我们的web应用程序需要通过mono部署在linux系统呢 ?
......
晕到!别挑刺儿了......

不满是前进的唯一动力,会有更好的解决方案吗 ?我不断问我自己
终于工夫不负有心人,终于想到一个“完美的方案”:
1,分解模块,把聊天室和邮件系统和主系统独立出来
2,在用户注册时,所有用户的聊天室和邮件系统密码都是一样的,这个密码信息存储在配置文件或数据库中
这样当用户进入聊天室和邮件系统传递这个密码过去就OK了
3,用户修改密码时,不需要修改聊天室和邮件系统密码,因为它们永远是一样的
这样做的话,就剩下一个问题,如果注册的时候出错怎么办 ?下面给出两种解决方案:
1,在用户每次登陆前,都重新注册一次[不怎么好]
2,当用户注册时忽略错误,在后台提供一个给管理员手工添加用户信息到聊天室和邮件系统的功能,当用户
出现问题时联系管理员处理[比较好]

众里寻他千百度,蓦然回首,那人却在灯火阑珊处

自得其乐,板砖不断......

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值