不为事务而事务

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

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

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

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

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

None.gifusing 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.gifExpandedBlockStart.gifusing 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.gifExpandedBlockStart.gifusing 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,当用户注册时忽略错误,在后台提供一个给管理员手工添加用户信息到聊天室和邮件系统的功能,当用户
出现问题时联系管理员处理[比较好]

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

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

转载于:https://www.cnblogs.com/kwklover/archive/2004/12/02/71800.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/380711.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

bugzilla学习

October 03, 2003 bugzilla学习 Bugzilla是一个bug追踪系统,用以管理bug提交、bug消除,不仅能降低同样错误的重复发生,提高开效率,而且有助于项目管理的难度。更有人打算用借助此系统,用前人的bug来教育新来的程序员&a…

php的list函数

作用&#xff1a;把索引数组中的值赋给一组变量&#xff0c;像 array() 一样&#xff0c;这不是真正的函数&#xff0c;而是语言结构。 list() 可以在单次操作内就为一组变量赋值。 <?phpheader(content-type:text/html;charsetutf-8);$personarray(DL_one,18,man);list($…

我也终于有被认为是高手的时候了,^_^

昨天&#xff0c;马超打电话&#xff0c;让我回去给老同事讲安装的制作过程&#xff0c;说什么——他们不会。 汗......心想&#xff0c;当初谁教我啊?!还不都是自己学习摸索的&#xff0c;可现如今人家话说到这儿&#xff0c;也不好硬搪塞掉&#xff0c;去就去呗&…

php的range函数

range() 函数用于创建一个包含指定范围的元素的数组。 语法&#xff1a; range(low,high,step) “”“ low:起始值 high&#xff1a;最大值 step&#xff1a;步长&#xff0c;可写可不写&#xff0c;默认为1 ”“”<?phpheader(content-type:text/html;charsetutf-8);$arr…

php常量变量连接,PHP常量及变量区别原理详解

常量&#xff1a;用于储存一个不会变化也不希望变化的数据的标示符(命名规则与变量相同)定义形式&#xff1a;使用 define() 函数定义使用形式&#xff1a;define(“常量名” &#xff0c;常量值)使用 counst 语法定义使用形式&#xff1a;counst 常量名 常量值使用常量&#…

字符串最长回文子串_最长回文子串

字符串最长回文子串Problem statement: 问题陈述&#xff1a; Given a string str, find the longest palindromic substring. A substring need to be consecutive such that for any xixj i<j must be valid in the parent string too. Like "incl" is a subst…

一个人在办公室的日子

同我一起工作的那个大学同学兼同事ALICE因为个人原因,最近请假了一个星期.剩下了孤单的我在公司应付日常英文翻译书写工作。的确有点闷&#xff0c;的确有些不习惯&#xff0c;点讲&#xff0c;习惯了两个人一起吃饭聊天&#xff0c;一起拼命赶稿子&#xff0c;一起饭后散步&am…

php的array_merge函数

array_merge函数用于把一个或多个数组合并为一个数组 语法&#xff1a; array_merge(array1,array2,array3...)<?phpheader(content-type:text/html;charsetutf-8);$a1array("a">"red","b">"green");$a2array("c"…

dlf packet_DLF的完整形式是什么?

dlf packetDLF&#xff1a;德里土地和金融 (DLF: Delhi Land and Finance) DLF is an abbreviation of Delhi Land and Finance. Delhi Land and Finance is one of the leading commercial real estate developers in India. In 1946, the company was established by Chaudha…

python求三个数中最小(大)的元素

求最小&#xff1a; def getThreeNumberMin(x,y,z):minx if x<y else yminmin if min<z else zreturn min agetThreeNumberMin(3,-1,-1) print(a)结果&#xff1a; 求最大&#xff1a; def getThreeNumberMin(x,y,z):maxx if x>y else ymaxmax if max>z else zr…

java内存分配空间大小,JVM内存模型及内存分配过程

一、JVM内存模型JVM主要管理两种类型内存&#xff1a;堆(Heap)和非堆(Permanent区域)。1、Heap是运行时数据区域&#xff0c;所有类实例和数组的内存均从此处分配。Heap区分两大块&#xff0c;一块是 Young Generation&#xff0c;另一块是Old Generation&#xff1a;1)在Young…

python自动翻译pdf_在Python中自动执行PDF

python自动翻译pdfModules used: 使用的模块&#xff1a; In this script, we will use PyPDF2 module which will provide us various functions such as to extract the data and read the pdf file and split the file and write a new file. 在此脚本中&#xff0c;我们将…

设置DVWA出现Could not connect to the MySQL service. Please check the config的解决方法,默认登录账号

按照这个路径&#xff0c;找到config.inc.php文件&#xff0c;打开 找到下面三个语句 db_server:一般填127.0.0.1&#xff0c;如果修改了mysql的端口号&#xff0c;要在后面加上修改后的端口号&#xff0c;默认为3306 db_user:自己mysql数据库的用户名 db_password&#xff1…

关于用户角色权限的一点想法(1) 选择自 biggie 的 Blog

原文&#xff08;http://dev.csdn.net/article/19/19751.shtm&#xff09; 前言&#xff1a;权限往往是一个极其复杂的问题&#xff0c;但也可简单表述为这样的逻辑表达式&#xff1a;判断“Who对What(Which)进行How的操作”的逻辑表达式是否为真。针对不同的应用&#xff0c;需…

使用anconada 的conda更换环境

打开命令行界面。cmd&#xff0c;直接打开 查看有些环境 conda env list 我这里有两个环境使用指定的环境 我这里就用py27 命令&#xff1a;activate环境名 py27在前面&#xff0c;已经成功更换了退出使用某个环境 conda deactivate 前面已经没有py27&#xff0c;表示已经退…

php采集分页数据,如何通过php+wordpress实现分页获取数据

1.首先我们通过WordPress来搭建我们的博客网站&#xff0c;需要实现分页获取数据&#xff0c;我们需要了解一下WordPress给我们提供的api。主要是get_posts()这个api的使用方法。函数的结构大概长这么个样子&#xff1a;<?php get_posts($args); ?> &#xff0c;其中…

家纺B2C优雅100获IDG及DCM 1000万美元投资

网易科技讯 3月3日下午动静&#xff0c;家纺网上商城优雅100(uya100.com) 首创人陈腾华往日吐露&#xff0c;该公司明天不日完成了1000万美元的首轮融资&#xff0c;投资方为IDG及DCM。陈腾华以有合同约定为由拒绝流露更详细的财务细节。陈腾华说&#xff0c;这1000万美元曾经到…

手动打开和关闭windows的相关服务

winR&#xff0c;输入services.msc 找到指定的服务打开或者关闭

PetShop之ASP.NET缓存(转载)

《解剖PetShop》系列之四 四 PetShop之ASP.NET缓存 如果对微型计算机硬件系统有足够的了解&#xff0c;那么我们对于Cache这个名词一定是耳熟能详的。在CPU以及主板的芯片中&#xff0c;都引入了这种名为高速缓冲存 储器&#xff08;Cache&#xff09;的技术。因为Cache的存取速…

使用python学线性代数_最简单的神经网络简介| 使用Python的线性代数

使用python学线性代数A neural network is a powerful tool often utilized in Machine Learning because neural networks are fundamentally very mathematical. We will use our basics of Linear Algebra and NumPy to understand the foundation of Machine Learning usin…