逍遥一派 发表于 2015-1-16 22:39:45

ASP.NET网页编程之在.NET 2.0 中利用自界说事件操纵

以前很热炒跨平台,主要是由于硅谷挑战微软霸主地位的热情,但是冷静下来后,跨平台往往不是那么一回事。假设你有个软件,所谓的跨平台,你只需要为第二个平台上重新编译一次就行了,这样很难么?  .net2.0framework中新增了System.Transactions定名空间,个中供应的一系列接口和类使得在.net2.0中利用事件比起夙昔要便利了很多。有关在.net2.0下操纵数据库事件的文章已有了良多,这里只提一下怎样计划自界说事件操纵。

  1、事件利用基本

  先看一段利用事件的代码:

1using(TransactionScopets=newTransactionScope())
2{
3//自界说操纵
4ts.Complete();
5}
  这里利用using语句界说了一段隐性事件。假如我们在该语句块中到场一段对SQLServer操纵的代码,那末它们将会主动到场这个事件。能够看出,这类事件的利用体例是极为便利的。

  那末,有无大概在该语句块中到场我们本人界说的事件操纵,而且该操纵可以跟着全部事件块的乐成而提交,随其失利而回滚呢?谜底固然是能够的,不然我就不会写这篇漫笔了。

  2、完成自界说事件操纵

  依据事件的特征,我们能够推想:这个操纵必需有完成提交和回滚之类举措的办法。没错,这就是System.Transactions定名空间中的IEnlistmentNotification接口。我们先写一个最复杂的完成:

1classSampleEnlistment1:IEnlistmentNotification
2{
3voidIEnlistmentNotification.Commit(Enlistmentenlistment)
4{
5Console.WriteLine("提交!");
6enlistment.Done();
7}
8
9voidIEnlistmentNotification.InDoubt(Enlistmentenlistment)
10{
11thrownewException("Themethodoroperationisnotimplemented.");
12}
13
14voidIEnlistmentNotification.Prepare(PreparingEnlistmentpreparingEnlistment)
15{
16Console.WriteLine("筹办!");
17preparingEnlistment.Prepared();
18}
19
20voidIEnlistmentNotification.Rollback(Enlistmentenlistment)
21{
22Console.WriteLine("回滚!");
23enlistment.Done();
24}
25}
26
27
  好,界说完以后,还必要向事件办理器举行注册,把它到场到以后事件中往:

1using(TransactionScopets=newTransactionScope())
2{
3SampleEnlistment1myEnlistment1=newSampleEnlistment1();
4Transaction.Current.EnlistVolatile(myEnlistment1,EnlistmentOptions.None);
5ts.Complete();
6}
  实行这一段代码,我们能够失掉以下的输入:

  筹办!
  提交!

  先注释一下,当挪用ts.Complete()办法的时分,暗示事件已乐成实行。随后,事件办理器就会寻觅以后一切已注册的条目,也就是IEnlistmentNotification的每个完成,顺次挪用它们的Prepare办法,即关照每一个条目做好提交筹办,当一切条目都挪用了Prepared()暗示本人已筹办妥善以后,再顺次挪用它们的Commit办法举行提交。假如个中有一个没有挪用Prepared而是挪用了ForceRollback的话,全部事件都将回滚,此时势务办理器再挪用每一个条目标Rollback办法。

  而假如我们将后面的ts.Complete()行正文失落,明显实行了局就将变成:

  回滚!

  3、一个完成赋值的自界说操纵

  思索一下,我们要完成一个事件赋值操纵。该怎样做法?以下是一个例子:

1classSampleEnlistment2:IEnlistmentNotification
2{
3publicSampleEnlistment2(AssignTransactionDemovar,intnewValue)
4{
5_var=var;
6_oldValue=var.i;
7_newValue=newValue;
8}
9
10privateAssignTransactionDemo_var;
11privateint_oldValue;
12privateint_newValue;
13
14voidIEnlistmentNotification.Commit(Enlistmentenlistment)
15{
16_var.i=_newValue;
17Console.WriteLine("提交!i的值变成:"+_var.i.ToString());
18enlistment.Done();
19}
20
21voidIEnlistmentNotification.InDoubt(Enlistmentenlistment)
22{
23thrownewException("Themethodoroperationisnotimplemented.");
24}
25
26voidIEnlistmentNotification.Prepare(PreparingEnlistmentpreparingEnlistment)
27{
28preparingEnlistment.Prepared();
29}
30
31voidIEnlistmentNotification.Rollback(Enlistmentenlistment)
32{
33_var.i=_oldValue;
34Console.WriteLine("回滚!i的值变成:"+_var.i.ToString());
35enlistment.Done();
36}
37}
38
39classAssignTransactionDemo
40{
41publicinti;
42
43publicvoidAssignIntVarValue(intnewValue)
44{
45SampleEnlistment2myEnlistment2=newSampleEnlistment2(this,newValue);
46Guidguid=newGuid("{3456789A-7654-2345-ABCD-098765434567}");
47Transaction.Current.EnlistDurable(guid,myEnlistment2,EnlistmentOptions.None);
48}
49}
50
51
  然后,如许来利用:

1AssignTransactionDemoatd=newAssignTransactionDemo();
2atd.i=0;
3using(TransactionScopescope1=newTransactionScope())
4{
5atd.AssignIntVarValue(1);
6Console.WriteLine("事件完成!");
7scope1.Complete();
8Console.WriteLine("加入地区之前,i的值为:"+atd.i.ToString());
9}
10Thread.Sleep(1000);
11Console.WriteLine("加入地区以后,i的值为:"+atd.i.ToString());
  运转这一段代码,我们能够看到以下了局:

  事件完成!
  加入地区之前,i的值为:0
  提交!i的值变成:1
  加入地区以后,i的值为:1

  从输入了局来看,赋值操纵被乐成实行了。但是有无感到有些奇异?先做个会商:

  1、假如后面没有Thread.Sleep(1000)这一行,那末我们多数会看到最初一行的输入中,i的值仍然会是0!为何?想一想就简单分明,这里对Commit办法是接纳的异步伐用,好像另开了一个线程。假如主线程不作守候的话,当输入的时分事件的Commit办法多数还没有被实行,输入的了局固然就会不合错误。

  2、这个例子中,赋值操纵是在Commit办法中才实践实行的。但实践上就本例而言,我们也能够做个调剂:将赋值操纵放在AssignIntVarValue办法的最初往实行,然后把Commit办法中的赋值操纵往失落。相干的代码变更以下:

1classSampleEnlistment2:IEnlistmentNotification
2{
3voidIEnlistmentNotification.Commit(Enlistmentenlistment)
4{
5enlistment.Done();
6}
7//别的略
8}
9
10classAssignTransactionDemo
11{
12publicinti;
13
14publicvoidAssignIntVarValue(intnewValue)
15{
16SampleEnlistment2myEnlistment2=newSampleEnlistment2(this,newValue);
17Guidguid=newGuid("{3456789A-7654-2345-ABCD-098765434567}");
18Transaction.Current.EnlistDurable(guid,myEnlistment2,EnlistmentOptions.None);
19i=newValue;
20Console.WriteLine("提交前改动!i的值为:"+i.ToString());
21}
22}
23
24
  如许,实行了局将会变成:

  提交前改动!i的值为:1
  事件完成!
  加入地区之前,i的值为:1
  加入地区以后,i的值为:1

  3、在后面的基本上,当把挪用的中央作以下修改,使事件失利:

1using(TransactionScopescope1=newTransactionScope())
2{
3atd.AssignIntVarValue(1);
4Console.WriteLine("事件失利!");
5//scope1.Complete();
6Console.WriteLine("加入地区之前,i的值为:"+atd.i.ToString());
7}
  此时的实行了局将变成:

  提交前改动!i的值为:1
  事件失利!
  加入地区之前,i的值为:1
  回滚!i的值变成:0
  加入地区以后,i的值为:0

  可见,事件已乐成回滚。

  4、进一步的会商

  后面我们都是只举行了一次赋值操纵,假如我们必要举行两次呢?

1using(TransactionScopescope1=newTransactionScope())
2{
3atd.AssignIntVarValue(1);
4atd.AssignIntVarValue(2);
5Console.WriteLine("事件失利!");
6//scope1.Complete();
7Console.WriteLine("加入地区之前,i的值为:"+atd.i.ToString());
8}
  这时候的实行了局将会是怎样?我们固然是但愿回滚的时分,i的值能先变回为1,再变回为0。可是实践了局呢?

  提交前改动!i的值为:1
  提交前改动!i的值为:2
  事件失利!
  加入地区之前,i的值为:2
  回滚!i的值变成:0
  回滚!i的值变成:1
  加入地区以后,i的值为:1

  明显,事件的回滚并没有依照我们但愿的按次来,是何缘故原由?剖析一下机制就可以晓得,事件办理器向每一个条目收回回滚命令的时分只是收回了一个异步伐用,而且极可能仍是按挂号的按次来收回的,如许一来,Rollback办法的挪用按次明显就不克不及包管了。

  这时候,假如将Rollback办法作一个小调剂:

1voidIEnlistmentNotification.Rollback(Enlistmentenlistment)
2{
3while(_var.i!=_newValue)
4{
5Thread.Sleep(500);
6}
7_var.i=_oldValue;
8Console.WriteLine("回滚!i的值变成:"+_oldValue.ToString());
9enlistment.Done();
10}
  再次运转之,了局就对了:

  提交前改动!i的值为:1
  提交前改动!i的值为:2
  事件失利!
  加入地区之前,i的值为:2
  回滚!i的值变成:1
  回滚!i的值变成:0

  了局的准确实在并非挪用的按次就对了,只是Rollback办法在实行的时分先反省一下_newValue的值是不是与以后i的值分歧,纷歧致的话就等上一会儿。在守候的过程当中,另外一个实例的Rollback办法被实行,而它反省发明是婚配的,以是就会回滚到1。第一个Rollback守候停止后再反省发明婚配了,因而就回滚为0。

  固然实践使用中,这类办法是极不成取的。且不说实行按次仍然会有很年夜的风险,光是计划体例就有年夜成绩。那末在实践使用中我们应该怎样往做呢?这里只供应一下计划头脑,详细的完成代码不再列出了。

  在后面的例子中,两次赋值共举行了两次挂号,这一点是激发不不乱性的原因。我们应该思索,两次赋值仍然只挂号一次,在第一次赋值的时分,创建一个SampleEnlistment2的实例并在AssignTransactDemo中保留上去,而且SampleEnlistment2必要纪录以后的操纵。下一次赋值时,仍旧利用这个实例,只举行操纵纪录便可。如许,当回滚的时分,它依据纪录的反按次实行回滚操纵就能够了。

  再进一步呢?假如说有多个Transaction必要举行赋值操纵呢?这时候我们能够在AssignTransactionDemo类中到场一个Dictionary<Transaction,SampleEnlistment2>,利用的时分依据Transaction往寻觅响应的条目便可。

  本文会商暂到此为止。在微软的101个例子中,有一个利用事件举行文件拷贝的例子。那边面有对照深切的完成。假如你还没有看过,保举往研讨一下,信任你读过此篇漫笔,研讨它应该不再是个困难。如果需要重新编写代码,几乎任何一门计算机语言都可以跨平台了,还用得着Java嘛,而且像PHP/C#等语言不需要修改代码都可以跨Windows/Linux。

不帅 发表于 2015-1-19 17:11:38

asp.net空间的支持有:ASP.NET1.1/虚拟目录/MicrosoftFrontPage2000扩展/CDONTS,同时他的网站上也提供了Asp.net的使用详解和程序源代码,相信对使用ASP.NET编程的程序员来说会非常有用哦!

只想知道 发表于 2015-1-24 15:07:38

CGI程序在运行的时候,首先是客户向服务器上的CGI程序发送一个请求,服务器接收到客户的请求后,就会打开一个新的Process(进程)来执行CGI程序,处理客户的请求。CGI程序最后将执行的结果(HTML页面代码)传回给客户。

若天明 发表于 2015-2-1 17:11:58

ASP.Net和ASP的最大区别在于编程思维的转换,而不仅仅在于功能的增强。ASP使用VBS/JS这样的脚本语言混合html来编程,而那些脚本语言属于弱类型、面向结构的编程语言,而非面向对象。

柔情似水 发表于 2015-2-7 11:29:29

JSP/Servlet虽然在国内目前的应用并不广泛,但是其前途不可限量。

变相怪杰 发表于 2015-2-21 19:46:32

同时也感谢博客园给我们这个平台,也感谢博客园的编辑们做成专题引来这么多高人指点。

愤怒的大鸟 发表于 2015-3-6 21:14:04

逐步缩小出错代码段的范围,最终确定错误代码的位置。

海妖 发表于 2015-3-13 09:10:04

使用普通的文本编辑器编写,如记事本就可以完成。由脚本在服务器上而不是客户端运行,ASP所使用的脚本语言都在服务端上运行,用户端的浏览器不需要提供任何别的支持,这样大提高了用户与服务器之间的交互的速度。

若相依 发表于 2015-3-20 18:25:35

Servlet却在响应第一个请求的时候被载入,一旦Servlet被载入,便处于已执行状态。对于以后其他用户的请求,它并不打开进程,而是打开一个线程(Thread),将结果发送给客户。由于线程与线程之间可以通过生成自己的父线程(ParentThread)来实现资源共享,这样就减轻了服务器的负担,所以,JavaServlet可以用来做大规模的应用服务。
页: [1]
查看完整版本: ASP.NET网页编程之在.NET 2.0 中利用自界说事件操纵