Delphi三层实例

来源:互联网 发布:js unicode编码 编辑:程序博客网 时间:2024/06/05 15:47
所谓三层:(1)客户端(2)服务器端(3)数据库
 
在数据访问时,使得客户端必须通过服务器来访问数据库。提高了系统的安全性。
在Delphi中可以使用Socket或者Dcom来连接他们相互间的通讯。如果使用Scocket在系统使用时必须提供Scocket连接器,而Dcom则不用。客户端和服务器的连接需要Broker来联系。
环境为winxp sp2 + delphi 7 + db7.(MSSQL2000)
创建过程:
1、选择"Multitier"--"Remote data module"。在跳出来的对话框里面输入名称(任意),例如:AppSqlConn。选择确定,进入remote data module窗口。
 
2、加入组件:adodataset,点击connectionstring属性,点击后面的…,进入设定连接窗口。选择:use connection string--build,在提供程序中选择:"Microsoft ole db provider for sql server",在连接中:服务器名称输入sql server的ip地址,登录信息中输入用户名和密码(sql server),在选择数据库中选择自己想要使用的数据库。一般只要地址正确、用户名和密码无误,肯定可以连接通过。确定退出。
 
3、在commandtext中点击后面的…,进入sql 语句设定,根据自己的要求设定。
 
4、将active属性设置为true。只要前面的设定是正确的,这里应该顺利通过。
5、加入组件:datasetprovider。设定其dataset属性为上面的adodataset。
6、到此服务器端已经设置完成。请保存并且运行一次,从而使服务注册。
7、运行delphi的bin目录下面的scktsrvr,因为下面要使用socket连接。运行后任务栏中出现socket server的图标。
 
8、新建程序(application),然后file--new--data module,会创建客户端的data module。
9、加入组件:socketconnection,在address中输入sql server的ip地址,然后在servername中输入刚才创建的remote data module的服务程序。程序会自动在serverguid中加入id。然后选择connected属性为true。只要此处不报告错误,此程序基本成功了。
10、加入组件:clientdataset,选择remoteserver属性为socketconnection,选择providename为服务器程序的datasetprovider。然后选择active属性为true。
11、到程序的form窗口状态,首先选择file--use unit,选择上面创建的data module,确定。然后加入组件datasource和dbgrid。选择datasourece的dataset属性为data module的clientdataset,选择dbgrid的datasource为这里的datasource组件。现在应该可以看到dbgrid的窗口中出现了想要的数据。保存并且编译客户端程序。
12、客户端分发软件设定。从服务器端拷贝midas.dll文件到本机的system(98)或者system32(2000),一般自己就可以注册,否则用regsvr32 midas.dll注册一下。然后将刚才客户端程序拷贝过来运行一下,应该可以正常运行。
 
还有拒绝访问的错误我也碰到过,是没有打开borland socket server,该程序是borland/bin/scktsrvr.exe
注册服务器端时应该使用 run-install mts objects安装到组件服务里,安装完后可以在
控制面板-组件服务里找到服务器的GUID,然后填到borland socket server里就可以了。
 
scktsrvr.exe是一个NT的服务程序,你用scktsrvr.exe -install安装之后,每次系统启动,它都会自动运行的。如果你的客户端用了socketconnection,每次连接应用服务器的时候,都需要通过scktsrvr.exe才能访问到你的应用服务器.
scktsrvr.exe -uninstall 即可卸载
midas.dll是个什么文件?
1.数据库三层的文件.
2.TClientDataSet小家碧玉,恐怕人人都喜欢使用。但是,都知道娶TClientDataSet是有代价的,因为你同时也得面对丈母娘MIDAS.DLL。
3.delphi用来设计多层应用程序的动态库,用来作客户端和服务器连接和处理。
4. Multi-tier distributed application service
   多层分布式开发服务
   midas.dll是它运行时需要的动态连接库。
5.你安装好delphi时装在system32里的,delphi的一个DLL.
 
能不能使用TClientDataSet又不用MIDAS.DLL呢?
很简单,就是uses一下MidasLib单元!
MidasLib单元在Delphi6中才有,是Lib目录下的一个dcu文件。一旦在你的源程序中引用了MidasLib单元,程序运行时就不再需要MIDAS.DLL文件。
然而,编译后程序大小一定会增加200k以上,即使使用包编译模式。
 
在使用DataModel窗体存放数据库控件的程序中,应注意:
1、如果在其他窗体中调用DataModel中的控件必须在先引用Datemodel的单元,在implementation下面加入以下语句
uses
     DataModel的单元名称;
2、必须改变窗体的创建顺序,datamodel窗体必须先于引用该窗体控件的窗体创建
三层结构
 
应用层:
 
一、需要三个数据控件:
Adoconnect        
AdoQuery
DataSetProvider
二、联接顺序:
SQL数据库<--Adoconnect<--Adoquery<--DataSetProvider
三、属性设置:
Adoconnect:      1、ConnectionString
                  2、Connected
Adoquery:         1、connection
                  //2、Active
DataSetProvider: 1、DataSet
                  2、option.poAllowCommand:=true
四、其他
1、运行SocketServer并开端口
2、数据模块使用远程数据模块
 
客户端:
 
一、基本控件
SocketConnection
ClientDataSet
DataSoure
DBGrid
二、联接顺序:
应用层<--SocketConnection<--ClientDataSet<--DataSoure<--DBGrid
三、属性设置:
SocketConnection: 1、Adrress
                   2、ServerName
                   3、Port
                   4、connected
ClientDataSet:    1、RemoteServer
                   2、CommandText
                   3、ProviderName
                   4、Active
DataSoure:        1、DataSet
DBGrid:           1、DataSoure
四、其他
一、先关闭记录集后执数据库操作语句
    rs.close
    sqlstr="insert into abc(a,b,c) values("a","b","c")"
    rs.open sqlstr,conn
    rs.update
    rs.colose
二、在可能发生出错的数据库操作做错误捕捉,如:open,update,delete等
    rs.close
    sqlstr="insert into abc(a,b,c) values("a","b","c")"
    on error resume next 
    rs.open sqlstr,conn
    if err then
    msgbox(err.Description)
    msgbox(cstr(err.number))
    rs.update
    rs.colose
一、Midas的安全问题。
    Midas技术是Delphi中进行三层开发的首选技术,它不仅有纯DCOM/COM+(COM+技术是.NET技术的基础)的优点,而且也结合了Delphi的快速开发特性,可以快速开发出想要的系统,其开发速度是用VC,PB等开发DCOM的数十倍,把程序员从烦杂的代码中解脱出来,从而将更多的精力投入到业务逻辑的设计中去。
    但是Midas技术的一个最令人担忧的就是它的安全问题:
远端只要知道应用服务器的端口号即可访问到应用服务器,而一旦访问到应用服务器,TClientDataSet即可获得ProviderNames列表。一旦知道了ProviderNames列表,这就相当于将数据库暴露在外了。
关于可轻易获得ProviderNames列表的问题,我使用下面的方法解决:
在服务器端定义一个
LoginMTS(const AUserId, APassword: WideString): WordBool;
方法。初始状态下,所有的DataSetProvider和数据集的连接断开。用户必须调用LoginMTS并传递用户名和密码,登陆成功才将DataSetProvider和数据集的连接打开。这样如果用户验证没有通过,即使它获得了ProviderNames列表也没法调用接口中的方法对数据库进行操作。
 
二、Midas中主从表的实现
主从表的应用在信息系统中应用很广。在两层开发中我们可以通过直接建立两个数据集之间为主从关系来实现主从表;
在三层中虽然我们仍然可以通过直接建立两个数据集之间为主从关系来实现主从表,但是这样就要求把数据库中所有相关的数据行都下载到本地,丧失了三层开发的优势。我在实际中使用下面的方法实现。这里我以实现入库单查询、添加、修改、删除(CRUD)为例来讲解:
(1)新建一个MTS Data Module,命名为TmtsStockInListBiz,增加如下方法:
    function QueryStockInListMasterById(const AId: WideString;var ADatas: OleVariant): WordBool; safecall;
    function QueryStockInListSlaveByMasterId(const AId: WideString;var ADatas: OleVariant): WordBool; safecall;
    procedure UpdataStockInListMaster(var ADatas: OleVariant); safecall;
    procedure UpdataStockInListSlave(var ADatas: OleVariant); safecall;
    function GenerateStockInListId: WideString; safecall;
QueryStockInListMasterById作用是根据入库单单号查询入库单的基本信息(入库日期、负责人等),Aid为入库单单号,Adatas为返回值,其格式就是Midas的数据包,可以将其附给ClientDatSet的Data属性。
QueryStockInListSlaveByMasterId作用是根据入库单单号查询入库单的详细信息(商品条码,数量)
UpdataStockInListMaster是对入库单主表进行删除、添加、修改操作。只要将ClientDataSet的Delta属性做为传递即可。
UpdataStockInListSlave是对入库单从表进行删除、添加、修改操作。
GenerateStockInListId是产生一个唯一的入库单号。
 
下面是几个方法的代码,都很简单,就不多解释了,可以查看Delphi的帮助。
function TmtsStockInListBiz.QueryStockInListMasterById(
const AId: WideString; var ADatas: OleVariant): WordBool;
begin
result := false;
ADatas := null;
try
    cdsQuery.Close;
    cdsQuery.CommandText := 'select * from t_StockInListMaster where Id=:Id';
    cdsQuery.Params.ParamByName('Id').AsString := AId;
    cdsQuery.Open;
    if cdsQuery.RecordCount > 0 then
    begin
      result := true;
      ADatas := cdsQuery.Data;
    end;
finally
    cdsQuery.Close;
end;
end;
 
procedure TmtsStockInListBiz.UpdataStockInListMaster(
var ADatas: OleVariant);
var
eCount: Integer;
OwnerData: OleVariant;
begin
DCOMConStockInList.GetServer.AS_ApplyUpdates('dspStockInListMaster',
    ADatas, -1, eCount, OwnerData);
end;
 
function TmtsStockInListBiz.GenerateStockInListId: WideString;
var
LPrior: string;
i: Integer;
begin
cdsQuery.Close;
cdsQuery.CommandText := 'select top 1 id from t_StockInListMaster order by id desc';
cdsQuery.Open;
LPrior := cdsQuery.FieldByName('Id').AsString;
i := StrToIntDef(RightStr(LPrior,8),0);
Inc(i);
result := 'RK' + FormatFloat('00000000',i);
cdsQuery.Close;
end;
(2)、新建一个应用程序,通过DCOMConnection、SocketConnection等连接到MTS组件,然后就可以调用MTS的相应的方法实现客户端功能了。
放入cdsStockInListMaster、cdsStockInListSlave两个ClientDataSet控件,在控件上点击右键,选择“FieldsEditor”新建于服务器中的字段同样的字段,然后再次在控件上单击右键,选择“CreateDataSet”,建立一个本地数据库。
(3)根据入库单号查询入库单的方法实现:
procedure TFormStockInList.BtnFindClick(Sender: TObject);
var
v,vs: OleVariant;
begin
if SocketConStockInList.AppServer.QueryStockInListMasterById(Trim(LEdtId.Text), v) then
begin
    cdsStockInListMaster.Data := v;//显示入库单主表(主要信息)
    if SocketConStockInList.AppServer.QueryStockInListSlaveByMasterId(Trim(LEdtId.Text), vs) then
      cdsStockInListSlave.Data := vs; ;//显示入库单从表(明细信息)
end
else
    ShowMessage('此单不存在!');
end;
(4)新建入库单的实现
procedure TFormStockInList.BtnNewClick(Sender: TObject);
var
LId: string;
begin
ClearCDSRecord;
cdsStockInListMaster.Open;
cdsStockInListMaster.Insert;
LId := SocketConStockInList.AppServer.GenerateStockInListId;
LEdtId.Text := LId;
cdsStockInListMaster.FieldByName('Id').AsString := LId;
cdsStockInListMaster.FieldByName('GenerateDate').AsDateTime := Now();
end;
(5)提交功能的实现
procedure TFormStockInList.BtnPostClick(Sender: TObject);
var
LQuerymts: ImtsQueryObjDisp;
LBar: string;
begin
SetSocketConnectionConnect(SocketConQuery);
LQuerymts := ImtsQueryObjDisp(SocketConQuery.GetServer);
SocketConQuery.Close;
if cdsStockInListMaster.RecordCount > 0 then
    SocketConStockInList.AppServer.UpdataStockInListMaster(cdsStockInListMaster.Delta);
if cdsStockInListSlave.RecordCount > 0 then
SocketConStockInList.AppServer.UpdataStockInListSlave(cdsStockInListSlave.Delta);
end;
注:本文中ClientDataSet控件的名称开头一般为cds、TsocketConnection控件的名称开头一般为SocketCon。
三、动态设置TsimpleObjectBroker的服务器列表
procedure SetSocketConnectionConnect(AValue: TSocketConnection);
procedure FillAppServerList(ABroker: TSimpleObjectBroker);
var
   sl: TStringList;
   i, n: Integer;
begin
    sl := TStringList.Create;
    从配置文件中读取服务器列表,并保存到sl中;
    n := sl.Count - 1;
    ABroker.ServerData := null;
    for i := 0 to n do
    begin
      ABroker.Servers.Add;
      ABroker.Servers[i].ComputerName := sl.Strings[i]
   end;
    sl.Free;
end;
var
LBroker: TSimpleObjectBroker;
begin
LBroker := TSimpleObjectBroker.Create(nil);
    FillAppServerList(LBroker);
    AValue.ObjectBroker := LBroker;
    try
      AValue.Connected := true;
    except
      raise Exception.Create('应用服务器连接错误!');
    end;
    LBroker.Free;
end;
0 0
原创粉丝点击