delphi_三层架构简单例子.

合集下载

DelphiXE10跨平台三层数据库应用教程绑定

DelphiXE10跨平台三层数据库应用教程绑定

DelphiXE10跨平台三层数据库应用教程绑定Delphi XE 开始越来越庞大,比经典的Delphi7难用,但依然是目前所有跨平台开发工具中开发效率最高、最容易上手的,其快速设计RAD理念是无与伦比的符合人性(什么?是懒惰)。

目前网上XE10类似教程很少,而且学习途中遇到一些问题就难以继续了,经本菜鸟千辛万苦的求索,特推出Delphi XE 10系列教程。

三层数据库应用是目前最简单、方便、易扩展的架构,而跨平台应用又是所谓的“互联网+”最需要的,下面的教程分服务器端和客户端两大部分完成该设计。

一、Delphi XE10 datasnap服务器设计(1)生成DataSnap服务器的框架初学者都是呆子,还是用向导吧,主菜单“File”->“New”->“Other…”得到“New Items”向导对话框。

见图一。

图一、?向导一般选择有窗口的Forms程序,如果是正式场合,建议Service 程序。

见图二。

图二、选择应用类型如果服务器程序运行在Windows平台就选“VCLapplication”,如果要跨平台,还是选“FireMonkeyapplication”,但生成的文件要大一些。

见图三。

图三、选择应用基本库源默认TCP/IP为通讯协议,简单快速,菜鸟专用的Sample Methods用于测试,见图四。

图四、选择通讯等参数缺省211端口,别忘了“Test Port”一下更健康,见图五。

图五、测试端口不要使用默认的“TComponent”,而用TDSServerModule作为数据服务提供主体,非常方便以后变更为能挣钱的Service应用服务。

图六、选择服务模式Finish这个向导后,硬盘一阵轰鸣,自动生成了工程及其三个主文件,图七:ServerContainerUnit1.pas放的是网络服务相关控件,非高手莫入。

ServerMehtodsUnit1.pas就是我们第二步主要的活动场所。

三层架构及实例演示

三层架构及实例演示

三层架构及实例演示简述通常意义上的三层架构就是将整个业务应用划分为:表现层(UI)、业务逻辑层(BLL)、数据访问(DAL)。

实例 登录界面这是一个经过改版的登录,视频里的用c#来实现的。

使用的工具:sql server 2010 、vs 2010建立数据库:Login 表:Scores、UsersScore表Users表LoginModel(实体层)创建实体层LoginModel,建立Userinfo类,存储从UI返回的数据。

这里我们称为业务实体层,因为他也是为业务逻辑服务的。

[vb] view plaincopyprint?Public Class UserInfoPrivate _ID As StringPrivate _Username As StringPrivate _Password As StringPrivate _Email As StringPublic Property ID As IntegerGetReturn _IDEnd GetSet(ByVal value As Integer)_ID = valueEnd SetEnd PropertyPublic Property Username As StringGetReturn _UsernameEnd GetSet(ByVal value As String)_Username = valueEnd SetEnd PropertyPublic Property Password As StringGetReturn _PasswordEnd GetSet(ByVal value As String)_Password = valueEnd SetEnd PropertyPublic Property Email As StringGetReturn _EmailEnd GetSet(ByVal value As String)_Email = valueEnd SetEnd PropertyEnd ClassLoginBLL(业务逻辑层)UI层调用业务逻辑层来执行登录的操作。

Delphi三层开发小技巧:TClientDataSet的Delta妙用

Delphi三层开发小技巧:TClientDataSet的Delta妙用

Delphi三层开发小技巧:TClientDataSet的Delta妙用Delphi三层开发小技巧:TClientDataSet的Delta妙用Delphi做三层开发时,很多人都会在客户端放一个TClientDataSet,中间层远程数据模块就对应放一个TDataSetProvider,然后再连起来.其实这种方法很烦琐,而且程序痈肿不甘,不好维护.我们都知道TClientDataSet的Delta属性记录了数据的所有修改,应用它我们就可以方便的实现一个单表更新的通用方法.首先,在中间层添加一个方法,就叫ApplyUpdates吧.方法定义如下: function ApplyUpdates(const UpdateTable:String;Delta:Variant;out err:String):Boolean;参数UpdateTable是指要更新的表名,Delta是指传过来的TClientDataSet的Delta属性,如果更新错误err返回错误的内容.下面实现这个方法,首先在DataModule上放一个Query,Query连上Connection,然后再放一个TDataSetProvider连Query.代码如下: function TRoDm.ApplyUpdates(const UpdateTable:String;Delta:Variant;out err:String):Boolean;const sql='select * from %s where 1<>1';var sqlstr:string;ErrCount:Integer;beginResult:=False;sqlstr:=Format(sql,[UpdateTable]);tryConn.BeginTrans;Query.Close;Query.sql.text:=sqlstr;Query.open;Provider.ApplyUpdates(Delta,-1,ErrCount);Result:=ErrCount=0;if Result thenmitTranselse Conn.RollbackTrans;excepton E:Exception dobeginConn.RollbackTrans;err:=E.Message;end;end;end;到此,通用的更新方法已经完成了.不过客户端的ClientDataSet还不能查询显示数据,因此,还要写一个查询方法:function QuerySQL(const sqlstr:string;out Data:Variant;out err:String):Boolean;参数sqlstr就是要持行的查询语句,Data返回查询结果,错误时err 返回错误消息QuerySQL实现代码如下:function TRoDm.QuerySQL(const sqlstr:string;out Data:Variant;out err:String):Boolean;beginResult:=False;tryQuery.close;Query.sql.text:=sqlstr;Query.sql.Open;Data:=Provider.Data;Result:=True;Excepton E:Exception doerr:=E.Message;end;end;到这里,中间层的代码已经完了,客户端的调用就简单了.比如客户端有个数据模块DM,上面放一个DcomConnection或者SocketConnection,名叫Conn.例如,我们现在要做一个商品管理的功能,在窗体上放一个TClientDataSet叫Cds,放DataSource,DBGrid等,设置好相应的属性.然后在窗体创建(Create事件)时查询回所有数据,代码如下:const sql='select * from xxxx';var Data:Variant;err:String;beginif Dm.Conn.AppServer.QuerySQL(sql,Data,err) thenCds.Data:=Dataelse MessageBox(self.handle,pchar('查询数据出错:'+err),'错误',MB_OK+MB_ICONERROR);end;然后还有"添加","修改","删除"按扭,代码都和我们平时操作一样,比如"添加"按扭的代码:cds.append;cds.fieldbyname('xxx').asinteger:=xxx;//....cds.post;修改,删除也这样写.不过现在还有个小问题是,这个表的主键的生成问题,这里我们不能用自增主键,要自己自己生成主键,这样你还得在中间层写一个中间层生成主键的方法,在"增加"按扭时生调用生成主键,然后再上面的操作.这里不再多说.增删改完后,这时的数据还在客户端的内存里,想保存到远程的中间层服务器就要用到我们刚才的方法了,下面就是"保存"按扭下的代码:var err:string;beginif cds.ChangeCount=0 then exit;//数据没改变就不用提交了if Dm.Conn.AppServer.ApplyUpdates('xxx',cds.Delta,err) then//xxx就是表名了beginMessageBox(self.handle,'保存成功!','提示',MB_OK+MB_ICONINFORMATION);cds.MergeChangeLog;//合并所有改变的数据end else MessageBox(self.handle,pchar('保存出错:'+err),'错误',MB_OK+MB_ICONERROR);end;到此,这篇文章也讲完了.用这个方法,那些单表的基础数据更新还可以写成一个祖先类,只要加一个取得更新表名的虚方法,比如:function TableName:string;virtual;然后其后代只要override这个方法,返回各自的表名,其他的一句代码都不用写.分类: 三层。

三层架构简易实例详解

三层架构简易实例详解

三层架构简易实例详解何为三层架构?通常意义上的三层架构就是将整个业务应用划分为:表现层(UI)、业务逻辑层(BLL)、数据访问层(DAL)。

区分层次的目的即为了“高内聚,低耦合”的思想。

1.表现层(UI):即展现给用户的界面;2.业务逻辑层(BLL):针对具体问题的操作,也可以说是对数据层的操作,对数据业务逻辑处理;3.数据访问层(DAL):该层所做事务直接操作数据库,针对数据的增添、删除、修改、查找等。

下面通过通过一个简单的例子来描述三层架构:需求1.实现一个客户信息管理界面(包括增加、修改、删除)操作;2.用户sql—server作为数据库以下是成型界面,至于UI设计是否合理,望各位大神拍砖UI层设计设计器代码:<Grid><DockPanel><ToolBar DockPanel.Dock="Top" Height="30"><Button Name="BtnAdd" Click="BtnAdd_Click" ToolTip="新增"><Image Source="Image\add.ico"></Image></Button><Button Name="BtnEdit" Click="BtnEdit_Click" ToolTip="编辑"><Image Source="Image\edit.ico"></Image></Button><Button Name="BtnDel" Click="BtnDel_Click" ToolTip="删除"><Image Source="Image\delete.ico"></Image></Button></ToolBar><DataGrid Name="DataGrid1" DockPanel.Dock="Top" IsReadOnly="True" AutoGenerateColumns="False"><DataGrid.Columns><DataGridTextColumn Header="姓名" Binding="{Binding Name}"></DataGridTextColumn><DataGridTextColumn Header="生日" Binding="{Binding BirthDay}"></DataGridTextColumn><DataGridTextColumn Header="电话" Binding="{Binding TelNum}"></DataGridTextColumn><DataGridTextColumn Header="地址" Binding="{Binding Address}"></DataGridTextColumn><DataGridTextColumn Header="等级" Binding="{BindingCustlevel}"></DataGridTextColumn></DataGrid.Columns></DataGrid></DockPanel></Grid>主要是通过设计器后台代码,DataGrid控件绑定数据,代码如下:/// <summary>/// 初始化加载数据/// </summary>private void LoadData(){DataGrid1.ItemsSource = CustomDAL.GetAll();}数据业务逻辑代码如下:/// <summary>/// 新增/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void BtnAdd_Click(object sender, RoutedEventArgs e){CustomEditUI editUI = new CustomEditUI();editUI.isInsert = true;if (editUI.ShowDialog() == true){LoadData();}}/// <summary>/// 编辑/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void BtnEdit_Click(object sender, RoutedEventArgs e){Custom customer = (Custom)DataGrid1.SelectedItem;if (customer == null){MessageBox.Show("请选择要编辑的行!");return;}CustomEditUI editUI = new CustomEditUI();//添加标识便于区分是编辑保存还是新增保存editUI.isInsert = false;editUI.editId = customer.Id;if (editUI.ShowDialog() == true){LoadData();}}/// <summary>/// 删除/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void BtnDel_Click(object sender, RoutedEventArgs e){if (MessageBox.Show("确认删除此条数据?", "提醒", MessageBoxButton.YesNo) == MessageBoxResult.Yes){Custom custom = DataGrid1.SelectedItem as Custom;if (custom != null){if ((MessageBox.Show("确认要删除此调数据", "提醒") == MessageBoxResult.OK)){CustomDAL.Delete(custom.Id);LoadData();}}else{MessageBox.Show("请选择要删除的数据", "提醒");return;}}}数据访问层(DAL),代码如下:public class CustomDAL{/// <summary>/// 查询数据总条数/// </summary>/// <returns></returns>public static int GetCount(){return (int)SqlHelper.ExecuteScalar("select * from T_Custom");}/// <summary>///根据ID查询数据/// </summary>/// <param name="id"></param>/// <returns></returns>public static Custom GetByid(long id){DataTable table = SqlHelper.ExecuteDataTable("select * from T_custom where id=@id", new SqlParameter("@id", id));return ToCustom(table.Rows[0]); ;}/// <summary>/// 根据id删除数据/// </summary>/// <param name="id"></param>public static void Delete(long id){SqlHelper.ExecuteNonQuery("delete from T_custom where id=@id", new SqlParameter("@id", id));}/// <summary>/// 插入空值处理/// </summary>/// <param name="value"></param>/// <returns></returns>public static object FromDBNull(object value){if (value == null){return DBNull.Value;}else{return value;}}/// <summary>/// 查询空值处理/// </summary>/// <param name="value"></param>/// <returns></returns>public static object ToDBNull(object value){if (DBNull.Value == null){return null;}else{return value;}}/// <summary>/// 新增数据/// </summary>/// <param name="custom"></param>public static void InSert(Custom custom){SqlHelper.ExecuteNonQuery(@"insert into T_Custom(Name,Birthday,Address,Telnum,Custlevel)values(@Name,@Birthday,@Address,@Teln um,@Custlevel)",new SqlParameter("@Name", ),new SqlParameter("@Birthday", FromDBNull(custom.BirthDay)),new SqlParameter("@Address", custom.Address),new SqlParameter("@Telnum", custom.TelNum),new SqlParameter("@Custlevel", custom.Custlevel));}/// <summary>/// 更新数据/// </summary>/// <param name="custom"></param>public static void UpDate(Custom custom){SqlHelper.ExecuteNonQuery(@"update T_Custom set Name=@Name,Birthday=@Birthday,Address=@Address,Telnum=@Telnum,Custlevel=@Custlevel where id=@id",new SqlParameter("@Name", ),new SqlParameter("@Birthday", FromDBNull(custom.BirthDay)),new SqlParameter("@Address", custom.Address),new SqlParameter("@TelNum", custom.TelNum),new SqlParameter("@Custlevel", custom.Custlevel),new SqlParameter("@Id", custom.Id));}/// <summary>/// 查询所有数据/// </summary>/// <returns></returns>public static List<Custom> GetAll(){DataTable table = SqlHelper.ExecuteDataTable("select * from T_custom");List<Custom> lst = new List<Custom>();for (int i = 0; i < table.Rows.Count; i++){lst.Add(ToCustom(table.Rows[i]));}return lst;}/// <summary>/// 影射关系赋值/// </summary>/// <param name="row"></param>/// <returns></returns>public static Custom ToCustom(DataRow row){Custom custom = new Custom(); = row["Name"].ToString();custom.Address = row["Address"].ToString();custom.BirthDay = (DateTime?)ToDBNull(row["BirthDay"]);custom.TelNum = row["TelNum"].ToString();custom.Custlevel = (int)row["CUstlevel"];custom.Id = (long)row["id"];return custom;}}public class SqlHelper{private static string connstr = ConfigurationManager.ConnectionStrings["conn"].ConnectionString;/// <summary>/// 查询数据/// </summary>/// <param name="sql"></param>/// <param name="parameters"></param>/// <returns></returns>public static Object ExecuteScalar(string sql, params SqlParameter[] parameters){using (SqlConnection cnn = new SqlConnection(connstr)){cnn.Open();using (SqlCommand cmd = cnn.CreateCommand()){mandText = sql;cmd.Parameters.AddRange(parameters);return cmd.ExecuteScalar();}}}/// <summary>/// 增、删、改操作/// </summary>/// <param name="sql"></param>/// <param name="parameters"></param>public static void ExecuteNonQuery(string sql, params SqlParameter[] parameters){using (SqlConnection cnn = new SqlConnection(connstr)){cnn.Open();using (SqlCommand cmd = cnn.CreateCommand()){mandText = sql;cmd.Parameters.AddRange(parameters);cmd.ExecuteNonQuery();}}}/// <summary>/// 单个查询结果返回/// </summary>/// <param name="sql"></param>/// <param name="parameters"></param>/// <returns></returns>public static DataTable ExecuteDataTable(string sql, params SqlParameter[] parameters){using (SqlConnection cnn = new SqlConnection(connstr)){cnn.Open();using (SqlCommand cmd = cnn.CreateCommand()){mandText = sql;cmd.Parameters.AddRange(parameters);SqlDataAdapter apter = new SqlDataAdapter(cmd);DataSet dataset = new DataSet();apter.Fill(dataset);return dataset.Tables[0];}}}}业务模型层public class Custom{public long Id { set; get; }public string Name { set; get; }public DateTime? BirthDay { set; get; }public string Address { set; get; }public string TelNum { set; get; }public int Custlevel { set; get; }}业务模型层public class Custom{public long Id { set; get; }public string Name { set; get; }public DateTime? BirthDay { set; get; }public string Address { set; get; }public string TelNum { set; get; }public int Custlevel { set; get; } }上述用思想,图形表示如下:。

三层架构简易实例详解

三层架构简易实例详解

三层架构简易实例详解三层架构是一种软件设计模式,它将软件系统分为三个层次:表现层、业务逻辑层和数据访问层。

每个层次都有特定的职责,通过分层的方式提高了系统的可维护性、可扩展性和可复用性。

以下是一个简单的示例来解释三层架构的概念:1. 表现层(Presentation Layer):这是用户与系统交互的界面。

它负责接收用户的输入、展示数据和呈现界面效果。

可以使用 Web 页面、桌面应用程序或移动应用程序等来实现。

2. 业务逻辑层(Business Logic Layer):该层处理系统的核心业务逻辑。

它接收来自表现层的请求,执行相应的业务规则和计算,并与数据访问层进行交互以获取和保存数据。

3. 数据访问层(Data Access Layer):这一层负责与数据库或其他数据源进行交互。

它封装了数据的读取、写入、修改和查询操作,提供了一个统一的数据访问接口。

以下是一个简单的示例,以在线书店为例:1. 表现层:用户通过网站或移动应用程序浏览图书列表、查看图书详细信息、添加到购物车和进行结算。

2. 业务逻辑层:处理用户的请求,例如检查购物车中的图书数量、计算价格、应用折扣等。

它还负责与数据访问层交互以获取图书信息和保存用户的订单。

3. 数据访问层:与数据库进行交互,执行图书的查询、插入、更新和删除操作。

通过将系统划分为三层,每层专注于特定的职责,可以提高代码的可维护性和可复用性。

当需求发生变化或需要进行系统扩展时,只需修改相应层次的代码,而不会影响其他层次。

这种分层的架构也有助于团队协作和开发效率。

请注意,这只是一个简单的示例,实际的三层架构应用可能会更加复杂,并涉及更多的模块和技术。

具体的实现方式会根据项目的需求和规模而有所不同。

三层架构实例

三层架构实例

三层架构实例4、三层架构下的包图:注:Entity中放的都是实体类,即由数据库中的表抽象出来的类。

实体类主要作为数据的载体,在各个层之间被传递。

我们现在要做的就是对⽤例图中所⽰的这个功能进⾏抽象,即分别在UI,BLL,DAL三层抽象类。

数据访问层DAL:数据库中的⼀张表对应DAL层的⼀个类,所以这⾥要有⼀个dal_DealStudentInfo类,它必然有⼀个⽅法AddRecord,⽤于向数据库中添加数据。

这个⽅法的参数便是studentInfo这张表映射出的⼀个实体类的⼀个实例,⽅法中包含了⼀些SQL语句。

IsExist是验证数据有效性,这⾥主要是检验⽤户是否已经存在。

图⽰:关键代码如下:Public Class dal_DealStudentInfoPublic Function AddRecord(ByVal RecInfo As Student) As Boolean'……'连接数据库'……'……Try'SQL语句向数据库中写⼊数据'……Return TrueCatchReturn FalseFinally'关闭数据库End TryEnd FunctionPublic Function IsExist() As Boolean'查询数据库,如果存在该卡,则返回True,不存在则返回falseIf '存在Return TrueElseReturn FalseEnd IfEnd FunctionEnd Class业务逻辑层BLL:这⾥会有⼀个类bll_ControlStudent与DAL层中的dal_DealStudentInfo类对应,调⽤dal_DealStudentInfo的AddRecord⽅法,这个⽅法传递的参数就是UI层传递过来的studentInfo实体类的⼀个实例。

如果需要验证输⼊合法性,也放在bll_ControlStudent中,并与dal_DealStudentInfo中的⽅法相对应,可以单独⽤⼀个⽅法,也可以集成到AddStudent中,我这⾥因为只是进⾏了简单的判断,就把它放到了AddStudent。

Delphi三层主从表设置

Delphi三层主从表设置

Delphi三层主从表设置三层中主从表的操作(删除、新增、修改)一定要在一个事物中完成,那在Delphi中的事物又如何控制呢?我们在开发客户端时,如果为了在一个事物中而用TSqlConnection 的事物来控制是徒劳的,没有一点意义,因为真正事物控制是在服务端的TDataSetProvider中产生的,它是自动产生事物的,如果有错误产生,它会回滚事物。

当客户端的ClientdataSet把Delta数据提交给远程的DataSetProvider时,这个DataSetProvider会解析这个语句,并且会自动产生一个事物,所以我们不必要写事物控制,当然用函数时而不用DataSetProvider是另外一个话题。

如果我们直接在远程端设置二个TDataSetProvider,客户端也放两个对就的ClientDataSet,并把客户端的两个数据集设置成主从可以吗?我的回答是:绝对不可以。

为什么?因为你在更新数据时是在两个DataSetProvider中产生二个不同的事物,这样的话就不能保证数据更新的完整性。

Delphi为我们提供的机制是在服务端设置好主从结构,而从表数据集变为主表的一个字段,这样当客户端连接远程的TDataSetProvider时,只把客户端主表数据控件连接到DataSetProvider就可以,从表只要设置一下DataSetField为客户端主表中那个数据集字段就可以了。

当保存数据时,只用客户端主表的ApplyUpdata就可以保存主从表数据(可以有多个从表)。

更新时就把主从表相关改动的数据传到远程的DataSetProvider中,些时DataSetProvider可以开启一个事物,这样就能保证数据的完整性。

以下我详细说一下开发主从表的实例:我用的数据集是:ADOQuery,(用SQL Server 2008 r2的hisdb 库中的orders和order detail表做实例)一.开发服务端1. 放上连接数据库的控件:ADOConnection。

DELPHI三层架构设计方案

DELPHI三层架构设计方案

随便说说最近项目中的三层架构吧。

讲点实际的东西。

我最讨厌空讲道理。

网上讲道理的太多了,不喜欢举例子。

大多数文章中都或多或少的讲到了三层架构。

表示层,业务层,数据层。

又把业务层再细分,分为外观服务层,主业务服务,及数据库库服务层。

今天主要讨论一下业务层吧。

举个最简单的例子。

客户端获取数据。

业务层要与表示层尽量解藕,我的方法是:首先我们在中间层TLB_中定义一个接口IBusinessService, 定义一个方法。

getvoList,我要得到一个VO的列表,VO即V alueObject, 例如:TV alueObject= class(TPersistent)privateb_insertFlag :Boolean;b_updateFlag :Boolean;b_deleteFlag :Boolean;d_rowV ersion :double;procedure setInsertFlag(pInsertFlag :Boolean);function getInsertFlag: Boolean;procedure setUpdateFlag(pUpdateFlag :Boolean);function getUpdateFlag: Boolean;procedure setDeleteFlag(pDeleteFlag :Boolean);function getDeleteFlag: Boolean;procedure setRowV ersion(pRowV ersion :double);function getRowV ersion:double;protectedfunction GetOLEData: OleV ariant; virtual;procedure SetOLEData(const V alue: OleV ariant); virtual;publishedproperty bInsertFlag: Boolean read getInsertFlag write setInsertFlag;property bUpdateFlag: Boolean read getUpdateFlag write setUpdateFlag;property bDeleteFlag: Boolean read getDeleteFlag write setDeleteFlag;property dRowV ersion: double read getRowV ersion write setRowV ersion;property POLEData:OleV ariant read GetOLEData write SetOLEData;end;TUserVO = class(TV alueObject)privateid: string;name: string;password: string;。

三层架构实例

三层架构实例

这里以查询数据库中student表的所有信息为例:1、模型层,Student.cs文件:using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace SchoolModels{public class Student{public Student(){ }public Student(int id, string name, string pwd, int age, string sex) {this.StudentId = id;this.StudentName = name;this.StudentPwd = pwd;}private int studentId;public int StudentId{get { return studentId; }set { studentId = value; }}private string studentName;public string StudentName{get { return studentName; }set { studentName = value; }}private string studentPwd;public string StudentPwd{get { return studentPwd; }set { studentPwd = value; }}}}2、数据访问层,StudentService.cs文件:using System.Text;using System.Data;using System.Data.SqlClient;using SchoolModels;namespace SchoolDal{public class StudentService{public static IList<Student> GetAllStudents(){List<Student> stus = new List<Student>();string sql = "select * from student";SqlCommand cmd = new SqlCommand(sql, DBHelper.con);DBHelper.con.Open();SqlDataReader reader = cmd.ExecuteReader();while (reader.Read()){Student student = new Student();student.StudentId = (int)reader["StudentId"];student.StudentName = reader["StudentName"].ToString();student.StudentPwd = reader["StudentPwd"].ToString();stus.Add(student);}reader.Close();DBHelper.con.Close();return stus;}}其中,里面有一个DBHelper.cs文件:using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Configuration;using SchoolModels;using System.Data;using System.Data.SqlClient;namespace SchoolDal{public class DBHelper{public static string conStr = ConfigurationManager.ConnectionStrings["conDb"].ToString();public static SqlConnection con = new SqlConnection(conStr); }}3、业务逻辑层,StudentManager.cs文件:using System;using System.Collections.Generic;using System.Linq;using System.Text;using SchoolDal;using SchoolModels;namespace SchoolBll{public class StudentManager{public static IList<Student> GetAllStudents(){return StudentService.GetAllStudents();}}}最后,假设在Form1里面把所有学生的信息显示到DataGridView中,Form1.cs 文件:using System;using System.Collections.Generic;using ponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms;using SchoolBll;using SchoolModels;namespace School{public partial class Form1 : Form{public Form1(){InitializeComponent();}private void Form1_Load(object sender, EventArgs e){IList<Student> stus = StudentManager.GetAllStudents();this.dgvStudent.DataSource = stus;}}以上就是通过三层架构做的一个查询数据库某一张表所有信息的小例子,你可以根据具体的需求进行修改。

Delphi的多层数据库应用体系结构

Delphi的多层数据库应用体系结构

Delphi的多层数据库应用体系结构Delphi提出的MIDAS是把原来双层的数据连接访问放到了应用服务器上,客户端只剩下了执行文件和MIDAS.DLL,前台和后台服务器通过DCOM机制互相沟通。

其结构如图所示。

图MIDAS应用程序的结构1. 应用服务器的结构应用服务器的关键部件是TRemoteDataModule(远程数据模块),它提供了IAppServer 接口。

当客户程序与应用服务器建立了连接,就通过IAppServer接口来获得数据集提供者列表(list of providers)。

TRemoteDataModule组件是一个支持双重接口的自动化服务器,这种类型的远程数据模块适合于DCOM, HTTP, Sockets通讯方式。

远程数据模块可以作为容器,但只能放置非可视化的组件。

另外,远程数据模块上一般要放一个或几个TDataSetProvider或TProvider构件来提供IProvider接口。

TDataSetProvider组件存在于应用服务器端的远程数据模块中,用于连接数据模块中的数据集,并向客户端发送数据。

2. 客户端的结构对于最终用户来说,多层体系结构中的客户程序与两层体系结构中的应用程序没有什么区别,在结构上,客户程序仍然通过标准的数据控件与用户交互。

但与单机模式应用程序不同的是,多层体系结构中的客户程序是通过应用服务器提供的IAppServer接口获得数据的,也通过IAppServer接口申请更新数据。

在客户程序中,MIDAS连接组件TDispatchConnection扮演着极其重要的角色。

不同的MIDAS连接组件使用不同的通讯协议将客户端连接到一个远程应用服务器,以获取一个IAppServer接口服务,然后获取数据集提供者列表。

通常使用TDispatchConnection派生的组件:(1)TDCOMConnection组件使用DCOM将客户端连接到一个远程应用服务器。

delphi 三层架构CS简单例子一步步做

delphi 三层架构CS简单例子一步步做

delphi 三层CS架构一步步建立的简单例子所谓三层:(1) 客户端(2) 服务器端(3) 数据库在数据访问时,使得客户端必须通过服务器来访问数据库。

提高了系统的安全性。

在Delphi中可以使用Socket或者Dcom组件来连接他们相互间的通讯。

本例使用SOCKET组件. DCOM用在局域网, SOCKET用在互联网.环境为windows xp+ delphi 7 + access2000创建过程:1、请用ACCESS2000新建一个数据库database02.mdb.2、请在delphi7执行file-new- application, 新建一个桌面,上面放一个label, label.caption =’服务器端已运行….’3、接下来在delphi7执行file --new--other,选择"Multitier"--"Remote datamodule"。

在跳出来的对话框里面输入名称(任意),例如:AppSqlConn。

选择确定,进入remote data module窗口。

界面如下图所示4、加入组件:adodataset,点击connectionstring属性,点击后面的…,进入设定连接窗口。

在选择数据库中选择自己想要使用的数据库database02.mdb。

一般只要地址正确、用户名和密码无误,肯定可以连接通过。

确定。

3、ADODATASET1在commandtext中点击后面的…,啥也不用设定,默认配置;4、ADODATASET1将active属性设置为false。

5、再加入组件:datasetprovider。

设定其dataset属性为上面的adodataset1。

5.1 接着设置datasetprovider1的属性,poallowcommandtext=true,其他的参数默认即可.datasetprovider1的属性如下图所示6、到此服务器端已经设置完成。

Delphi三层框架开发服务端开发

Delphi三层框架开发服务端开发

Delphi三层框架开发服务端开发采⽤Delphi7+SQL2008⼀、创建数据库和表[sql]1. CREATE TABLE [dbo].[tb_Department](2. [FKey] [uniqueidentifier] NOT NULL,3. [FName] [varchar](50) NULL,4. [FAge] [varchar](50) NULL,5. [FSex] [varchar](50) NULL,6. [FMobile] [varchar](50) NULL,7. [FRemark] [varchar](200) NULL8. ) ON [PRIMARY]⼆、写服务端2.1 先创建⼀个application在窗体中添加Label如图显⽰[delphi]1. unit ufrmMain;2.3. interface4.5. uses6. Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,7. Dialogs, StdCtrls;8.9. type10. TfrmMain = class(TForm)11. lbl1: TLabel;12. private13. { Private declarations }14. public15. { Public declarations }16. end;17.18. var19. frmMain: TfrmMain;20.21. implementation22.23. {$R *.dfm}24.25. end.2.2 File-New-Other点击OK 在弹出的对话框中填写名字⾃⼰根据需要填写此时⽣成2个单元⼀个Project1_TLB 和 Unit2 单元打开Project1_TLB 单元按F12键在弹出的对话框中Name就是我们要的⽅法名称(根据⾃⼰需要填写)GetData 获取数据新增参数如下图再按相同的⽅法添加PostData⽅法(保存数据)最终结果如下图添加后的最代码终结果[delphi]1. unit Project1_TLB;2.3. // ************************************************************************ //4. // WARNING5. // -------6. // The types declared in this file were generated from data read from a7. // Type Library. If this type library is explicitly or indirectly (via8. // another type library referring to this type library) re-imported, or the9. // 'Refresh' command of the Type Library Editor activated while editing the10. // Type Library, the contents of this file will be regenerated and all11. // manual modifications will be lost.12. // ************************************************************************ //13.14. // PASTLWTR : 1.215. // File generated on 2014-10-24 14:24:49 from Type Library described below.16.17. // ************************************************************************ //18. // Type Lib: D:\Delphi7\Projects\Project1.tlb (1)19. // LIBID: {C6713A20-F49B-4B06-8869-9E040C912074}20. // LCID: 021. // Helpfile:22. // HelpString: Project1 Library23. // DepndLst:24. // (1) v2.0 stdole, (C:\Windows\SysWOW64\stdole2.tlb)25. // (2) v1.0 Midas, (C:\Windows\SysWOW64\midas.dll)26. // (3) v4.0 StdVCL, (C:\Windows\SysWOW64\stdvcl40.dll)27. // ************************************************************************ //28. {$TYPEDADDRESS OFF} // Unit must be compiled without type-checked pointers.29. {$WARN SYMBOL_PLATFORM OFF}30. {$WRITEABLECONST ON}31. {$VARPROPSETTER ON}32. interface33.34. uses Windows, ActiveX, Classes, Graphics, Midas, StdVCL, Variants;35.36.37. // *********************************************************************//38. // GUIDS declared in the TypeLibrary. Following prefixes are used:40. // CoClasses : CLASS_xxxx41. // DISPInterfaces : DIID_xxxx42. // Non-DISP interfaces: IID_xxxx43. // *********************************************************************//44. const45. // TypeLibrary Major and minor versions46. Project1MajorVersion = 1;47. Project1MinorVersion = 0;48.49. LIBID_Project1: TGUID = '{C6713A20-F49B-4B06-8869-9E040C912074}';50.51. IID_ITestService: TGUID = '{C59D7F3C-4AE7-473B-81B8-8EE1C73BB2B1}';52. CLASS_TestService: TGUID = '{82AEC5B8-E53F-4725-A24D-456FD570E355}';53. type54.55. // *********************************************************************//56. // Forward declaration of types defined in TypeLibrary57. // *********************************************************************//58. ITestService = interface;59. ITestServiceDisp = dispinterface;60.61. // *********************************************************************//62. // Declaration of CoClasses defined in Type Library63. // (NOTE: Here we map each CoClass to its Default Interface)64. // *********************************************************************//65. TestService = ITestService;66.67.68. // *********************************************************************//69. // Interface: ITestService70. // Flags: (4416) Dual OleAutomation Dispatchable71. // GUID: {C59D7F3C-4AE7-473B-81B8-8EE1C73BB2B1}72. // *********************************************************************//73. ITestService = interface(IAppServer)74. ['{C59D7F3C-4AE7-473B-81B8-8EE1C73BB2B1}']75. procedure GetData(const Table: WideString; const Where: WideString; var Ret: OleVariant); safecall;76. procedure PostData(const Table: WideString; Value: OleVariant; var Ret: OleVariant); safecall;77. end;78.79. // *********************************************************************//80. // DispIntf: ITestServiceDisp81. // Flags: (4416) Dual OleAutomation Dispatchable82. // GUID: {C59D7F3C-4AE7-473B-81B8-8EE1C73BB2B1}83. // *********************************************************************//84. ITestServiceDisp = dispinterface85. ['{C59D7F3C-4AE7-473B-81B8-8EE1C73BB2B1}']86. procedure GetData(const Table: WideString; const Where: WideString; var Ret: OleVariant); dispid 301;87. procedure PostData(const Table: WideString; Value: OleVariant; var Ret: OleVariant); dispid 302;88. function AS_ApplyUpdates(const ProviderName: WideString; Delta: OleVariant; MaxErrors: Integer;89. out ErrorCount: Integer; var OwnerData: OleVariant): OleVariant; dispid 20000000;90. function AS_GetRecords(const ProviderName: WideString; Count: Integer; out RecsOut: Integer;91. Options: Integer; const CommandText: WideString; var Params: OleVariant;92. var OwnerData: OleVariant): OleVariant; dispid 20000001;93. function AS_DataRequest(const ProviderName: WideString; Data: OleVariant): OleVariant; dispid 20000002;94. function AS_GetProviderNames: OleVariant; dispid 20000003;95. function AS_GetParams(const ProviderName: WideString; var OwnerData: OleVariant): OleVariant; dispid 20000004;96. function AS_RowRequest(const ProviderName: WideString; Row: OleVariant; RequestType: Integer;97. var OwnerData: OleVariant): OleVariant; dispid 20000005;98. procedure AS_Execute(const ProviderName: WideString; const CommandText: WideString;99. var Params: OleVariant; var OwnerData: OleVariant); dispid 20000006;100. end;101.102. // *********************************************************************//103. // The Class CoTestService provides a Create and CreateRemote method to104. // create instances of the default interface ITestService exposed by105. // the CoClass TestService. The functions are intended to be used by106. // clients wishing to automate the CoClass objects exposed by the108. // *********************************************************************//109. CoTestService = class110. class function Create: ITestService;111. class function CreateRemote(const MachineName: string): ITestService;112. end;113.114. implementation115.116. uses ComObj;117.118. class function CoTestService.Create: ITestService;119. begin120. Result := CreateComObject(CLASS_TestService) as ITestService;121. end;122.123. class function CoTestService.CreateRemote(const MachineName: string): ITestService; 124. begin125. Result := CreateRemoteComObject(MachineName, CLASS_TestService) as ITestService; 126. end;127.128. end.Unit2单元成功添加以下前⾯新增了2个接⼝⽅法然后我们在这个单元⾥⾯实现⽅便客户端调⽤代码如下[delphi]1. unit Unit2;2.3. {$WARN SYMBOL_PLATFORM OFF}4.5. interface6.7. uses8. Windows, Messages, SysUtils, Classes, ComServ, ComObj, VCLCom, DataBkr,9. DBClient, Project1_TLB, StdVcl, ADODB, Provider, DB;10.11. type12. TTestService = class(TRemoteDataModule, ITestService)13. conData: TADOConnection;14. dsTemp: TClientDataSet;15. dspTemp: TDataSetProvider;16. qryTemp: TADOQuery;17. procedure RemoteDataModuleCreate(Sender: TObject);18. private19. I: Integer;20. Params: OleVariant;21. OwnerData: OleVariant;22. // ⾃⼰加⼊23. function InnerGetData(strSQL: String): OleVariant;24. function InnerPostData(Delta: OleVariant): Integer;25. protected26. class procedure UpdateRegistry(Register: Boolean; const ClassID, ProgID: string); override;27. procedure GetData(const Table, Where: WideString; var Ret: OleVariant);28. safecall;30. var Ret: OleVariant); safecall;31.32. public33. { Public declarations }34. end;35.36. implementation37.38. {$R *.DFM}39.40. procedure TTestService.GetData(const Table, Where: WideString;41. var Ret: OleVariant);42. const SQL = 'select * from %s where %s';43. begin44. Ret := Self.InnerGetData(Format(SQL, [Table, Where]));45. end;46.47.48. function TTestService.InnerGetData(strSQL: String): OleVariant;49. begin50. // 必须是CLOSE状态, 否则报错.51. if qryTemp.Active then qryTemp.Active := False;52. Result := Self.AS_GetRecords('dspTemp', -1, I, ResetOption+MetaDataOption,53. strSQL, Params, OwnerData);54. end;55.56. function TTestService.InnerPostData(Delta: OleVariant): Integer;57. begin58. Self.AS_ApplyUpdates('dspTemp', Delta, 0, Result, OwnerData);59. end;60.61. procedure TTestService.PostData(const Table: WideString; Value: OleVariant;62. var Ret: OleVariant);63. var64. KeyField: TField;65. begin66. dsTemp.Data := Value;67. if dsTemp.IsEmpty then Exit;68. {69. 这⾥假设每个表都有⼀个FKey字段, 并且值是唯⼀的.70. 也可以根据表中, 改成相应的主键字段名.71. }72. KeyField := dsTemp.FindField('FKey');73. if KeyField=nil then raise Exception.Create(' 键值字段未发现.');74. if KeyField.IsNull then75. begin76. qryTemp.SQL.Text := 'select * from '+Table+' where 1>2';77. end78. else79. begin80. qryTemp.SQL.Text := 'select * from '+Table+' where FKey='+QuotedStr(KeyField.AsString);81. qryTemp.Open;82. with qryTemp.FieldByName('FKey') do ProviderFlags := ProviderFlags + [pfInKey];83. dspTemp.UpdateMode := upWhereKeyOnly;84. end;85. qryTemp.Open;86. Ret := InnerPostData(Value);87. end;88.89. class procedure TTestService.UpdateRegistry(Register: Boolean; const ClassID, ProgID: string);90. begin91. if Register then92. begin93. inherited UpdateRegistry(Register, ClassID, ProgID);94. EnableSocketTransport(ClassID);95. EnableWebTransport(ClassID);96. end else98. DisableSocketTransport(ClassID);99. DisableWebTransport(ClassID);100. inherited UpdateRegistry(Register, ClassID, ProgID);101. end;102. end;103.104.105.106. procedure TTestService.RemoteDataModuleCreate(Sender: TObject);107. begin108. Self.qryTemp.Connection := Self.conData;109. Self.dspTemp.DataSet := Self.qryTemp;110. Self.dspTemp.Options := Self.dspTemp.Options + [poAllowCommandText];111. conData.ConnectionString:='File Name='+ExtractFilePath(ParamStr(0))+'conData.udl';112. try113. Self.conData.Open;114. except115. on e:Exception do116. begin117.118. end;119. end;120. end;121.122. initialization123. TComponentFactory.Create(ComServer, TTestService,124. Class_TestService, ciMultiInstance, tmApartment);125. end.再讲讲conData.udl ⽂件的创建新建⼀个txt⽂件添加内容[oledb]; Everything after this line is an OLE DB initstringProvider=SQLOLEDB.1;Password=test;Persist Security Info=True;User ID=sa;Initial Catalog=db_test;Data Source=192.168.0.1保存修改扩展名为.udl 就可以了。

Delphi XE7开发入门教程之DataSnap三层架构篇-第一部分预览

Delphi XE7开发入门教程之DataSnap三层架构篇-第一部分预览

右键点击 MySQLConnection,然后 Modify:
假定我们的测试数据库为 delphi_test:
填写好参数之后点击 Test Connection 看看一切是否正常:
(5) 如果要配置其它类型的数据库驱动,过程是类似的,比如采取 FireDAC 的话则应该展开 Data Explorer 下 的 FireDAC,然后选择数据库服务器类型,配置和 DBExpress 并无本质差异:
Define a DataSet on the DataSnap Server
1. 切换至 ServerContainerUnit 单元的代码窗口,在 implementation 处更正代码为: uses Winapi.Windows, ServerModuleUnit; 2. 切换至 ServerModuleUnit 单元的设计窗口,将 Server Module 的 Name 属性改为 DSServerModule_MyTest 3. 为 Server Module 添加以下控件并做好相应设置: o 放置一个 TSQLConnection 控件并设置属性如下: 将 Name 设置为 SQLConnection_MyTest 将 LoginPrompt 设置为 False 将 ConnectionName* 设置为 MySQLConnection 注意: 请确保 MySQL Server 在此之前已经运行正常。
Run the DataSnap Server
现在试着运行一下你的程序吧,注意 Windows 给出的防火墙警告,请允许程序访问网络即可。
OK,服务正常跑起来了,不过看起来它只是一个空空的窗体似乎啥也没有。其实中间层服务它已经完成了。
可是这样的中间层服务器只能静态的将 SQL 语句产生的数据结果提供给客户端,完全不具备灵活性。让我们进入重 点吧,修正一下 ServerModuleUnit 单元的代码: 1. 找到以下定义:

基于Delphi的三层数据库系统的实现方法

基于Delphi的三层数据库系统的实现方法

1引言当前的数据库应用系统中,按其结构划分为两类,一类是两层结构的数据库应系统,另一类是多层结构的数据库应用系统。

两层结构的数据库应用系统包括客户/服务器(C/S)模式和浏览器/服务器(B/S)模式,它的特点是数据和程序分离,数据库服务器端管理数据,客户机或浏览器显示、访问和更新数据,因此可以分别进行开发和维护,同时也降低了数据库服务器的工作量,减小了网络的流量,具有较强的安全性,但当客户端程序或浏览器较多时,服务器的性能会明显下降,而且应用逻辑安装在每个客户机上,如果系统要升级,就只能对客户机逐一进行,如果客户机分布在不同的地点,维护和升级任务将非常繁重。

为了解决两层结构应用系统的不足,人们提出了多层数据库应用系统的结构模式,具有代表性的是三层结构。

2三层系统的体系结构所谓三层结构,是在客户端与数据库之间加入了一个“中间层”,也叫组件层。

在这种体系结构中,客户机只存放表示层软件,后台是数据库,业务逻辑包括事务处理、监控、信息排队、Web服务等采用专门的中间件服务器,大大减少了与数据库服务器连接的次数。

通常情况下客户端通过COM/DCOM通讯与中间层建立连接,再经由中间层与数据库进行交互,客户端与数据库隔离,提高了整个系统的可靠性。

在三层系统体系中,系统资源被统一管理和使用,用户可以通过网格门户(portal)透明地使用整个网络资源。

它的体系结构如图1所示。

三层结构功能如下:表示层:提供简洁的人机交互界面,负责数据显示和客户端的数据处理,由客户机实现;功能层:实现业务逻辑,即实现客户与数据库对话,同时,在这一层中,还应实现分布式管理、负载均衡、安全隔离等;它是一个远程模块,由应用服务器来实现;数据层:负责数据管理,提供数据的存储服务,由数据库服务器来实现。

三层结构的主要特点:(1)安全性。

中间层隔离了客户直接对数据服务器的访问,保护了数据库的安全。

(2)稳定性。

三层分布式体系提供了更可靠的稳定性:中间层缓冲Client与数据库的实际连接,使数据库的实际连接数量远小于Client应用数量。

Delphi在三层C_S结构中的应用.

Delphi在三层C_S结构中的应用.

引言Delphi 作为前端开发工具,它结合了计算机软件开发两个体系的优点,结合了传统的编程语言Object Pascal 和数据库语言的强大功能,既可以用于传统的算术编程和进行Windows 程序开发,又可以用于数据库编程。

Delphi 采用窗体和面向对象的方法,提供高性能快速的编译器和集成开发环境。

Delphi 通过BDE 、ADO 、dbExpress 等数据驱动和访问机制,可以访问多种数据格式以及开发灵活的客户/服务器体系的数据库应用系统[1 ̄2]。

1Delphi 三层C/S 的总体结构Delphi 支持面向对象的编程技术,提供数据库引擎BDE ,可以通过组件技术支持三层C/S 应用程序的开发。

其结构图如图1所示[3]:其中,客户应用程序提供友好的用户界面,中Delphi 在三层C/S 结构中的应用刘秀霞(肇庆市工业贸易学校,广东肇庆526060摘要:本文主要论述了Delphi 在三层客户/服务器体系结构中的应用,从数据库应用系统的总体结构、逻辑结构出发,讨论了Delphi 的应用原理,并具体介绍了ADO 组件和Socket 组件及其在Delphi 中的建立方法。

关键词:Delphi ;ADO 组件;Socket 组件中图分类号:TP311文献标识码:A文章编号:1671-3699(200704-0048-03Application of Delphi in the Three-Tiers-C/SLIU Xiu-xia(Zhaoqing Industry &Trade Vocational Shool ,Zhaoqing 526060,ChinaAbstract :Application of Delphi in the three-tiers-C/S is studied in the thesis.Based on analysis of the main and the logical construction about the system,it also discusses its basic principles,and introduces ADO and Socket components,as well as the way how it build the components in the Delphi.Key words:delphi ;ADO ;socket图1Delphi 下实现三层C/S 结构原理图收稿日期:2007-04-09作者简介:刘秀霞(1970-,女,广西省南宁人,讲师,主要从事计算机教学工作研究。

三层架构简易实例详解 -回复

三层架构简易实例详解 -回复

三层架构简易实例详解-回复什么是三层架构?三层架构是一种常见的软件架构模式,将应用程序划分为三个主要的逻辑层:表示层(UI层)、业务逻辑层(BLL层)和数据访问层(DAL层)。

这种架构模式将不同的功能和职责进行了分离,使得应用程序更易于维护、拓展和重用。

表示层(UI层):表示层是用户与系统之间的接口,负责接收用户输入并向用户展示结果。

它通常包括用户界面、控制器和视图等。

用户界面负责与用户的交互,接收用户输入;控制器负责处理用户请求,将其传递给业务逻辑层;视图负责向用户展示处理结果。

业务逻辑层(BLL层):业务逻辑层是应用程序的核心,负责处理应用程序的业务逻辑。

它包含了应用程序的主要处理逻辑、算法和规则等。

业务逻辑层负责接收来自表示层的用户请求,进行处理并将结果返回给表示层。

数据访问层(DAL层):数据访问层是与数据存储和数据库交互的层。

它主要负责将业务逻辑层的请求转化为对数据库的操作,并将数据库返回的结果返回给业务逻辑层。

数据访问层的主要目的是将业务逻辑层与具体的数据存储实现进行解耦,使得业务逻辑层的实现与数据访问细节无关。

三层架构的优势:1. 模块化和可维护性:三层架构将应用程序拆分为不同的逻辑层,使得每个层次都具备清晰的功能和职责。

这种模块化的设计使得代码更易于维护和拓展。

2. 可重用性:由于不同的层次之间的耦合度较低,有助于提高代码的可重用性。

例如,业务逻辑层可以被多个不同的表示层共享,减少了重复编写代码的工作量。

3. 性能优化:三层架构可以根据实际需求进行负载均衡和性能优化。

例如,可以将数据库部署在单独的服务器上,以提高数据访问的效率。

4. 安全性:通过将业务逻辑与数据访问逻辑分离,可以更好地保护数据安全和业务逻辑的完整性。

5. 易于团队合作开发:每个层次的功能和职责被清晰划分,有助于团队合作开发。

不同的开发人员可以并行地开发不同的层次,减少了沟通和协作的压力。

三层架构的实例:假设我们要开发一个简单的学生管理系统,其中包括学生信息的录入、查询和删除等功能。

最新整理多层结构及其在Delphi中的实现.docx

最新整理多层结构及其在Delphi中的实现.docx

最新整理多层结构及其在Delphi中的实现多层结构及其在Delphi中的实现二层结构的退出在传统的二层C/S结构数据库应用中,客户端的机器执行应用程序,连接到后端的数据库服务器中存取应用系统所需资料,因为应用系统的企业逻辑都编写在客户端的应用程序中,造成客户端非常臃肿,且当应用系统需求改变时,所有在客户端的应用程序都必须改变,使维护成本太高。

OracleMSQL客户端1数据库服务器Sybase等图一2层C/S结构1.2多层结构的概念和特点为了解决这些问题,多层结构应用体系应运而生,即在传统的二层C/S模型中放入应用程序服务器。

应用程序服务器简单地说就是一个包含企业逻辑的应用程序,开发人员以一种特定的组件形态,如Microsoft /D ,CORBA对象,或EnterpriseJAVABean等,封装企业逻辑的程序代码,这种经过封装,能够执行特定企业功能的对象被称为“企业对象”,把这些企业对象分发到应用程序服务器中,开发人员在开发应用程序时就可以使用这些企业对象提供的服务。

多层结构的典型是三层结构,其基本思想是把用户界面与企业逻辑分离。

整体结构如图二所示。

客户端应用程序服务器远程数据库服务器(提供用户界面)(包含企业逻辑)(Oracle,MSQL,Sybase等DBMS)图二三层结构模型(1)客户端应用程序。

提供用户接口,主要功能是指导操作人员使用界面,输入数据,输出结果,并不具有企业逻辑,或只拥有部分不涉及企业核心的、机密的应用逻辑。

这样客户端就显的很廋,称为“廋”客户。

(2)应用程序服务器。

它是应用的主体,包含了企业中核心的及易变的企业逻辑(规划,运作方法,管理模型等),其功能即接受输入,处理后返回结果。

(3)远程数据库服务器。

即数据库管理系统(DBMS),负责管理对数据的读写和维护。

在更复杂的多层体系结构中,“廋”客户与远程数据库服务器之间可以加入更多的中间服务器,如加入一个中间安全服务器或中间转换服务器,用于对不同平台数据进行处理。

Delphi之MIDAS三层完美解决方案

Delphi之MIDAS三层完美解决方案

思路:中间层与客户端通过三个关键的接口过程进行交互操作(GetData,SetData,GetspData)GetData:获取数据集。

客户端传递数据集名称给中间层,中间层根据请求的数据集名称从数据库的配置文件中获取相关信息,与客户的的条件集合一起给合成SQL语句SetData:提交数据集。

客户端传递修改后的数据集Delta与名称给中间层,中间层根据请求的数据集名称从数据库的配置文件中获取相关信息,然后解释Delta并执行相关规则进行数据更新GetRecStrs:获取下拉列表信息GetspData:执行存储过程,并返回结果集ExecProd:执行存储过程,返回提示信息优点:因为获取数据与更新数据过程的配置文件在存储在数据库中,那么更改与配置更为灵活,对SQL语句不再存在限据,对权限方面可进行更格的控制(达到录入记录控制)数据提交时使用自定义更新过程,无论从速度、控制、安全等方面来说,都不是一件坏事(能使用附加工具快速生成标准的存储过程与配置信息)维护简单,更新业务逻辑时仅需更新相应的存储过程中,无需更改中间层与客户端能应付多变的系统开发过程,即使系统的流程或逻辑发生重大变更修改也相当简单,尤其是在需求不是相当明确的时候(有几个系统在上线实施之前能做到需求明细呢?^_^)缺点:即使系统再简单,若仅存在一个窗体的话,也必须将基类架设完整,与书本上一般开发过程存在差异,新手需一周左右时候才能上手中间层代码://///***********************************************************////////单元文件U_RDM.pasunit U_RDM;{$WARN SYMBOL_PLATFORM OFF}interfaceusesWindows, Messages, SysUtils, Classes, ComServ, ComObj, VCLCom, DataBkr, DBClient, MRPManange_TLB, StdVcl, DB, ADODB, Provider, Variants, StrUtils;typeTHPMRP = class(TRemoteDataModule, IHPMRP)sp_Pub_Ref: TADOStoredProc;get_Q_RecStrs: TADOQuery;BesConnection: TADOConnection;TmpCDS: TClientDataSet;tmpdsp: TDataSetProvider;Q_tmp: TADOQuery;sp_get_apply: TADOStoredProc;sp_get_spNm: TADOStoredProc;dsp_get_spQuery: TDataSetProvider;sp_get_Data: TADOStoredProc;sp_exec: TADOStoredProc;procedure RemoteDataModuleCreate(Sender: TObject);private { Private declarations }app_ChkNull, app_ParameStr, app_ParamSet: WideString; //不为空约束,更新参数Procedure LoginServer;Function GetSQL(UserID, dstNm, Corp_No, Cust_No, swhExpr: WideString; ParamStr: OleVariant): WideString; //取数据语句Function CannotNull(FieldStr: String; DeltaDS:TCustomClientDataSet; UpdKind: String=''): String; //不为空校验Function UpdKindStr(var Kind: TUpdateKind): String; //数据更改的状态:Ins, Upd, DelFunction SetspParam(spName:String; DeltaDS:TCustomClientDataSet; //存储过程更新数据ParameStr,ParamSet:WideString; UpdKind: String=''): WideString;procedure PubBeforeUpdateRecord(Sender: TObject; //数据提交公用过程SourceDS: TDataSet; DeltaDS: TCustomClientDataSet;UpdateKind: TUpdateKind; var Applied: Boolean);Function SetspParameters(UserID, dstNm: String; ParamStr: OleVariant; run_sp_Nm: TADOStoredProc): Boolean;Function varTypeCntInt(varType: TDataType): Integer;protectedclass procedure UpdateRegistry(Register: Boolean; const ClassID, ProgID: string); override;procedure GetRecStrs(const UserID, Corp_No, TabName, ColName,ExprStr: WideString; out RstStrs: OleVariant); safecall;function GetData(const UserID, dstNm, Corp_No, Cust_No: WideString;ParamStr: OleVariant; const sExpr: WideString): OleVariant; safecall;function SetData(const UserID, dstNm, ParamStr: WideString;vData: OleVariant): OleVariant; safecall;function GetAuth(const UserID, dstNm, GrpTyp: WideString): OleVariant;safecall;function GetspData(const UserID, dstNm: WideString;ParamStr: OleVariant): OleVariant; safecall;procedure GetColStrs(const UserID, Corp_No, TabName, ColName,ExprStr: WideString; out RstStrs: OleVariant); safecall;function ExecProc(const UserID, Corp_No, dstNm: WideString;ParamStr: OleVariant): Shortint; safecall;public { Public declarations }end;implementationuses U_PublicFun, U_MRPServer;{$R *.DFM}class procedure THPMRP.UpdateRegistry(Register: Boolean; const ClassID, ProgID: string); beginif Register thenbegininherited UpdateRegistry(Register, ClassID, ProgID);EnableSocketTransport(ClassID);EnableWebTransport(ClassID);end elsebeginDisableSocketTransport(ClassID);DisableWebTransport(ClassID);inherited UpdateRegistry(Register, ClassID, ProgID);end;end;{ 创建数据模块数据库连接 BES96261 }Procedure THPMRP.LoginServer;beginBESConnection.Connected := False;BESConnection.ConnectionString := GetServerConnetionStr;BESConnection.Connected := True;end;procedure THPMRP.RemoteDataModuleCreate(Sender: TObject);var I: Integer;beginLoginServer;{ 使用自定义更新过程 }For I := 0 to ponentCount - 1 dobeginIf (Components[I] is TDataSetProvider) and (Components[I].Tag = 100) then(Components[I] as TDataSetProvider).BeforeUpdateRecord := PubBeforeUpdateRecord; end;end;{ 根据条件返回指定列字段数据 BES96261 }procedure THPMRP.GetRecStrs(const UserID, Corp_No, TabName, ColName,ExprStr: WideString; out RstStrs: OleVariant);var sSQL, sExpr: String;I: Integer;beginsExpr := ExprStr;If Trim(sExpr) <> '' then sExpr := ' where '+sExpr;sSQL := Format('select %s from %s'+sExpr,[ColName,TabName,ExprStr]);with get_Q_RecStrs dobeginClose;SQL.Clear;SQL.Add(StringReplace(ReplaceSQLSafe(sSQL),',',GetUnChar,[rfReplaceAll]));Open;RstStrs := VarArrayCreate([0,NegToZero(RecordCount-1)],VarOleStr);I := 0;First;while not Eof dobeginRstStrs[I] := Fields[0].AsString;Inc(I);Next;end;Close;end;end;{ 根据用户及条件提取相关需求数据 BES96261UserID: 用户编码dstNm: 需求数据集名称Corp_No: 公司编码Cust_No 客户编码(提取指定客户的数据)ParamStr: 条件值的集合,使用'@' 作分隔符sExpr: 前台传来的附带查询条件的SQL语句 }function THPMRP.GetData(const UserID, dstNm, Corp_No, Cust_No: WideString; ParamStr: OleVariant; const sExpr: WideString): OleVariant;beginTryTmpCDS.Close;mandText := GetSQL(UserID,dstNm,Corp_No,Cust_No,sExpr,ParamStr);TmpCDS.Open;Result := TmpCDS.Data;FinallyTmpCDS.Close;End;end;{ 根据用户和请求的数据及条件返回取值SQL语句 BES96261 }Function THPMRP.GetSQL(UserID, dstNm, Corp_No, Cust_No, swhExpr: WideString; ParamStr: OleVariant): WideString;var sSQL, sExpr, sCrpExpr, sCstExpr: String;I: Integer;{ sSQL: 最终组合的SQL语句sExpr: 数据库表中的指定条件,通常为主从连接sCrpExpr: 提取指定公司的数据条件sCstExpr: 提取指定客户的数据条件}beginIf Trim(UserID) = '' then UserID := U_PublicFun.Pubchar;//临时赋值{ ------------------------ }UserID := 'SUPER';sSQL := Format('select * from Sys_GetData where UserID=%s and FrmNm=%s',[Quotedstr(UserID),Quotedstr(dstNm)]);with get_Q_RecStrs dobeginClose;SQL.Clear;SQL.Add(ReplaceSQLSafe(sSQL));Open;sSQL := '';If RecordCount <> 0 thenbeginIf (not VarIsArray(ParamStr)) or (VarArrayHighBound(ParamStr,1) < 0) then sExpr := ''else sExpr := FieldByName('Expr').AsString;{ 根据条件初始化SQL语句将条件中的参数变量具体化 }I := 0;while Pos('@',sExpr)<>0 dobeginif VarIsArray(ParamStr) and (VarArrayHighBound(ParamStr,1)>= I) thensExpr := StringReplace(sExpr,'@',ParamStr[I],[rfIgnoreCase])else sExpr := StringReplace(sExpr,'@',QuotedStr('0'),[rfIgnoreCase]);Inc(I);end;{ 客户权限 }sCstExpr := FieldByName('CstExpr').AsString;If Trim(sCstExpr) <> '' thensCstExpr := StringReplace(sCstExpr,'@Cust_No',QuotedStr(Cust_No),[rfIgnoreCase])elsesCstExpr := '';//公司权限sCrpExpr := FieldByName('CrpExpr').AsString;If Trim(sCrpExpr) <> '' thensCrpExpr := StringReplace(sCstExpr,'@Corp_No',QuotedStr(Corp_No),[rfIgnoreCase])elsesCrpExpr := '';//处理附带的SQL条件表达式If Trim(swhExpr) <> '' thenbeginIf (UpperCase(LeftStr(Trim(swhExpr),2)) <> 'OR') and (UpperCase(LeftStr(Trim(swhExpr),3)) <> 'AND') thenbeginIf Trim(sExpr+sCstExpr+sCrpExpr)<>'' then swhExpr := ' And '+swhExprelse swhExpr := ' where '+swhExpr;end elsebeginIf Trim(sExpr+sCstExpr+sCrpExpr) ='' then swhExpr := ' where 1=1 '+swhExpr;end;end;{ 生成SQL语句 }sSQL := 'Select '+FieldByName('MaxRec').AsString+' '+FieldByName('ColNm').AsString+ ' '+FieldByName('TabNm').AsString+' '+sExpr+' '+sCrpExpr+' '+ sCstExpr+' '+swhExpr +' '+FieldByName('OrdSQL').AsString;end;Close;end;Result := sSQL;end;{ 校验字段是否为空BES96261 }Function THPMRP.CannotNull(FieldStr: String; DeltaDS:TCustomClientDataSet; UpdKind: String=''):String; var I:Integer;FieldNm, VisField:String; //Field NamebeginResult := '';If UpdKind = 'Del' then Exit;If Trim(FieldStr)='' then Exit;While Trim(FieldStr)<>'' dobeginI:=Pos(';',FieldStr);If I<=0 thenbeginFieldNm := FieldStr;FieldStr := '';end elsebeginFieldNm := Copy(FieldStr,1,I-1);FieldStr := Copy(FieldStr,I+1,Length(FieldStr)-I);End;VisField := Copy(FieldNm,Pos(',',FieldNm)+1,length(FieldNm)-Pos(',',FieldNm));FieldNm := Trim(Copy(FieldNm,1,Pos(',',FieldNm)-1));If (VarIsEmpty(DeltaDS.FieldByName(FieldNm).NewValue) or(VarToStr(DeltaDS.FieldByName(FieldNm).NewValue) = ''))and ((UpdKind='Ins') or ((UpdKind='Upd') and VarIsEmpty(DeltaDS.FieldByName(FieldNm).OldValue))) thenbeginResult := 'Please input '+quotedstr(VisField)+' value.';Exit;End;End;end;{ 使用存储过程更新数据集时赋相应参数值 BES96261spName: 需调用的更新存储过程名DeltaDS: 需更新的数据集ParmaeStr: 存储过程参数名及对应的取值字段名ParameSet: 存储过程参数名及对应的取(Oldvalue)值字段名,用于关键字UpdKind: 数据更新类型 --修改,新增,删除}Function THPMRP.SetspParam(spName:String; DeltaDS:TCustomClientDataSet;ParameStr,ParamSet:WideString; UpdKind: String=''): WideString;var I: Integer;S, ParamName,FieldName: String;beginIf Trim(ParameStr) = '' then Exit;{ 获取存储过程名及相关参数}sp_pub_ref.ProcedureName := spName;sp_pub_ref.Parameters.Refresh;{ 根据参数名赋需更新数据集对应字段值 }While Trim(ParameStr) <> '' dobeginI := Pos(';',ParameStr);If I <= 0 thenbeginS := ParameStr;ParameStr := '';end elsebeginS := Copy(ParameStr,1,I-1);ParameStr := Copy(ParameStr,I+1,Length(ParameStr)-I);End;ParamName := Trim(Copy(S,1,Pos(',',S)-1));FieldName := Trim(Copy(S,Pos(',',S)+1,length(S)-Pos(',',S)));if FieldName = '-' then FieldName := Trim(copy(ParamName,2,length(ParamName)-1));if (VarIsEmpty(DeltaDS.FieldByName(FieldName).NewValue) and (UpdKind<>'Ins')) or (UpdKind='Del') thensp_pub_ref.Parameters.ParamByName(ParamName).Value :=DeltaDS.FieldByName(FieldName).OldValueelsesp_pub_ref.Parameters.ParamByName(ParamName).Value :=DeltaDS.FieldByName(FieldName).NewValue;End; //end while{ 赋数据更新类型值}if Trim(UpdKind) <> '' thensp_pub_ref.Parameters.ParamByName('@UpdateKind').Value := UpdKind;{ 根据关键字参数名赋所对应Old值 }While Trim(ParamSet) <> '' dobeginI := Pos(';',ParamSet);If I <= 0 thenbeginS := ParamSet;ParamSet := '';end elsebeginS := Copy(ParamSet,1,I-1);ParameStr := Copy(ParamSet,I+1,Length(ParamSet)-I);End;ParamName := Trim(Copy(S,1,Pos(',',S)-1));FieldName := Trim(Copy(S,Pos(',',S)+1,length(S)-Pos(',',S)));if FieldName = '-' then FieldName := Trim(copy(ParamName,2,length(ParamName)-1));sp_pub_ref.Parameters.ParamByName(ParamName).Value := DeltaDS.FieldByName(FieldName).OldValue End; //end whilesp_pub_ref.ExecProc;Result := sp_pub_ref.Parameters.ParamByName('@rststr').Value;end;{ 根据数据集更新状态返加对应的字符串BES96261 }Function THPMRP.UpdKindStr(var Kind: TUpdateKind): String;beginif Kind = ukModify then Result := 'Upd';if Kind = ukInsert then Result := 'Ins';if Kind = ukDelete then Result := 'Del';end;{ 数据公用更新过程 BES96261 2003-12-25 17:02 }procedure THPMRP.PubBeforeUpdateRecord(Sender: TObject;SourceDS: TDataSet; DeltaDS: TCustomClientDataSet;UpdateKind: TUpdateKind; var Applied: Boolean);var sMsg, spName: String;ChkNull, ParameStr, ParamSet:WideString;UpdKind: ShortString;beginspName := app_spName;ParameStr := app_ParameStr;ParamSet := app_ParamSet;ChkNull := app_ChkNull;sMsg := '';UpdKind := UpdKindStr(UpdateKind);{ 不为空检测 }sMsg := CannotNull(ChkNull, DeltaDS, UpdKind);if Trim(sMsg) <> '' then //raise Exception.Create(IntToStr(DeltaDS.RecNo) + Unchar + UpdKind + Unchar + sMsg); { 数据更新 }If Trim(sMsg) = '' thenbeginsMsg := SetspParam(spName,DeltaDS,ParameStr,ParamSet,UpdKind);if Trim(sMsg) <> '' then //raise Exception.Create(IntToStr(DeltaDS.RecNo) + Unchar + UpdKind + Unchar + sMsg); end;Applied := True;end;{ 数据公用提交过程 BES96261UserID: 用户编码,用以权限判断dstNm: 提交的功能数据集Parmastr: 更新参数vData: 需更新的数据集Result: 更新过程中需返回的列表}function THPMRP.SetData(const UserID, dstNm, ParamStr: WideString;vData: OleVariant): OleVariant;var ErrCount: Integer;beginIf GetAuth(UserID,dstNm,'Apply') = 1000 thenbegin{ 此次更新与上次更新数据集不相同则从后台取更新数据参数值 }If dstNm <> app_dspName thenbeginapp_dspName := dstNm;with sp_get_apply dobeginClose;Parameters.ParamByName('@dsp_nm').Value := app_dspName;ExecProc;app_spName := Parameters.ParamByName('@spName').Value;app_ParameStr :=Parameters.ParamByName('@Pstr1').Value+Parameters.ParamByName('@Pstr2').Value;app_ParamSet := Parameters.ParamByName('@Pstr3').Value;app_ChkNull := Parameters.ParamByName('@Chkstr').Value;end;end;Result := tmpdsp.ApplyUpdates(vData,-1,ErrCount);end;{ 操作数据集时权限判断BES96261 }function THPMRP.GetAuth(const UserID, dstNm, GrpTyp: WideString): OleVariant; beginResult := 1000;end;{ 使用存储过程查询,并返回结果值 BES96261 }function THPMRP.GetspData(const UserID, dstNm: WideString;ParamStr: OleVariant): OleVariant;beginFrmServer.Memo1.Lines.Add(UserID + '---' + dstNm);{ Open Query and Result Data}If SetspParameters(UserID, dstNm, ParamStr, sp_get_Data) thenbeginsp_get_Data.Open;Result := dsp_get_spQuery.Data;sp_get_Data.Close;end;end;{ 执行存储过程,无结果集返回 }function THPMRP.ExecProc(const UserID, Corp_No, dstNm: WideString; ParamStr: OleVariant): Shortint;beginResult := -1;FrmServer.Memo1.Lines.Add(UserID + '---Exec Procedure---' + dstNm);{ Exec Procedure }If SetspParameters(UserID, dstNm, ParamStr, sp_exec) thensp_exec.ExecProc;Result := 1;end;end;{ 执行存储过程或通过存储过程查询数据时设置存储过程参数 BES96261 } Function THPMRP.SetspParameters(UserID, dstNm: String; ParamStr: OleVariant; run_sp_Nm: TADOStoredProc): Boolean;var spNm: String; //存储过程名称I: Integer;beginResult := False;If Trim(dstNm) = '' then Exit;If GetAuth(UserID,dstNm,'Query') <> 1000 then Exit;with sp_get_spNm dobeginClose;Parameters.ParamByName('@UserID').Value := UserID;Parameters.ParamByName('@dstnm').Value := dstNm;ExecProc;spNm := Parameters.ParamByName('@spNm').Value;end;If Trim(spNm) = '' then Exit;{ Exec Proc }run_sp_Nm.Close;run_sp_Nm.ProcedureName := spNm;run_sp_Nm.Parameters.Refresh;If not varIsNull(ParamStr) and VarIsArray(ParamStr) thenbeginFor I:=0 to VarArrayHighBound(ParamStr,1) dobegincase varTypeCntInt(run_sp_Nm.Parameters[I+1].DataType) of2: run_sp_Nm.Parameters[I+1].Value := StrToFloat(ParamStr[I]);3: run_sp_Nm.Parameters[I+1].Value := VarCntbool(ParamStr[I]);else run_sp_Nm.Parameters[I+1].Value := ParamStr[I];end;FrmServer.Memo1.Lines.Add(ParamStr[I]);end; //end forend;Result := True;end;{ 判数参数类型 }Function THPMRP.varTypeCntInt(varType: TDataType): Integer;beginCase varType offtString, ftDate, ftTime, ftDateTime, ftWideString,ftFixedChar :Result := 1;ftSmallint, ftInteger, ftWord, ftFloat, ftCurrency, ftBCD, ftLargeint,ftBytes, ftVarBytes :Result := 2;ftBoolean : Result := 3;elseResult := 1;end;end;{ 根据条件返回指定列字段数据BES96261 } procedure THPMRP.GetColStrs(const UserID, Corp_No, TabName, ColName,ExprStr: WideString; out RstStrs: OleVariant);var sSQL, sExpr: String;I: Integer;beginsExpr := ExprStr;If Trim(sExpr) <> '' then sExpr := ' where '+sExpr;sSQL := Format('select %s from %s'+sExpr,[ColName,TabName,ExprStr]); with get_Q_RecStrs dobeginClose;SQL.Clear;SQL.Add(sSQL);Open;RstStrs := VarArrayCreate([0,NegToZero(Fields.Count-1)],VarOleStr);If RecordCount > 0 thenbeginFor I := 0 to Fields.Count - 1 doRstStrs[I] := Fields[I].AsString;end;Close;end;end;initializationTComponentFactory.Create(ComServer, THPMRP,Class_HPMRP, ciMultiInstance, tmFree);end./////***********************************************************/// ////U_RDM.dfm文件object HPMRP: THPMRP OldCreateOrder = FalseOnCreate = RemoteDataModuleCreate Left = 196Top = 124Height = 203Width = 481object sp_Pub_Ref: TADOStoredProcConnection = BesConnectionParameters = <>Left = 32Top = 80endobject get_Q_RecStrs: TADOQueryConnection = BesConnectionParameters = <>Left = 112Top = 16endobject BesConnection: TADOConnection ConnectionTimeout = 5LoginPrompt = FalseProvider = 'SQLOLEDB.1'Left = 32Top = 16endobject TmpCDS: TClientDataSetAggregates = <>Params = <>ProviderName = 'tmpdsp'Left = 192Top = 16endobject tmpdsp: TDataSetProviderTag = 100DataSet = Q_tmpOptions = [poAllowCommandText] UpdateMode = upWhereKeyOnlyLeft = 240Top = 16endobject Q_tmp: TADOQueryConnection = BesConnectionCursorType = ctStaticParameters = <>SQL.Strings = ('')Left = 288Top = 16endobject sp_get_apply: TADOStoredProc Connection = BesConnection ProcedureName = 'Bes_S_GetApplyParame;1' Parameters = <itemName = '@RETURN_VALUE'DataType = ftIntegerDirection = pdReturnValuePrecision = 10enditemName = '@dsp_nm' Attributes = [paNullable] DataType = ftStringSize = 50Value = NullenditemName = '@spName' Attributes = [paNullable] DataType = ftString Direction = pdInputOutput Size = 50Value = NullenditemName = '@Pstr1' Attributes = [paNullable] DataType = ftString Direction = pdInputOutput Size = 255Value = NullenditemName = '@Pstr2' Attributes = [paNullable] DataType = ftString Direction = pdInputOutputValue = NullenditemName = '@Pstr3'Attributes = [paNullable]DataType = ftStringDirection = pdInputOutputSize = 255Value = NullenditemName = '@Chkstr'Attributes = [paNullable]DataType = ftStringDirection = pdInputOutputSize = 255Value = Nullend>Prepared = TrueLeft = 386Top = 16endobject sp_get_spNm: TADOStoredProc Connection = BesConnection ProcedureName = 'Bes_S_GetspQuery;1' Parameters = <itemName = '@RETURN_VALUE'DataType = ftIntegerDirection = pdReturnValuePrecision = 10Value = NullenditemName = '@UserID'Attributes = [paNullable]DataType = ftStringSize = 50Value = NullenditemName = '@dstnm'Attributes = [paNullable]DataType = ftStringSize = 50Value = NullenditemName = '@spNm'Attributes = [paNullable]DataType = ftStringDirection = pdInputOutputSize = 50Value = Nullend>Left = 386Top = 72endobject dsp_get_spQuery: TDataSetProviderDataSet = sp_get_DataLeft = 385Top = 126endobject sp_get_Data: TADOStoredProcConnection = BesConnectionCommandTimeout = 800Parameters = <>Left = 120Top = 80endobject sp_exec: TADOStoredProcConnection = BesConnectionCommandTimeout = 500Parameters = <>Left = 192Top = 80endend/////***********************************************************/// /////公用单元文件U_PublicFun.pasunit U_PublicFun;interfaceuses SysUtils, IniFiles, Forms;Function EncrypKey(Src:String; Key:String='wtgvkssqyouvkxnn2'):string;Function UncrypKey(Src:String; Key:String='wtgvkssqyouvkxnn2'):string;Function GetServerConnetionStr: String; //获取数据库接字符串Function UnionStr(const Str1,Str2: String):String;Function ReplaceSQLSafe(var SQLStr: String): String;Function GetUnChar: String; //在表达式中替换连接符值固定为:'+"'+unchar+'"+' Function NegToZero(value: Integer): Integer; //如果是负值,则转为0Function VarCntbool(value: Integer): Boolean; overload;Function VarCntbool(value: string): Boolean; overload;constUnchar = ' -- '; //多字段之间的连接分隔符Pubchar = 'SUPER'; //公用数据编码或用户编码implementationFunction EncrypKey (Src:String; Key:String):string;varKeyLen, KeyPos, offset, SrcPos, SrcAsc, Range :Integer;dest :string;beginKeyLen:=Length(Key);if KeyLen = 0 then key:='wtgvkssqyouvkxnn2';KeyPos:=0;Range:=256;Randomize;offset:=Random(Range);dest:=format('%1.2x',[offset]);for SrcPos := 1 to Length(Src) dobeginSrcAsc:=(Ord(Src[SrcPos]) + offset) MOD 255;if KeyPos < KeyLen then KeyPos:= KeyPos + 1 else KeyPos:=1;SrcAsc:= SrcAsc xor Ord(Key[KeyPos]);dest:=dest + format('%1.2x',[SrcAsc]);offset:=SrcAsc;end;Result:=Dest;end;Function UncrypKey (Src:String; Key:String):string;varKeyLen, KeyPos, offset, SrcPos, SrcAsc, TmpSrcAsc :Integer;dest :string;beginKeyLen:=Length(Key);if KeyLen = 0 then key:='wtgvkssqyouvkxnn2';KeyPos:=0;offset:=StrToInt('$'+ copy(src,1,2));SrcPos:=3;repeattrySrcAsc:=StrToInt('$'+ copy(src,SrcPos,2));if KeyPos < KeyLen Then KeyPos := KeyPos + 1else KeyPos := 1;TmpSrcAsc := SrcAsc xor Ord(Key[KeyPos]);if TmpSrcAsc <= offset then TmpSrcAsc := 255 + TmpSrcAsc - offset else TmpSrcAsc := TmpSrcAsc - offset;dest := dest + chr(TmpSrcAsc);offset:=srcAsc;SrcPos:=SrcPos + 2;exceptend;until SrcPos >= Length(Src);Result:=Dest;end;{ 获取数据库接字符串 BES96261 2003-11-18 18:50 }Function GetServerConnetionStr: String;var tempIni: TIniFile;begintempIni := TIniFile.Create(ExtractFilePath(Application.ExeName)+'ServerConfig.ini');TryResult := 'Provider=SQLOLEDB.1;Password='+UncrypKey(tempIni.ReadString('SYSTEM', 'PassWord', ''))+ ';Persist Security Info=True;User ID='+UncrypKey(tempIni.ReadString('SYSTEM', 'UserID', ''))+';Initial Catalog='+tempIni.ReadString('SYSTEM', 'DBNAME', '')+';Data Source='+tempIni.ReadString('SYSTEM', 'SERVER', '')FinallytempIni.Free;End;end;{ 连接字符串,当Str2为空时返回空串 BES96261 2003-11-23 22:10 }Function UnionStr(const Str1,Str2: String):String;beginIf Length(Str2) = 0 thenResult := ''elseResult := Str1 + ' ' + Str2;end;{ 过滤从客户端传来SQL参数中的不安全关键字 BES96261 2003-11-23 10:15 }Function ReplaceSQLSafe(var SQLStr: String): String;var S: String;beginS := StringReplace(SQLStr,'--','',[rfReplaceAll, rfIgnoreCase]);S := StringReplace(S,'/*','',[rfReplaceAll, rfIgnoreCase]);S := StringReplace(S,'Delete ','',[rfReplaceAll, rfIgnoreCase]);S := StringReplace(S,'Drop ','',[rfReplaceAll, rfIgnoreCase]);S := StringReplace(S,'Exec ','',[rfReplaceAll, rfIgnoreCase]);S := StringReplace(S,'Create ','',[rfReplaceAll, rfIgnoreCase]);S := StringReplace(S,'Alter ','',[rfReplaceAll, rfIgnoreCase]);S := StringReplace(S,'Update ','',[rfReplaceAll, rfIgnoreCase]); Result := S;end;{ 在表达式中替换连接符 BES96261 2003-12-09 23:21 } Function GetUnChar: String;beginResult := '+'+QuotedStr(Unchar)+'+';end;{ 若数值为负,则转为0输出 BES96261 2003-12-09 23:07 } Function NegToZero(Value: Integer): Integer;beginIf Value < 0 then Result := 0else Result := Valueend;Function VarCntbool(value: Integer): Boolean;beginResult := (value <> 0);end;Function VarCntbool(value: string): Boolean; beginIf (Trim(value)='') or (Trim(value)='0') thenResult := FalseelseResult := True;end;end.。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

delphi 三层架构简单例子(经测试成功2009-01-22 下午 02:45所谓三层: (1 客户端 (2 服务器端 (3 数据库在数据访问时,使得客户端必须通过服务器来访问数据库。

提高了系统的安全性。

在Delphi中可以使用Socket或者Dcom来连接他们相互间的通讯。

如果使用Scocket在系统使用时必须提供Scocket连接器,而Dcom
则不用。

客户端和服务器的连接需要Broker来联系。

环境为winxp sp2 + delphi 7 + db7.(MSSQL2000 创建过程: 1、请不要新建application.file-new-activex-activex library,file --new--other,选择"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里就可以了。

如果需要code的话,请发mail到tech@ 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以上,即使使用包编译模式。

相关文档
最新文档