RemObjects文档翻译3.docx
《RemObjects文档翻译3.docx》由会员分享,可在线阅读,更多相关《RemObjects文档翻译3.docx(13页珍藏版)》请在冰豆网上搜索。
![RemObjects文档翻译3.docx](https://file1.bdocx.com/fileroot1/2022-12/7/6af1eecf-ec47-436f-81d6-4571e43f4d52/6af1eecf-ec47-436f-81d6-4571e43f4d521.gif)
RemObjects文档翻译3
这个文档展示了RemObjectsSDK3.0框架的几个新特性.这个文档只讨论代码和类级别的特性.其它的特性像均衡负载,容错,多服务器支持和服务器事件将在其它稳定中介绍.
这个文档中的范例代码可以在Chat范例和升级的MegaMemo,DispatchNotifier范例中找到.
对象的生存期
当你在服务器上写一个返回复杂类型的方法时(或者返回,或者Var参数),RemObjects框架将其流化发送到客户端后将自动释放他们.
假定你写一个简单的方法:
functionTChatService.GetLoggedUsers:
TUserInfoArray;
begin
result:
=TUserInfoArray.Create;
result.Add;
result.Add;
end;
在方法调用后,实例给返回结果赋值,发送到客户端,并释放.但是有时我们并不希望这样.假设你的服务器是使用TROSingletonClassFactory控件创建的Singleton模式的,这时需要像GetLoggedUsers方法一样返回一个内部对象:
type
{TChatService}
TChatService=class(TRORemoteDataModule,IChatService)
private
fUsers:
TUserInfoArray;
[..]
functionTChatService.GetLoggedUsers:
TUserInfoArray;
begin
result:
=fUsers;
end;
但是这样的话无论你返回什么都将被框架释放.
在RemObjects2.0中,只有拷贝一个新的fUsers变量返回(例如result:
=fUsers.Clone;).
RemObjectsSDK3.0中提供了一个新特性可以让我们自己觉得什么对象自动释放什么对象不自动释放.
防止fUsers被释放,我们可以使用一个简单的方法
procedureRetainObject(constanObject:
TObject);
下面的代码举例说明:
functionTChatService.GetLoggedUsers:
TUserInfoArray;
begin
result:
=fUsers;
RetainObject(fUsers);
end;
在方法调用完毕后,fUsers不会释放.但在服务器关闭时必须释放掉fUsers实例.
Objectretentionisafeaturethattheframeworkiscapableofusingifyourobjectsimplementthefollowinginterface:
{IROObjectRetainer}
IROObjectRetainer=interface
['{1DFCCCAB-CD61-415F-ADFB-258C067E9A59}']
procedureRetainObject(constanObject:
TObject);
procedureReleaseObject(constanObject:
TObject);
functionIsRetained(constanObject:
TObject):
boolean;
end;
类TRORemotable和TRORemoteDataModule被强化,支持IROObjectRetainer接口.
对象生命周期也在客户端通过触发服务器事件实现了.我们讲再以后讨论更多细节.
Variant支持
由于客户要求我们介绍一下对Variant数据类型的支持.你可以创建接受和返回Variant类型的服务器方法.下面的代码展示了使用Variant类型的接口服务器方法:
INewService=interface
['{509D1C6D-51DF-4269-A160-DB5B5B671874}']
functionSum(constA:
Integer;constB:
Integer):
Integer;
functionGetServerTime:
DateTime;
procedureEchoVariant(constInputVariant:
Variant;
outOutputVariant:
Variant);
end;
下面的截图展示了在客户端使用EchoVariant方法的例子.客户端发送不同类型的Variant类型:
integers,strings,datetime,boolean和bytes数组.
注意这时TROSOAPMessage控件不支持Byte数组.
TROArrayandTROCollectionSearchandGetIndexmethods
当你在ServiceBuilder中建立数组类型时,RemObjects可以自动在XXX_INtf.pas中生成两个类.一个是标准的TCollection对象,它可以使用RTTI反射机制,第二个是继承于TROArray的类.
下面的代码是在Chat范例中自动产生的:
{TUserInfo}
TUserInfo=class(TROComplexType)
private
fUserID:
String;
fSessionID:
String;
public
procedureAssign(iSource:
TPersistent);override;
published
propertyUserID:
StringreadfUserIDwritefUserID;
propertySessionID:
StringreadfSessionIDwritefSessionID;
end;
{TUserInfoCollection}
TUserInfoCollection=class(TROCollection)
protected
constructorCreate(aItemClass:
TCollectionItemClass);overload;
[..]
public
constructorCreate;overload;
functionAdd:
TUserInfo;reintroduce;
procedureSaveToArray(anArray:
TUserInfoArray);
procedureLoadFromArray(anArray:
TUserInfoArray);
propertyItems[Index:
integer]:
TUserInforeadGetItems
writeSetItems;default;
end;
{TUserInfoArray}
TUserInfoArray=class(TROArray)
private
[..]
protected
[..]
public
[..]
functionAdd:
TUserInfo;overload;
functionAdd(constValue:
TUserInfo):
integer;overload;
propertyCount:
integerreadGetCount;
propertyItems[Index:
integer]:
TUserInforeadGetItems
writeSetItems;default;
end;
使用这种类型你需要查找特定的项,例如要查找第一个UserID是’Jack’的项,代码如下:
funcionSearchByUserID(anArray:
TROUserInfoArray;
constaUserID:
string):
TUserInfo;
vari:
integer;
begin
result:
=NIL;
fori:
=0to(userarray.Count-1)do
ifSameText(userarray[i].UserID,'JACK')thenbegin
result:
=userarray[i];
Exit;
end;
end;
不是很复杂,但是使用每个集合体都要写这些是很枯燥的.
TROCollection和TROArray类现在支持GetIndex方法和查询,允许只写一行代码就能查找集合体或数组.
在RemObjectsSDK3.0中我们作上面的查询,可以这样:
myuser:
=TUserInfo(userarray.Search('UserID','JACK'));
Search和GetIndex方法声明如下:
functionSearch(constaPropertyName:
string;
constaPropertyValue:
Variant;
StartFrom:
integer=0;
Options:
TROSearchOptions=[soIgnoreCase]):
TCollectionItem;
functionGetIndex(constaPropertyName:
string;
constaPropertyValue:
Variant;
StartFrom:
integer=0;
Options:
TROSearchOptions=[soIgnoreCase]):
integer;
Search方法返回一个集合或数组的一项.没有没有匹配的就返回Nil.GetIndex方法返回集合或数组的索引.附件的参数StartFrom和Options可以更灵活的控制查找条件.
自定义异常
TheRemObjectsSDK3.0增强了对自定义异常的支持并可以在新的异常类中添加自定义成员.你可以创建一个新的包含自定义类型成员的异常类型,并可以不用写序列化和解析代码就能完全的发送到客户端.MegaDemo范例中使用了这种特性.在MegaDemo范例目录中的NewService_Impl.pas文件可以发现如下方法:
procedureTNewService.RaiseTestException;
begin
raiseETestException.Create(
'Thisistheexceptionmessage',
666,
'Someextrainfohere');
end;
在ServiceBuilder中异常ETestException类型包含一个ErrorCode整型数型和AdditionalInfo字符串属性.
在NewLibrary_Intf单元中RemObjects自动生成的代码如下:
{Exceptions}
ETestException=class(EROException)
private
[..]
public
constructorCreate(constanExceptionMessage:
string;
aErrorCode:
Integer;
constaAdditionalInfo:
String);
published
propertyErrorCode:
IntegerreadfErrorCodewritefErrorCode;
propertyAdditionalInfo:
StringreadfAdditionalInfo
writefAdditionalInfo;
end;
可以看到ErrorCode和AdditionalInfo已经加入到了类和构造函数中,我们只需要一行代码就能抛出异常.此外,注册自定义异常的代码也自动在NewLibrary_Intf单元的Initialization结中生成.
unitNewLibrary_Intf;
[..]
initialization
[..]
RegisterExceptionClass(ETestException);
[..]
finalization
[..]
UnregisterExceptionClass(ETestException);
[..]
end.
原来支持的标准异常也被扩展了,Delphi中的标准异常都没有在RemObjects框架中注册,在客户端抛出EROUnregisteredException类型的异常.
MegaDemo范例中的RaiseError方法,抛出一个Delphi异常:
procedureTNewService.RaiseError;
begin
//Genericandunregisteredexceptions
raiseEDivByZero.Create('Afakedivbyzero!
');
end;
客户端用简单的代码如下:
functionTClientForm.InvokeRaiseError(constaService:
INewService)
:
integer;
[..]
begin
try
[..]
ifnotcbCustomException.Checked
thenaService.RaiseError()
elseaService.RaiseTestException;
except
onE:
ETestExceptiondobegin
result:
=GetTickCount-start;
LogMessage('ETestException-->Message:
"%s"ErrorCode:
"%d"'+
'AdditionalInfo:
"%s"',
[E.Message,E.ErrorCode,E.AdditionalInfo],result,true);
end;
onE:
Exceptiondobegin
result:
=GetTickCount-start;
LogMessage('Genericexception-->'+E.ClassName+'Message:
'+
E.Message,[],result,true);
end;
end;
end;
很多异常都被改进并从EROException继承.上面谈到很多都是EROException子类的信息.
联合服务器(ComboServers)
在RemObjects项目类型中有一个新的模板"ComboStandalone".
这个服务器是标准VCLStandalone和NTService应用程序的联合形式,你可以运行这个服务器项目做其它的事情.
使用"/install命令行参数可以在WindowsNT/2000的服务列表中注册服务.如果要在Windows9x下就直接运行程序就可以了.使用"/uninstall"卸载NTservice.
可以在TeamRO的成员ReinholdErlacher中看到这个模板!
IROStreamAccess接口
RemObjects服务器允许你通过自定义类或实现了IRODispatchNotifier接口的类在一些方法执行前后去做一些控制.可以在DispatchNotifier范例中看到这两种方式.
TRORemoteDataModule类后来提供了OnGetDispatchInfo事件而简化了第二种方式.
事件OnGetDispatchInfo声明为:
procedure(constaTransport:
IROTransport;
constaMessage:
IROMessage)ofobject;
当我们有一个传输通道来接受远程请求时,通过aMessage参数可以读取消息名称(例如Sum和GetServerTime),但是这种方式只能限于SOAP消息其他的方式无法读出消息的值.
原因在于二进制消息使用的是顺序流,所以有时这样做:
aMessage.Read('aMessage',TypeInfo(string),textmessage,[]);
我们移动指针并中断调试这个消息.
RemObjects3.0通过实现IROStreamAccess接口扩充了TROBINMessage.
IROStreamAccess接口定义如下:
IROStreamAccess=interface
['{DF3D000F-7EB3-4981-AA01-921553CAFF52}']
functionGetStream:
TStream;
propertyStream:
TStreamreadGetStream;
end;
通过这个接口我们可以将消息流保存到文件,定为当前指针等.新的DispatchNotifier范例在GetDispatchInfo方法中利用这个特性:
procedureTDispService.GetDispatchInfo(constaTransport:
IROTransport;
constaMessage:
IROMessage);
vartcpinfo:
IROTCPTransport;
textmessage:
string;
streamaccess:
IROStreamAccess;
begin
ifSupports(aTransport,IROTCPtransport,tcpinfo)
thenServerForm.Log('Client'+tcpinfo.GetClientAddress+'connected!
');
withaTransportdo
ServerForm.Log('Gotareferencetoa'+GetTransportObject.ClassName);
withaMessagedobegin
ServerForm.Log('Abouttoinvoke'+InterfaceName+'.'+MessageName);
if(MessageName='SendMessage')thenbegin
aMessage.Read('aMessage',TypeInfo(string),textmessage,[]);
ServerForm.Log('Thetextmessagewas"'+textmessage+'"');
{NewRemObjects3.0:
nowyoucanresetthepositionofthe
messagestream}
ifSupports(aMessage,IROStreamAccess,streamaccess)
thenstreamaccess.Stream.Position:
=0;
end;
end;
ServerForm.Log('');
end;
TROSOAPMessage.OnEnvelopeCompleteevent
新版本的TROSOAPMessage控件为服务器提供了更好的兼容性,并有一个新的事件OnEnvelopeComplete.
OnEnvelopeComplete定义如下:
procedure(Sender:
TROSOAPMessage)ofobject;
这个事件允许我们在将SOAP包发送到客户端或服务器之前做更正或写入.
新的MegaDemo范例在客户端利用这个新特性在SOAP添加"Test"报头,其值为”1234”.
下面的代码证明了这点:
procedureTClientForm.SOAPMessageEnvelopeComplete(Sender:
TROSOAPMessage);
begin
Sender.Header.Add('Test').Value:
='1234';
memo1.Lines.Text:
=Sender.EnvNode.XML;
end;
TROSOAPMessage也允许我们存取其他节点:
propertyEnvNode:
IXMLNodereadGetEnvNode;
propertyBodyNode:
IXMLNodereadGetBodyNode;
propertyMessageNode:
IXMLNodereadGetMessageNode;
propertyFaultNode:
IXMLNodereadGetFaultNode;
propertyHeader:
IXMLNodereadGetHeader;
有一些属性可能不赋值,所以要在使用前检测其值是否为NIL.例如不要去存取FaultNode节点,它只用于向客户端反馈服务器端异常和错误信息.
NewRemObjects_WebBrokerpackage
RemObjectsSDK以前的版本有一个单元包含TROWebBrokerServer是RemObjects_Core包得一部分.它依赖于INet包,而与BPDX或Indy组件无关.
RemObjects3.0有一个新的包叫做RemObjects_WebBroker,这样你可以编译相关的INet包了.
NewEvents
为了开发者在消息序列化前后提供更好的控制,RemObjects3.0增加了如下事件:
OnInitializeMessage,OnFinalizeMessage,OnWriteMessageParameter和OnReadMessageParameter.
下面代码是MegaDemo范例的客户端,展示了如何使用这些事件:
procedureTClientForm.BINMessageInitializeMessage(Sender:
TROMessage;
constaTransport:
IROTransport;constanInterfaceName,
aMessageName:
String);
begin
ifcbVerbose.CheckedthenLogMessage(Sender.Name+'isinitialized',[]);
end;
procedureTClientForm.BINMessageFinalizeMessage(Sender:
TROMessage);
begin
ifcbVerbose.CheckedthenLogMessage(Sender.Name+'isfinalized',[]);
end;
procedureTClientForm.BINMessageReadMessageParameter(Sen