是否可以在没有.DFM的情况下使用数据模块?

时间:2015-06-24 21:58:59

标签: delphi datamodule dfm

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

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;

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

AdoConnect仅在DataModuleCreate事件中运行一次。 TDatModule派生自TPersistent,允许在整个运行时中持久保存单个连接实例。

到目前为止,唯一令我烦恼的是 - 无用的.DFM,我根本不需要。
有没有选择摆脱它?

3 个答案:

答案 0 :(得分: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.

<强>继承

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

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;

答案 1 :(得分:2)

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

另外,正如评论中所提到的,不要对TPersistent的含义感到困惑。此类的使用方式完全不同,可以集成到IDE Object Inspector中(作为组件中的子属性)。

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

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;

至于存在&#34; persistent&#34;的解释,您可以通过多种方式创建/销毁它的实例。例如,您可以使用单元的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时,将实例化一个新的全局实例。然后,每次重新使用相同的实例。这也解决了ActiveXCoInitialize等的需要,因为在这一点上,COM预计已经被实例化(这是ADO所必需的)。

使用此单元非常简单 - 请将其包含在uses的任何位置,并通过MyData函数访问其实例。

备注

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

答案 2 :(得分:0)

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

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