如何在三层结构中处理逻辑错误(200分)

  • 主题发起人 主题发起人 blue_morning
  • 开始时间 开始时间
B

blue_morning

Unregistered / Unconfirmed
GUEST, unregistred user!
看了李SIR的书和在DFW上找了找对三层结构中处理逻辑错误的方法,
先简要总结如下:
例子:client要新增一个用户,当提交的新增的用户名时如果此用户名已经存在时
就产生了一个逻辑错误(用户名必须唯一)。
不讨论不先做判断是否已有用户就插入,再通过数据库异常来处理。

1、中间层用一个query写好一条如select * from UserTable wherer UserName = :UserName
的SQL语句,用ClientDataSet带入动态参数,然后在客户端判断ClientDataSet.RecordCoun > 0
确定是否用户名重复。

2、在客户端调用DCOM或Socket的AppServer.Event(UserName, OtherParameters),来进行新增用户的操作,

1的特点是简单,但逻辑写在了客户端,当更新程序时必须更新客户端的应用程序。且这样的方法
不能被其它的系统调用。
2我觉得才是真正三层结构的写法,但是这里的逻辑错误如何处理呢?
从李SIR的书来看,可以带入一个或多个OldVariant变量,然后对这个变量赋值进行操作。
我的想法是每个方法都有vResult, vMsg两个OleVariant参数。
vResult是一个Integer的错误标识(如用0来表示成功,其它表示失败),vErrorMsg是说明错误的string。
客户端在调用了AppServer.Event后就读vResult的值来判断是否执行成功。如果不成功就显示vErrorMsg的内容。

但这样的方法是不是太土了? :)
请大家指点。
 
我以前就是有的李维书中的方法,还行。
//关注此题。
 
有个问题: 如果在select * from UserTable wherer UserName = :UserName执行完成后,
在你插入记录之前另一个客户端也进行了同样的操作,并且比你先插入了跟你相同的用户名
由于你判断RecordCount=0,但不知道此时数据库其实已经新增了那个用户,然后你再插入
相同的用户也能通过(如果数据库没有唯一限制的话),这样你数据库中的用户名就重复了
所以你这种做法在逻辑上是有问题的,特别是在并发比较大的情况下出错的机率更大! 除
非你在select * from UserTable wherer UserName = :UserName之前把表锁住,避免别的
用户在你插入之前增加相同的用户,但这显然不是好的解决办法。
>不讨论不先做判断是否已有用户就插入,再通过数据库异常来处理。
从上面的说明来看,这恰恰才是正确的方法。
 
to: xianjun i 服了 u,

下面是我写的代码, 在try except 中处理了你说的情况。这样的情况很少吧?上面的情况
应当更多些,我想讨论的是大家都采用了什么方法来处理这样的错误

begin
if UserIsExist(UserName) then
begin
vErrMsg := '用户:' + UserName + '已经存在,请使用其它的用户名。';
vResult := -1;
Exit;
end;
if LoginNameIsExist(LoginName, s) then
begin
vErrMsg := '用户' + s + '已经使用了' + LoginName
+ '做为登录名,请选择其它登录名。';
vResult := -1;
Exit;
end;
try
ADOQuery.Close;
ADOQuery.SQL.Text :=
'INSERT INTO TB_Users(ParentID, LoginName, UserName, Password) VALUES(:msParentID, :msLoginname, :msUsername, :msPassword)';
ADOQuery.Parameters.ParamByName('msParentID').Value := ParentGUID;
ADOQuery.Parameters.ParamByName('msLoginname').Value := LoginName;
ADOQuery.Parameters.ParamByName('msUsername').Value := UserName;
ADOQuery.Parameters.ParamByName('msPassword').Value := Password;
ADOQuery.ExecSQL;
vResult := 0;
vErrMsg := '';
except
vResult := -1;
vErrMsg := '向数据库提交数据失败,请重试';
end;
end;

//----------------------client--------------------
if not DM.scAppServer.Connected then
DM.scAppServer.Open;
DM.scAppServer.AppServer.AddUser(edtUserName.Text, edtLoginName.Text,
edtPassword.Text, ParentGUID, vResult, vErrMsg);
s := vErrMsg;
if vResult = 0 then
Application.MessageBox('添加用户成功', '提示', MB_OK)
else
Application.MessageBox(PChar(s), '提示', MB_OK);
 
你这样写其实还是用了数据库的异常,对吧
只不过数据库发生异常的时候你把错误信息屏蔽掉了。
如果是这样的话,前面的判断就显得多余了,因为你大可以在except里判断出错原因
同样也能告诉用户是因为用户名重复所以不能插入
因为你用的是三层,客户端与中间层通信要额外的时间,这就增加了并发错误的机率。
只要把except中处理后的错误信息再返回给客户端就可以了。
所以错误处理还是在中间层,只返回错误信息。 这样还有一个好处就是安全性
如果你的客户端被人改了也没关系,因为错误处理在中间层。反之,错误处理在客户端
我虽然发现RecordCount=1,但我照样做接下来的操作你也没办法(客户端被人反汇编的情况下)。
 
当调用AppServer.Envnt时,也就是不使用clientdataset,Midas的错误机制就帮不上忙了

错误就要自己处理不是吗?
 
To blue_morning,

I have read your comment about kbmMemTable at:
http://www.delphibbs.com/delphibbs/dispq.asp?lid=478936

Could you please have a look at:
http://www.delphibbs.com/delphibbs/dispq.asp?lid=1160427

I have a question about kbmMemTable
 
不应该用Event来处理,而应该在服务器端导出一个IUserManager接口,然后在其中实现AddUser等函数,错误判断在服务器端进行,发生错误的时候服务器端将错误记入日志,并且通过返回错误码告知客户端发生了什么错误。
客户端调用方式如下:
hr := ClientDataset.AppServer.AddUser(...);
case hr of
S_OK:
;// 正常执行
错误码:
;//错误处理
end;
 
Traveller:

我指的方法和你的意思是一样的。
 
>>但这样的方法是不是太土了? :)
不管黑猫白猫。。。
 
大家继续吧,我感着UP!哈哈!
 
学习来了。。。
归根到底是:异常发生在中间层,客户端怎么准确捕获并能根据这个异常做相应的判断处理。
xianjun 大侠说:
>>>如果是这样的话,前面的判断就显得多余了,因为你大可以在except里判断出错原因
这个出错原因能在except里能准确判断吗? 因为除了用户名已经存在的这中异常发生外,
还要可能有另外其他的异常发生。
此时except应该怎样判断才好? 并回传给客户端? 要用怎样的错误处理机制啊?
 
>>>这个出错原因能在except里能准确判断吗?
这在Oracle中肯定是可行的,其他数据库应该也是一样
比如,你插入相同的用户名时,如果你在表上设了用户名唯一性限制,那就会报ORA-00001
错误。
 
>>>这个出错原因能在except里能准确判断吗?
李维的书上也讲过如何取得数据库原生错误信息的方法
既然数据库的错误信息都取得了,当然知道是什么造成了出错。

另外,在三层中,中间层与客户端最好不要传递SQL
如果不这样的话,三层结构业务逻辑更改只需要修改中间层的优势就没了。
 
后退
顶部