3

我在一个单独的数据模块中卸载了所有 ADO 引擎盖,因此一个模块可以被多个应用程序引用。我所有的应用程序基本上只需要两个工作方法来访问数据:

AdoQueryTADODataSet.
AdoExecute执行简单的更新/删除查询而不获取任何结果。

这是类结构:

type
  TMyDataModule = class(TDataModule)
    procedure DataModuleCreate(Sender: TObject);
    procedure DataModuleDestroy(Sender: TObject);
  private
    procedure pvtAdoConnect;
    procedure pvtAdoExecute(const sql: string);
    function pvtAdoQuery(const sql: string): TADODataSet;
  public
    AdoConnection: TADOConnection;
  end;

然后我在类方法中添加了两个公开的包装器。我用它来避免调用中的长类引用:

function AdoQuery(const sql: string): TADODataSet;
procedure AdoExecute(const sql: string);

implementation

function AdoQuery(const sql: string): TADODataSet;
begin
  Result := MyDataModule.pvtAdoQuery(sql);
end;

以上是我在所有表单中调用的工作函数。

AdoConnectDataModuleCreate事件中只运行一次。TDatModule 派生自TPersistent,它允许在整个运行时持久化连接的单个实例。

到目前为止,唯一让我烦恼的是一个无用的 .DFM,我根本不需要它。
有没有办法摆脱它?

4

3 回答 3

4

我会以两种方式中的一种来处理这种类型的事情,使用接口或继承。在这些情况下,我宁愿不向外界公开课程。第二个几乎可以称为没有接口的接口:)

接口

此版本返回一个包含所需方法的接口。外界只需要使用接口即可。我们将实施细节保密。我们的 TMyDBClass 实现了我们向外界公开的接口,我们的全局函数GetDBInterface返回单个实例。

interface

uses
  ADODB;

type
  IMyDBInterface = interface
  ['{2D61FC80-B89E-4265-BB3D-93356BD613FA}']
    function AdoQuery(const sql: string): TADODataSet;
    procedure AdoExecute(const sql: string);
  end;

function GetDBInterface: IMyDBInterface;

implementation

type
  TMyDBClass = class(TInterfacedObject, IMyDBInterface)
  strict private
    FConnection: TADOConnection;
  protected
    procedure AfterConstruction; override;
    procedure BeforeDestruction; override;
  public
    function AdoQuery(const sql: string): TADODataSet;
    procedure AdoExecute(const sql: string);
  end;

var
  FMyDBInterface: IMyDBInterface;

procedure TMyDBClass.AdoExecute(const sql: string);
begin
  // ...
end;

function TMyDBClass.AdoQuery(const sql: string): TADODataSet;
begin
  // ...
end;

procedure TMyDBClass.AfterConstruction;
begin
  inherited;
  FConnection := TADOConnection.Create(nil);
end;

procedure TMyDBClass.BeforeDestruction;
begin
  FConnection.Free;
  inherited;
end;

// Our global function

function GetDBInterface: IMyDBInterface;
begin
  if not Assigned(FMyDBInterface) then
    FMyDBInterface := TMyDBClass.Create;
  Result := FMyDBInterface;
end;

initialization

finalization
  FMyDBInterface := nil;
end.

遗产

此版本使用具有所需方法的基类。这对人们来说更容易处理,因为它排除了对刚开始的人来说可能很复杂的界面。我们再次向用户隐藏实现细节,只公开一个类的外壳,其中包括我们希望人们访问的两种方法。这些方法的实现由继承自公开类的实现中的类执行。我们还有一个返回此类实例的全局函数。接口方法相对于这种方法的最大优势是该对象的用户不能意外释放该对象。

interface

uses
  ADODB;

type
  TMyDBClass = class(TObject)
  public
    function AdoQuery(const sql: string): TADODataSet; virtual; abstract;
    procedure AdoExecute(const sql: string); virtual; abstract;
  end;

function GetDBClass: TMyDBClass;

implementation

type
  TMyDBClassImplementation = class(TMyDBClass)
  strict private
    FConnection: TADOConnection;
  protected
    procedure AfterConstruction; override;
    procedure BeforeDestruction; override;
  public
    function AdoQuery(const sql: string): TADODataSet; override;
    procedure AdoExecute(const sql: string); override;
  end;

var
  FMyDBClass: TMyDBClassImplementation;

procedure TMyDBClassImplementation.AdoExecute(const sql: string);
begin
  inherited;
  // ...
end;

function TMyDBClassImplementation.AdoQuery(const sql: string): TADODataSet;
begin
  inherited;
  // ...
end;

procedure TMyDBClassImplementation.AfterConstruction;
begin
  inherited;
  FConnection := TADOConnection.Create(nil);
end;

procedure TMyDBClassImplementation.BeforeDestruction;
begin
  FConnection.Free;
  inherited;
end;

// Our global function

function GetDBClass: TMyDBClass;
begin
  if not Assigned(FMyDBClass) then
    FMyDBClass := TMyDBClassImplementation.Create;
  Result := FMyDBClass;
end;

initialization
  FMyDBClass := nil;
finalization
  FMyDBClass.Free;
end.

用法

使用这些真的很容易。

implementation

uses
  MyDBAccess; // The name of the unit including the code

procedure TMyMainForm.DoSomething;
var
  myDataSet: TADODataSet;
begin
  myDataSet := GetDBInterface.AdoQuery('SELECT * FROM MyTable');
  ...
  // Or, for the class version
  myDataSet := GetDBClass.AdoQuery('SELECT * FROM MyTable');
  ...
end;
于 2015-06-24T23:52:53.060 回答
2

如果您没有将任何设计时非可视组件拖放到数据模块上,并且不打算这样做,那么您根本不需要数据模块。整个目的是用于设计时组件和其他实现,例如 Web 模块甚至 Windows 服务应用程序。但不适用于包装没有设计时组件的纯代码。

另外,正如评论中提到的,不要对TPersistent. 此类的使用方式完全不同,并且可以集成到 IDE 对象检查器中(作为组件中的子属性)。

因此,理想的做法是将所有内容封装在一个类中。出于您的目的,数据库连接...

type
  TMyData = class(TObject)
  private
    FConnection: TADOConnection;
  public
    constructor Create;
    destructor Destroy; override;
    procedure pvtAdoConnect;
    procedure pvtAdoExecute(const sql: string);
    function pvtAdoQuery(const sql: string): TADODataSet;
    ...
  end;

implementation

{ TMyData }

constructor TMyData.Create;
begin
  FConnection:= TADOConnection.Create(nil);
end;

destructor TMyData.Destroy;
begin
  FConnection.Connected:= False;
  FConnection.Free;
  inherited;
end;

至于“持久”的解释,您可以通过多种方式创建/销毁它的实例。例如,您可以使用一个单元initializationfinalization部分(需要CoInitialize),或者您可以让您的主窗体在创建时初始化一个全局实例。

这样做的一种常见方法是添加...

interface

function MyData: TMyData;

implementation

var
  _MyData: TMyData;

function MyData: TMyData;
begin
  if not Assigned(_MyData) then
    _MyData:= TMyData.Create;
  Result:= _MyData;
end;

initialization
  _MyData:= nil;
finalization
  _MyData.Free;
end.

第一次MyData从任何地方调用时,将实例化一个新的全局实例。然后,每次它重新使用相同的实例。这也解决了等的需要,ActiveX因为CoInitialize此时,预计 COM 已经被实例化(这是 ADO 所必需的)。

这个单元的使用将非常简单——在任何地方使用包含它,并通过函数uses访问它的实例。MyData

笔记

你应该摆脱全局变量的习惯。在尝试做以后的工作时,这是在自找麻烦。上面的示例显示了如何适应全局对象实例。所有其他变量都应该在该对象中自包含,或者通常是范围/相关性之一。您的全部控制权TADOConnection都应该在这里,包括连接/断开连接、异常处理、分配连接字符串。

于 2015-06-24T23:24:50.677 回答
0

如果您可能对完全没有 DataModules 的替代方案感兴趣,请查看以下内容: https ://github.com/stijnsanders/xxm/blob/master/Delphi/demo2/03%20Data%20ADO/xxmData.pas

查询存储在单个.sql文件中,便于在特定的 SQL 编辑器或工作台中对其进行编辑。查询用带有 的行分隔--"QueryName",并在启动时加载到查询存储中。假设您大部分时间都在查询较小的记录集,最好的锁定和打开方式是只读和静态的,这样可以提供最好的性能和最少的数据库服务器负载。获取字段值使用Collect调用,这也提供了一点性能增益。

于 2015-06-25T07:23:49.637 回答