COM+开发中有关接口类型的属性的问题,请先看一下代码:interfaceuses
...., TestLib_TLB;
type
{
IError = interface(IDispatch)
['{10FE2C40-83CD-463B-9F3E-4A3D8CF18156}']
function Get_Number: Integer; safecall;
function Get_Source: WideString; safecall;
function Get_Description: WideString; safecall;
property Number: Integer read Get_Number;
property Source: WideString read Get_Source;
property Description: WideString read Get_Description;
end;
ITest = interface(IDispatch)
['{4089D564-07A2-465F-B2DC-F8145A52037A}']
function Get_Error: IError; safecall;
property Error: IError read Get_Error;
end;
}{ TError }
TError = class(TAutoIntfObject, IError)
private
FNumber: Integer;
FSource: WideString;
FDescription: WideString;
protected
{ IError }
function Get_Number: Integer; safecall;
function Get_Source: WideString; safecall;
function Get_Description: WideString; safecall;
public
constructor Create;
property Number: Integer read FNumber write FNumber;
property Source: WideString read FSource write FSource;
property Description: WideString read FDescription write FDescription;
end;{ TTestMtsAutoObject } TTestMtsAutoObject = class(TMtsAutoObject, ITest)
private
FError: TError;
FErrorIntf: IError; {****}
protected
procedure OnActivate; override;
procedure OnDeactivate; override;
protected
{ TTest }
function Get_Error: IError; safecall;
public
property Error: TError read FError;
end;implementation{ TError }constructor TError.Create;
var
TestLib: ITypeLib;
begin
OleCheck(LoadRegTypeLib(LIBID_TestLib, 1, 0, 0, TestLib));
inherited Create(TestLib, IError);
end;function TError.Get_Number: Integer;
begin
Result := FNumber;
end;function TError.Get_Source: WideString;
begin
Result := FSource;
end;function TError.Get_Description: WideString;
begin
Result := FDescription;
end;{ TTestMtsAutoObject }procedure TTestMtsAutoObject.OnActivate;
begin
inherited OnActivate;
FError := TError.Create;
FErrorIntf := FError; {****}
end;procedure TTestMtsAutoObject.OnDeactivate;
begin
inherited OnDeactivate;
FErrorIntf := nil; {****}
FError := nil;
end;function TTestMtsAutoObject.Get_Error: IError;
begin
Result := FErrorIntf; {****}
//Result := FError as IError;
end;在上面的情形下,使用了FErrorIntf变量,客户端调用是没问题的。但是如果去掉所有FErrorIntf{****}相关的内容(同时在Get_Error中使用注释的代码),
那么在客户端第一次使用Error属性是正确的,在第一次之后再使用Error属性就出现“灾难性故障”了,
调试后发现在COM+里FError对象是存在的,有正确的地址内容,而客户端得到的Error属性是nil。
为什么“FError as IError”在第一次正确,而后就不正确了呢?搞不懂为什么?
但是不管如何,每一次方法调用的前后都会 Activate 和 Deactivate。
我已经采用了Initialize和Destroy,但情况依然存在……但是,象我上面说的采用 FErrorIntf 来存储一下,是没有问题的,
或者在创建FError := TError.Create后调用一下FError._AddRef也可以,
在Destroy里再调用_Release。这个问题应该和引用计数有关,在FError := TError.Create后,引用计数并不是1而是0,
所以第一次调用属性完成后由于会调用_Release,此时RefCount 为0,自动Destroy了。
又有Item属性,每个Item是Field等等这样的形式。 不知道你有没有更好的设计
另外,假设你开发的 COM+ 对象能够 Pooling,它就可能会服务于多个客户。当你在 COM+ 对象中保存了状态,就很有可能影响到下一个使用该对象的客户,造成无法预测的错误。
我希望能当面想你请教,不知道是否可以……我的MSN是:[email protected]
Activate后一直激活,不会Deactivate,知道客户方释放接口引用。如果对象方法调用了SetComplete或SetAbort,将在调用结束后立刻被Deactivate
书上讲了,这是应该的
我想我用ADO的事务不会代表着自己开发了一个“COM+”吧?同样是M$的东西,ADO的事务不会比COM+的事务差吧?
我是这么认为的,COM+的事务处理的好处是不管是什么事务(数据库事务,消息队列事务),只要有对应的COM+资源管理器(RM)并已经向COM+注册,那么COM+就可以处理相应类型的事务。但对于纯粹的数据库事务来讲,完全可以用ADO Connection的事务来处理,对吧?如果每个有关数据更新的方法都调用SetComplete,我测试过了,一旦SetComplete,对象就被Deactivate,被放入池中,如果业务操作只需要调用这一个方法,那很好,没有问题。但往往在实际中(我的系统中),需要连续进行很多业务操作,需要调用很多方法,这样的话
在一次客户端调用周期中,对象被频繁Deactivate和Activate,我想肯定也会丧失一些性能吧?而且在一个调用期间保留不了状态,我觉得两者的性能比较好像不能光说了,得实际测试比较才行,我正在测试中……按我的想法去做,并不代表着没有使用COM+的Pooling等特性,一个客户端完整的业务操作的调用完成后,对象被释放,自然回到池中。假设一个COM+对象,按照上面的两种方法分别实现,并且一次业务操作都需要调用三个方法:
那么:
1. 用SetComplete的方式对象将被Activate和Deactivate三次,保留不了状态;2. 用我的想法,用ADO处理事务,对象只被Activate和Deactivate一次,并且可以保留状态;如果在OnActivate和OnDeactivate中的代码具有一定消耗的话,那么我想性能应该是1不如2了但是如果某个方法的执行时间比较长的话,对多用户访问来讲,性能可能还是1好---------------------------
以上是我的一些浅浅的认识,讲得乱了些,还希望leapmars(流铭)多多指教,多谢……
比如一次完整的业务调用要进行三个方法的调用,
在前两个方法中不使用SetComplete而是使用EnableCommit,
最后一个方法调用才调用SetComplete,
这样可以在三个调用期间保留状态,并实现事务控制。可这种方法对客户端调用增加了约束,方法的调用需要按照
一定的调用顺序来进行……麻烦对于消息队列等不是数据库事务的事务来说,可以增加“协调组件”的概念
来实现,下班了,先不多说了……
如果只有一个 COM+ 对象,可以用 ADO.Connection 来进行事务处理(此时不让 COM+ 来管理事务);
但是如果有多个 COM+ 对象要访问数据库、进行数据修改,并且要求在一个事务处理中,楼主准备如何用 ADO.Connection 来进行事务处理呢?
对这种情况我想这么处理,采用“协调对象”的概念,客户只和协调对象交互,
由协调对象调用其他进行数据更新的COM+对象,这些其他的COM+对象一般不需要
保留状态,也不一定要“对象化”,在里面可以大胆使用SetComplete,
在协调组件里,使用CreateTransactionContextEx创建自己的事务来实现多个
其他组件的事务控制,使用ITransactionContextEx.Commit来提交,这样协调对象
仍然是“对象化”,可以保留状态的。ITransactionContextEx.Commit并不会Deactivate协调组件。客户端只需要和“协调对象”交互我想是可行的,你说呢?
可是一个业务过程并不代表就一个COM+方法,一个业务过程也许调用了 3 个方法,而这三个方法并不是处于一个事务之中,在三个方法之间需要保留状态……
它经历了太昂贵的转换,或者无法在客户机器上完成。
它经历了太昂贵的转换,或者无法在客户机器上重新完成。
它太昂贵或者无法将状态数据传输到客户机上。
数据没有复制到其他事务存储区中。 如果上面大多数条件为真,那么就很有理由实现有状态的服务器端编程模型。这可能会提高性能,但是它会在很大程度上减少并发度甚至可扩展性。系统设计的时候,需要权衡一下各种条件,然后决定实施方案。
另外,保留状态的方法很多,如 SPM 等等