我也看过李维的书, 提供的方法而不是代码,很多事还需要解决KeepConnection 为 true 对你的程序可能没有帮助,你的更新可能代码是在一个数据模块中而每次更新时建立这个模块,唯一的方法是先建立许多模块,用到时取一个空闲的用于更新, 见 TRemoteDataModule 中IAppServer 的定义, 我可以提供一部份关于这种模式的代码(我也刚实现,所以完整代码并不可靠)在你的 RemoteDataModule 中加入重新实现 IAppServer 中所有接口 { IAppServer } Function AS_GetProviderNames: OleVariant; Safecall; Function AS_ApplyUpdates(Const ProviderName :WideString; Delta :OleVariant; MaxErrors :Integer; Out ErrorCount :Integer; Var OwnerData :OleVariant) :OleVariant; Safecall; Function AS_GetRecords(Const ProviderName :WideString; Count :Integer; Out RecsOut :Integer; Options :Integer; Const CommandText :WideString; Var Params, OwnerData :OleVariant) :OleVariant; Safecall; Function AS_DataRequest(Const ProviderName :WideString; Data :OleVariant) :OleVariant; Safecall; Function AS_GetParams(Const ProviderName :WideString; Var OwnerData :OleVariant) :OleVariant; Safecall; Function AS_RowRequest(Const ProviderName :WideString; Row :OleVariant; RequestType :Integer; Var OwnerData :OleVariant) :OleVariant; Safecall; Procedure AS_Execute(Const ProviderName :WideString; Const CommandText :WideString; Var Params, OwnerData :OleVariant); Safecall;Function TXXXRemoteDataModule.AS_ApplyUpdates(Const ProviderName: WideString; Delta: OleVariant; MaxErrors: Integer; Out ErrorCount: Integer; Var OwnerData: OleVariant) :OleVariant; Var Provider :TCustomProvider; Begin Lock; Try Provider := Providers[ProviderName]; If Provider.Tag = 0 Then Result := Provider.ApplyUpdates(Delta, MaxErrors, ErrorCount, OwnerData) Else Begin Result := TModuleConnection(Provider.Tag).AS_ApplyUpdates (Provider.Name, Delta, MaxErrors, ErrorCount, OwnerData); End; Finally UnLock; End; End;我利用 CustomProvider (TRemoteDataModule 中定义)中的 Tag, 来存放 TModuleConnection 地址, TModuleConnection 是我写的一个控件, 可以认为是一个 TList 用于存放先分配好的那些 Connection
Module Connection 实现//--------------------------------------------------------------------------- TPooleredConnection = Class; TModuleConnection = Class(TComponent) Private FConnections :TList; FCacheCount :Integer; FCriticalSection :TCriticalSection; FServerName :AnsiString; FServerGUID :TGUID; Procedure SetServerName(Value :AnsiString); Procedure SetServerGUID(Value :AnsiString); Function GetServerGUID( ) :AnsiString; Procedure SetCacheCount( Value :Integer ); Function GetConnectionCount :Integer; Protected Procedure Loaded; Override; Function GetLock( Index :Integer ) :Boolean; Function CreateNewConnection( ) :TPooleredConnection; Procedure RemoveConnection( Connection :TPooleredConnection ); Public Constructor Create( AOwner :TComponent ); Override; Destructor Free( ); Function GetConnection( ) :TPooleredConnection; Procedure ReleaseConnection(Connection :TPooleredConnection); { IAppServer methods } Function AS_GetProviderNames: OleVariant; Function AS_ApplyUpdates(Const ProviderName :WideString; Delta :OleVariant; MaxErrors :Integer; Out ErrorCount :Integer; Var OwnerData :OleVariant) :OleVariant; Function AS_GetRecords(Const ProviderName :WideString; Count :Integer; Out RecsOut :Integer; Options :Integer; Const CommandText :WideString; Var Params, OwnerData :OleVariant) :OleVariant; Function AS_DataRequest(Const ProviderName :WideString; Data :OleVariant) :OleVariant; Function AS_GetParams( Const ProviderName :WideString; Var OwnerData :OleVariant) :OleVariant; Function AS_RowRequest( Const ProviderName :WideString; Row :OleVariant; RequestType :Integer; Var OwnerData :OleVariant) :OleVariant; Procedure AS_Execute( Const ProviderName :WideString; Const CommandText :WideString; Var Params, OwnerData :OleVariant); Published Property ServerName :AnsiString Read FServerName Write SetServerName; Property ServerGUID :AnsiString Read GetServerGUID Write SetServerGUID; Property CacheCount :Integer Read FCacheCount Write SetCacheCount Default 10; Property ConnectionCount :Integer Read GetConnectionCount; End; //---------------------------------------------------------------------------
//--------------------------------------------------------------------------- // { DONE -oBin. :TModuleConnection implementation //--------------------------------------------------------------------------- Constructor TModuleConnection.Create( AOwner :TComponent ); Begin Inherited Create( AOwner ); FCacheCount := 10; FConnections := TList.Create( ); FCriticalSection := TCriticalSection.Create( ); End; //--------------------------------------------------------------------------- Destructor TModuleConnection.Free( ); Var i :Integer; Begin For i := 0 To FConnections.Count Do TPooleredConnection( FConnections[i] ).Free( ); FConnections.Free( ); FCriticalSection.Free( ); End; //--------------------------------------------------------------------------- Procedure TModuleConnection.SetServerName( Value :AnsiString ); Begin If Value <> FServerName Then Begin If Not ( csLoading In ComponentState ) Then Begin If CLSIDFromProgID( PWideChar( WideString( Value ) ), FServerGUID ) <> 0 Then Begin FillChar( FServerGUID, SizeOf( FServerGUID ), 0 ); End; End; FServerName := Value; End; End; //--------------------------------------------------------------------------- Procedure TModuleConnection.SetServerGUID( Value :AnsiString ); Var ServerName: PWideChar; Begin If Value = '' Then FillChar( FServerGUID, SizeOf( FServerGUID ), 0 ) Else Begin FServerGUID := StringToGUID( Value ); If ProgIDFromCLSID( FServerGUID, ServerName ) = 0 Then Begin FServerName := ServerName; CoTaskMemFree(ServerName); End; End; End; //--------------------------------------------------------------------------- Procedure TModuleConnection.SetCacheCount( Value :Integer ); Begin FCacheCount := Value; End; //--------------------------------------------------------------------------- Function TModuleConnection.GetConnectionCount :Integer; Begin Result := FConnections.Count; End; //--------------------------------------------------------------------------- Function TModuleConnection.GetServerGUID( ) :AnsiString; Begin If ( FServerGUID.D1 <> 0 ) Or ( FServerGUID.D2 <> 0 ) Or ( FServerGUID.D3 <> 0 ) Then Begin Result := GUIDToString( FServerGUID ) End Else Result := ''; End; //--------------------------------------------------------------------------- Procedure TModuleConnection.Loaded; Begin Inherited Loaded; If Not (csDesigning In ComponentState) Then Begin End; End; //--------------------------------------------------------------------------- Function TModuleConnection.GetLock( Index :Integer ) :Boolean; Begin FCriticalSection.Enter( ); Try Result := Not TPooleredConnection( FConnections[Index] ).InUse; If Result Then TPooleredConnection( FConnections[Index] ).InUse := True; Finally FCriticalSection.Leave( ); End; End; //--------------------------------------------------------------------------- Function TModuleConnection.GetConnection( ) :TPooleredConnection; Var i :Integer; Begin Result := Nil; For i := 0 To FConnections.Count-1 Do Begin If GetLock( i ) Then Begin Result := TPooleredConnection( FConnections[i] ); Exit; End; End; If Result = Nil Then Result := CreateNewConnection; End; //--------------------------------------------------------------------------- Procedure TModuleConnection.ReleaseConnection( Connection :TPooleredConnection ); Begin FCriticalSection.Enter; Try Connection.InUse := False; If FConnections.Count > FCacheCount Then RemoveConnection( Connection ); Finally FCriticalSection.Leave; End; End; //--------------------------------------------------------------------------- Function TModuleConnection.CreateNewConnection( ) :TPooleredConnection; Begin FCriticalSection.Enter( ); Try Result := TPooleredConnection.Create( ); Result.Connection := CreateComObject( FServerGUID ) As IAppServer; FConnections.Add( Result ); Finally FCriticalSection.Leave( ); End; End; //--------------------------------------------------------------------------- Procedure TModuleConnection.RemoveConnection( Connection :TPooleredConnection ); Var Index :Integer; Begin Index := FConnections.IndexOf( Connection ); If Index <> -1 Then FConnections.Delete( Index ); Connection.Free; End; //--------------------------------------------------------------------------- Function TModuleConnection.AS_GetProviderNames: OleVariant; Var Connection :TPooleredConnection; Begin Connection := GetConnection( ); Try Result := Connection.AppServer.AS_GetProviderNames; Finally ReleaseConnection( Connection ); End; End; //--------------------------------------------------------------------------- Function TModuleConnection.AS_ApplyUpdates( Const ProviderName :WideString; Delta :OleVariant; MaxErrors :Integer; Out ErrorCount :Integer; Var OwnerData :OleVariant ) :OleVariant; Var Connection :TPooleredConnection; Begin Connection := GetConnection( ); Try Result := Connection.AppServer.AS_ApplyUpdates( ProviderName, Delta, MaxErrors, ErrorCount, OwnerData ); Finally ReleaseConnection( Connection ); End; End; //--------------------------------------------------------------------------- Function TModuleConnection.AS_GetRecords( Const ProviderName :WideString; Count :Integer; Out RecsOut :Integer; Options :Integer; Const CommandText:WideString; Var Params, OwnerData :OleVariant) :OleVariant; Var Connection :TPooleredConnection; Begin Connection := GetConnection( ); Try Result := Connection.AppServer.AS_GetRecords( ProviderName, Count, RecsOut, Options, CommandText, Params, OwnerData ); Finally ReleaseConnection( Connection ); End; End;
“应该尽量重复使用数据库连接,因为数据库连接是个费时间的事情,这方面的问题非常复杂,你需要多看看李维的书。” 李维的书说得的确是很理想,但有谁真正用他所阐述的方法开发过系统,如果有,效果是不是好呢?再好的思想观点都得经过实践检验是否可行,如果只是他书上那些简单例子来做论据未免站不住脚,何况上面的例子还有很多没有实现,至少我是如此!
一家之言,尽供参考,请大家多发言讨论!
说实话,我也看过李维的两本书,不过感觉实际上很多照书上做,走不下去。
其实,很多要靠自己去试。
Function AS_GetProviderNames: OleVariant;
Safecall;
Function AS_ApplyUpdates(Const ProviderName :WideString; Delta :OleVariant;
MaxErrors :Integer; Out ErrorCount :Integer; Var OwnerData :OleVariant)
:OleVariant; Safecall;
Function AS_GetRecords(Const ProviderName :WideString; Count :Integer;
Out RecsOut :Integer; Options :Integer; Const CommandText :WideString;
Var Params, OwnerData :OleVariant)
:OleVariant; Safecall;
Function AS_DataRequest(Const ProviderName :WideString; Data :OleVariant)
:OleVariant; Safecall;
Function AS_GetParams(Const ProviderName :WideString;
Var OwnerData :OleVariant)
:OleVariant; Safecall;
Function AS_RowRequest(Const ProviderName :WideString; Row :OleVariant;
RequestType :Integer; Var OwnerData :OleVariant)
:OleVariant; Safecall;
Procedure AS_Execute(Const ProviderName :WideString; Const CommandText
:WideString; Var Params, OwnerData :OleVariant);
Safecall;Function TXXXRemoteDataModule.AS_ApplyUpdates(Const ProviderName: WideString;
Delta: OleVariant; MaxErrors: Integer; Out ErrorCount: Integer;
Var OwnerData: OleVariant)
:OleVariant;
Var
Provider :TCustomProvider;
Begin
Lock;
Try
Provider := Providers[ProviderName];
If Provider.Tag = 0 Then Result := Provider.ApplyUpdates(Delta,
MaxErrors, ErrorCount, OwnerData)
Else Begin
Result := TModuleConnection(Provider.Tag).AS_ApplyUpdates
(Provider.Name, Delta, MaxErrors, ErrorCount, OwnerData);
End;
Finally
UnLock;
End;
End;我利用 CustomProvider (TRemoteDataModule 中定义)中的 Tag, 来存放 TModuleConnection 地址, TModuleConnection 是我写的一个控件, 可以认为是一个 TList 用于存放先分配好的那些 Connection
TPooleredConnection = Class;
TModuleConnection = Class(TComponent)
Private
FConnections :TList;
FCacheCount :Integer;
FCriticalSection :TCriticalSection;
FServerName :AnsiString;
FServerGUID :TGUID; Procedure SetServerName(Value :AnsiString);
Procedure SetServerGUID(Value :AnsiString);
Function GetServerGUID( ) :AnsiString;
Procedure SetCacheCount( Value :Integer );
Function GetConnectionCount :Integer;
Protected
Procedure Loaded; Override;
Function GetLock( Index :Integer ) :Boolean;
Function CreateNewConnection( ) :TPooleredConnection;
Procedure RemoveConnection( Connection :TPooleredConnection );
Public
Constructor Create( AOwner :TComponent ); Override;
Destructor Free( );
Function GetConnection( ) :TPooleredConnection;
Procedure ReleaseConnection(Connection :TPooleredConnection);
{ IAppServer methods }
Function AS_GetProviderNames: OleVariant;
Function AS_ApplyUpdates(Const ProviderName :WideString; Delta :OleVariant;
MaxErrors :Integer; Out ErrorCount :Integer; Var OwnerData :OleVariant)
:OleVariant;
Function AS_GetRecords(Const ProviderName :WideString; Count :Integer;
Out RecsOut :Integer; Options :Integer; Const CommandText :WideString;
Var Params, OwnerData :OleVariant)
:OleVariant;
Function AS_DataRequest(Const ProviderName :WideString; Data :OleVariant)
:OleVariant;
Function AS_GetParams( Const ProviderName :WideString;
Var OwnerData :OleVariant)
:OleVariant;
Function AS_RowRequest( Const ProviderName :WideString; Row :OleVariant;
RequestType :Integer; Var OwnerData :OleVariant)
:OleVariant;
Procedure AS_Execute( Const ProviderName :WideString; Const CommandText
:WideString; Var Params, OwnerData :OleVariant);
Published
Property ServerName :AnsiString Read FServerName Write SetServerName;
Property ServerGUID :AnsiString Read GetServerGUID Write SetServerGUID;
Property CacheCount :Integer Read FCacheCount Write SetCacheCount Default 10;
Property ConnectionCount :Integer Read GetConnectionCount;
End;
//---------------------------------------------------------------------------
// { DONE -oBin. :TModuleConnection implementation
//---------------------------------------------------------------------------
Constructor TModuleConnection.Create( AOwner :TComponent );
Begin
Inherited Create( AOwner ); FCacheCount := 10;
FConnections := TList.Create( );
FCriticalSection := TCriticalSection.Create( );
End;
//---------------------------------------------------------------------------
Destructor TModuleConnection.Free( );
Var
i :Integer;
Begin
For i := 0 To FConnections.Count Do
TPooleredConnection( FConnections[i] ).Free( );
FConnections.Free( );
FCriticalSection.Free( );
End;
//---------------------------------------------------------------------------
Procedure TModuleConnection.SetServerName( Value :AnsiString );
Begin
If Value <> FServerName Then
Begin
If Not ( csLoading In ComponentState ) Then
Begin
If CLSIDFromProgID( PWideChar( WideString( Value ) ), FServerGUID )
<> 0 Then
Begin
FillChar( FServerGUID, SizeOf( FServerGUID ), 0 );
End;
End;
FServerName := Value;
End;
End;
//---------------------------------------------------------------------------
Procedure TModuleConnection.SetServerGUID( Value :AnsiString );
Var
ServerName: PWideChar;
Begin
If Value = '' Then FillChar( FServerGUID, SizeOf( FServerGUID ), 0 )
Else Begin
FServerGUID := StringToGUID( Value );
If ProgIDFromCLSID( FServerGUID, ServerName ) = 0 Then
Begin
FServerName := ServerName;
CoTaskMemFree(ServerName);
End;
End;
End;
//---------------------------------------------------------------------------
Procedure TModuleConnection.SetCacheCount( Value :Integer );
Begin
FCacheCount := Value;
End;
//---------------------------------------------------------------------------
Function TModuleConnection.GetConnectionCount :Integer;
Begin
Result := FConnections.Count;
End;
//---------------------------------------------------------------------------
Function TModuleConnection.GetServerGUID( ) :AnsiString;
Begin
If ( FServerGUID.D1 <> 0 ) Or
( FServerGUID.D2 <> 0 ) Or
( FServerGUID.D3 <> 0 ) Then
Begin
Result := GUIDToString( FServerGUID )
End
Else Result := '';
End;
//---------------------------------------------------------------------------
Procedure TModuleConnection.Loaded;
Begin
Inherited Loaded; If Not (csDesigning In ComponentState) Then
Begin
End;
End;
//---------------------------------------------------------------------------
Function TModuleConnection.GetLock( Index :Integer ) :Boolean;
Begin
FCriticalSection.Enter( );
Try
Result := Not TPooleredConnection( FConnections[Index] ).InUse;
If Result Then
TPooleredConnection( FConnections[Index] ).InUse := True;
Finally
FCriticalSection.Leave( );
End;
End;
//---------------------------------------------------------------------------
Function TModuleConnection.GetConnection( ) :TPooleredConnection;
Var
i :Integer;
Begin
Result := Nil;
For i := 0 To FConnections.Count-1 Do
Begin
If GetLock( i ) Then
Begin
Result := TPooleredConnection( FConnections[i] );
Exit;
End;
End;
If Result = Nil Then Result := CreateNewConnection;
End;
//---------------------------------------------------------------------------
Procedure TModuleConnection.ReleaseConnection( Connection :TPooleredConnection );
Begin
FCriticalSection.Enter;
Try
Connection.InUse := False;
If FConnections.Count > FCacheCount Then
RemoveConnection( Connection );
Finally
FCriticalSection.Leave;
End;
End;
//---------------------------------------------------------------------------
Function TModuleConnection.CreateNewConnection( ) :TPooleredConnection;
Begin
FCriticalSection.Enter( );
Try
Result := TPooleredConnection.Create( );
Result.Connection := CreateComObject( FServerGUID ) As IAppServer;
FConnections.Add( Result );
Finally
FCriticalSection.Leave( );
End;
End;
//---------------------------------------------------------------------------
Procedure TModuleConnection.RemoveConnection( Connection :TPooleredConnection );
Var
Index :Integer;
Begin
Index := FConnections.IndexOf( Connection );
If Index <> -1 Then FConnections.Delete( Index );
Connection.Free;
End;
//---------------------------------------------------------------------------
Function TModuleConnection.AS_GetProviderNames: OleVariant;
Var
Connection :TPooleredConnection;
Begin
Connection := GetConnection( );
Try
Result := Connection.AppServer.AS_GetProviderNames;
Finally
ReleaseConnection( Connection );
End;
End;
//---------------------------------------------------------------------------
Function TModuleConnection.AS_ApplyUpdates( Const ProviderName :WideString;
Delta :OleVariant; MaxErrors :Integer; Out ErrorCount :Integer;
Var OwnerData :OleVariant ) :OleVariant;
Var
Connection :TPooleredConnection;
Begin
Connection := GetConnection( );
Try
Result := Connection.AppServer.AS_ApplyUpdates( ProviderName,
Delta, MaxErrors, ErrorCount, OwnerData );
Finally
ReleaseConnection( Connection );
End;
End;
//---------------------------------------------------------------------------
Function TModuleConnection.AS_GetRecords( Const ProviderName :WideString;
Count :Integer; Out RecsOut :Integer; Options :Integer;
Const CommandText:WideString; Var Params, OwnerData :OleVariant) :OleVariant;
Var
Connection :TPooleredConnection;
Begin
Connection := GetConnection( );
Try
Result := Connection.AppServer.AS_GetRecords( ProviderName,
Count, RecsOut, Options, CommandText, Params, OwnerData );
Finally
ReleaseConnection( Connection );
End;
End;