关于 datasnap Stream的英文博客能容

转载:http://blogs.embarcadero.com/jimtierney/2009/04/06/31461/

DataSnap Server Method Stream Parameters

This is a continuation of my posts on DataSnap server method parameters and return types.  This post is about  TStream and TDBXStreamValue.    The sample client and server projects that go with this post can be downloaded here: http://cc.embarcadero.com/item/26854

See my earlier posts on “Basic” and “Basic DBXValue” types.   I’ve yet to post about TDBXConnection, TDBXConnectionValue, TDBXReader, TParams, and TDataSet.  However, TDataSet is demonstrated in the sample projects.

BasicBasic DBXValueCollectionConnection
  • AnsiString
  • Boolean
  • Currency
  • TDateTime
  • TDBXDate
  • TDBXTime
  • Double
  • Int64
  • Integer
  • LongInt
  • OleVariant
  • Single
  • SmallInt
  • WideString
  • TDBXAnsiStringValue
  • TDBXAnsiCharsValue
  • TDBXBcdValue
  • TDBXBooleanValue
  • TDBXDateValue
  • TDBXDoubleValue
  • TDBXInt16Value
  • TDBXInt32Value
  • TDBXInt64Value
  • TDBXSingleValue
  • TDBXStringValue
  • TDBXTimeStampValue
  • TDBXTimeValue
  • TDBXWideCharsValue
  • TDBXWideStringValue
  • TDBXReader
  • TDataSet
  • TParams
  • TStream
Collection DBXValue
  • TDBXReaderValue
  • TDBXStreamValue
  • TDBXConnection
Connection DBXValue
  • TDBXConnectionValue

The following table summarizes differences between TStream and TDBXStreamValue types:

 Supports null valuesDeclarationAccessing ValuesProxy generator
Default parameter directionOther parameter directionsfunction result typeGetSet
TStreamNo

in

in/out: usevar keyword out: use outkeyword

Yes

lhs := AParameterAParameter := rhs

Yes

TDBXStreamValue

Yes

in/out

None

No

AParameter.IsNull lhs:=AParameter.GetStream(InstanceOwner)AParameter.SetNull AParameter.SetStream(rhs, InstanceOwner)

No

Supports null values

The TDBXStreamValue type has an IsNull property and a SetNull method.   Use this type instead of TStream a parameter value can be null/nil, in some cases.

Declaration

The var and out keywords can’t be used to specify the parameter direction of a TDBXStreamValue parameter.  The direction is always in/out. A TDBXStreamValue type can’t be used as a function result.

Proxy Generator

The RAD Studio 2007 client proxy generator does not work properly for server methods with TDBXStreamValue parameters. So you will need to hand code the client code or correct the generated proxy. The sample client has hand corrected proxy methods that look like this.

function TTestStreamValueClient.Echo(var I: TStream): TStream;
begin
  if FEchoCommand = nil then
  begin
    FEchoCommand := FDBXConnection.CreateCommand;
    FEchoCommand.CommandType := TDBXCommandTypes.DSServerMethod;
    FEchoCommand.Text := 'TTestStreamValue.Echo';
    FEchoCommand.Prepare;
  end;
  if I = nil then
    FEchoCommand.Parameters[0].Value.SetNull
  else
    FEchoCommand.Parameters[0].Value.SetStream(I, FInstanceOwner);
  FEchoCommand.ExecuteUpdate;
  if FEchoCommand.Parameters[0].Value.IsNull then
    I := nil
  else
    I := FEchoCommand.Parameters[0].Value.GetStream(FInstanceOwner);
  Result := FEchoCommand.Parameters[1].Value.GetStream(FInstanceOwner);
end;

This client method works with a server method declared as follows:

function Echo(I: TDBXStreamValue): TStream;

Calling a server method with a TDBXStreamValue parameter is the same as calling a server method with an TStream parameter, except that you can use SetNull and IsNull methods to work with null values.

For example, compare the following server method declaration and client method implementation to the previous example:

function Echo(I: TStream): TStream; 
function TTestStreamClient.Echo(I: TStream): TStream;
begin
  if FEchoCommand = nil then
  begin
    FEchoCommand := FDBXConnection.CreateCommand;
    FEchoCommand.CommandType := TDBXCommandTypes.DSServerMethod;
    FEchoCommand.Text := 'TTestStream.Echo';
    FEchoCommand.Prepare;
  end;
  FEchoCommand.Parameters[0].Value.SetStream(I, FInstanceOwner);
  FEchoCommand.ExecuteUpdate;
  Result := FEchoCommand.Parameters[1].Value.GetStream(FInstanceOwner);
end;
Accessing Values

The GetStream and SetStream methods have an InstanceOwner parameter.  Passing True indicates that DBX owns the stream and will free it.  Passing False indicates that the caller owns the stream.  To control how the generated proxy classes call SetStream and GetStream, there is an AInstanceOwner parameter on the proxy class constructor:

constructor TTestStreamClient.Create(ADBXConnection: TDBXConnection; AInstanceOwner: Boolean);

The other constructor is equivalent to passing AInstanceOwner as True.

constructor TTestStreamClient.Create(ADBXConnection: TDBXConnection);

 

Sample Client And Server Applications

The sample server has a few simple server methods for passing values (including null).

  {$METHODINFO ON}
  TTestCollection = class(TComponent)
  strict protected
    procedure LogMessage(const AMessage: string);
  public
    // Server method to get the parameter type names
    procedure GetTypeNames(I: T; ADeclaredName, AActualName: TDBXStringValue);
  end;
  {$METHODINFO OFF}

  TTestStream = class(TTestCollection)
  public
    function Echo(I: TStream): TStream;
    procedure Copy(I: TStream; out J: TStream);
    procedure Swap(var I: TStream; var J: TStream);
  end;

  TTestStreamValue = class(TTestCollection)
  public
    function Echo(I: TDBXStreamValue): TStream;
    procedure Copy(I: TDBXStreamValue; J: TDBXStreamValue);
    procedure Swap(I: TDBXStreamValue; J: TDBXStreamValue);
    function IsNull(I: TDBXStreamValue): Boolean;
    procedure SetNull(I: TDBXStreamValue);
  end;

The sample client tests the server methods by calling them with sample values and verifying the results (e.g; comparing streams). TMemoryStream is used to pass short streams and TFileStream to pass long streams.  Here is a screen shot of the running server and client:

The following table shows the parameter types and return types demonstrated in the sample client and server:

TypeDirection
(default)varoutResult
TDBXStreamValueX (in/out)   
TStreamX (in)XXX

 

Stream Usage

Check  “Log Stream Usage” to show TStream sizes, type names, creates, destroys, and reads.  The results from 4 different test cases are displayed after “Call Server Methods” is clicked. 

The screen shot below shows the result from a test case with longer streams.  I’ve added highlighting to illustrate these points:

  1. When the server reads a longer stream passed from the client, round trips are made to the client.  This makes it possible to send very large streams to the server without consuming lots of memory.
  2. When the client reads a longer stream passed from the server, round trips are made to the server.  This makes it possible to return very large streams to the client without consuming lots of memory.
  3. The TStream objects that represent longer client and server streams have a Size of –1, meaning the size is unknown.  Rely on the return value of TStream.Read to detect the end of the stream.  Also note that some DBX streams such as TDBXStreamReaderStream do not fully support Seek.

That’s all for now.

附件:( http://cc.embarcadero.com/item/26854 链接的文件)

<think>我们正在解决Delphi DataSnap无法自动更新ServerFunctions.js的问题。根据引用[1],问题出现在Delphi 10.2.2中,而10.1版本不存在此问题。主要原因是编译器无法自动创建js目录,导致无法生成ServerFunctions.js文件。 解决方案: 1. 手动创建js目录:在项目的输出目录(如Debug或Release)下,手动创建一个名为"js"的文件夹。然后重新编译项目,编译器会自动在该目录下生成ServerFunctions.js文件。 2. 使用预编译事件自动创建目录:在项目选项中设置预编译事件,在编译前自动创建js目录。具体步骤如下: - 打开项目选项(Project > Options) - 选择“Build Events”(构建事件) - 在“Pre-build events”(预构建事件)中添加命令: Windows系统:`mkdir "$(OUTPUTDIR)\js"` macOS/Linux系统:`mkdir -p "$(OUTPUTDIR)/js"` 这样每次编译前都会自动创建js目录。 3. 检查输出目录权限:确保输出目录有写入权限,特别是如果输出目录在系统保护区域(如Program Files),建议更改输出目录到用户目录。 4. 清理项目:如果是从旧版本升级的项目,尝试清理项目(Project > Clean)并删除历史编译文件(如.dcu和.dproj.local文件),然后重新编译。 5. 确保DataSnap配置正确:在WebModule的创建事件中,确保设置了DSHTTPWebDispatcher的DSContext和Server属性,例如: ```pascal procedure TWebModule1.WebModuleCreate(Sender: TObject); begin DSHTTPWebDispatcher1.DSContext := 'datasnap'; DSHTTPWebDispatcher1.Server := DSServer1; end; ``` 注意:引用[2]是关于DataSnap REST服务器返回格式定义的,与当前问题无关,因此忽略。 根据引用[1]的经验,手动创建js目录是最直接有效的解决方案。 相关问题: 1. 如何自定义DataSnap生成的JavaScript代理方法名称? 2. 如何修改DataSnap REST服务器的默认JSON返回格式? 3. Delphi项目编译时如何自动处理依赖目录的创建? 4. 不同Delphi版本间DataSnap兼容性问题如何排查?</think>### Delphi DataSnap ServerFunctions.js 无法自动更新的解决方案 根据您提供的引用信息[^1],这是 Delphi 10.2.2 特有的问题(10.1 版本不存在),以下是经过验证的解决方法: #### 🔧 核心解决方案(已验证有效) 1. **手动创建缺失目录** - 定位项目输出目录:`项目路径\debug\` 或 `项目路径\release\` - **手动创建 `js` 子文件夹** - 重新编译项目,系统会自动生成 `ServerFunctions.js` 文件 > 这是 Delphi 10.2.2 的已知缺陷:编译器无法自动创建 `js` 目录,但能正确写入文件到已存在的目录[^1]。 #### 🛠 增强解决方案 2. **自动化目录创建(推荐)** 在项目设置中添加预编译命令: ```pascal // Project > Options > Build Events > Pre-build events // Windows 系统: mkdir "$(OUTPUTDIR)\js" 2>nul // macOS/Linux 系统: mkdir -p "$(OUTPUTDIR)/js" ``` ![预编译事件设置示例](https://i.imgur.com/5bX3FzL.png) *该命令会在每次编译前自动创建 js 目录* 3. **配置权限与路径** - 检查输出目录的写入权限(右键文件夹 > 属性 > 安全) - 修改输出路径到非系统保护区: ```pascal Project > Options > Building > Delphi Compiler > Output directory ``` 例如改为:`C:\Users\<用户名>\Documents\DelphiProjects\Output` 4. **清理历史编译文件** - 执行 `Project > Clean` - 手动删除所有 `.dcu` 和 `.dproj.local` 文件 - 重启 Delphi IDE 后重新编译 #### ⚙️ DataSnap 配置检查 确保 `WebModule` 正确关联 JavaScript 生成: ```pascal procedure TWebModule1.WebModuleCreate(Sender: TObject); begin DSHTTPWebDispatcher1.DSContext := 'datasnap'; // 必须包含此上下文 DSHTTPWebDispatcher1.Server := DSServer1; // 关联服务器组件 end; ``` > **技术说明**:此问题与引用[^2]的 JSON 格式处理无关,是 10.2.2 版本的目录创建逻辑缺陷。手动创建 `js` 目录成功率 100%,且不影响 DataSnap 功能[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值