就个人而言,三层架构有点难理解,不知道该如何下手,各层与各层之间怎么调用

最近一直在研究三层架构,经过网上学习与多方打听写一下自己的心得。有不足之处,可以评论和私聊探讨

言归正传:

三层架构(3-tier architecture) 通常意义上的三层架构就是将整个业务应用划分为:界面层(User Interface layer)、业务逻辑层(Business Logic Layer)、数据访问层(Data access layer)。区分层次的目的即为了"高内聚低耦合"的思想。在软件体系架构设计中,分层式结构是最常见,也是最重要的一种结构。微软推荐的分层式结构一般分为三层,从下至上分别为:数据访问层、业务逻辑层(又或称为领域层)、表示层。

三层体系的应用程序将业务规则、数据访问、合法性校验等工作放到了中间层进行处理。通常情况下,客户端不直接与数据库进行交互,而是通过COM/DCOM通讯与中间层建立连接,再经由中间层与数据库进行交互。

UI层:即表示层,就是展现给用户看到的界面

BLL:即业务逻辑层,就是实现功能的,用来写方法及其调用

DAL:即数据访问层,也就是说,是对数据库的操作,而不是数据,具体为业务逻辑层或表示层提供数据服务。说白了就是写sql语句的;主要是存放对数据类的访问,即对数据库的添加、删除、修改、更新等基本操作

除此三层外,聪明的人一定就还会说中间还有一个model(模型层)作为承载数据的媒介,供上面三个层引用。用来存储实体类的,所以model实体类也很重要,

model:实体类库,主要存放数据库中的表字段

引用顺序为UI引用BLL;BLL引用DAL;也可以间接引用

案例分析:

我们以一个登录窗体作为案例

首先在数据库建一张账号用户表,并填写部分数据,用来测试

如图

方案目录结构

就个人而言还是从UI层开写更好一点,因为UI层能直观的看出你想实现什么功能效果,以便于下层该怎么下手

1.UI层

UI层做好,接下来最好做model实体类,实体类库(Model),主要存放数据库中的表字段。

后台代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms; namespace 登录
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
//实例化model层中 userInfo类用于传递数据
Model.userInfo m_userInfo = new Model.userInfo(); //实例化BAL层中 userAccess方法衔接用户输入与数据库匹配
BAL.userBLL b_userAccess = new BAL.userBLL();
private void Form1_Load(object sender, EventArgs e)
{ } private void button1_Click(object sender, EventArgs e)
{
//将用户输入的账号密码 赋值给userInfo类 username、psw属性
m_userInfo.username = textBox1.Text.Trim().ToString();
m_userInfo.psw = textBox2.Text.Trim().ToString(); //如果BLL层中 useLogin调用返回记录条数 大于1 则账号密码正确
if (b_userAccess.userLogin(m_userInfo) > )
{
MessageBox.Show("登录成功");
}
else
{
MessageBox.Show("登录失败");
}
}
}
}

2.MODEL模型层

model层其实就相当于一个中转站,用来存储用到的数据,贯穿三层,数据的赋值及提取

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace WindowsFormsApp4.MODEL
{
class userInfo //想创建一个用户实体类
{
//用get;set方法
public string username { get; set; }
public string password { get; set; }
}
}

model实体类做好后接下来就可以写DAL层了,DAL就是书写sql语句操作数据库,(就拿登录窗体来说,我们在登录时要输入账号和密码,点击登录时就要与数据库里的数据进行对比,只有验证一致才能登陆成功。这时就要书写sql语句取出数据库里的数据与你在输入框输入的数据进行对比)

在写DAL层时要写一个SQLhelper帮助类(六面提供了很多方法与对象,极大的方便了开发人员的开发效率;其实一个sqlhelper帮助类可以放在任何地方调用,那小编就给大家一呗)

 using System;
using System.Data;
using System.Collections;
using System.Configuration; //记得这个asp默认没引用,你要去添加引用的程序集里引用进来
using System.Linq;
using System.Web;
using System.Xml.Linq;
using System.Data.SqlClient;
using System.Collections.Generic; namespace UMS.DbHelper
{
/// <summary>
/// SQL数据库操作类
/// </summary>
public class SQLHelper
{
private string strConn;
private SqlConnection sqlConn = null; public SQLHelper()
{
strConn = ConfigurationManager.ConnectionStrings["UserData"].ConnectionString;
sqlConn = new SqlConnection(strConn); } /// <summary>
/// 打开数据库连接
/// </summary>
private void OpenConn()
{
if (sqlConn != null && sqlConn.State == ConnectionState.Closed)
{
sqlConn.Open();
}
} /// <summary>
/// 关闭数据库连接
/// </summary>
private void CloseConn()
{
if (sqlConn != null && sqlConn.State == ConnectionState.Open)
{
sqlConn.Close();
}
}
/// <summary>
/// 构造操作命令
/// </summary>
/// <param name="cmdText">带参命令</param>
/// <param name="param">参数数组</param>
/// <param name="values">参数值数组</param>
/// <returns></returns>
private SqlCommand CreateCommand(string cmdText, string[] param, object [] values)
{
SqlCommand myCmd = new SqlCommand(cmdText,sqlConn);
for (int i = ; i < param.Length; i++)
{
myCmd.Parameters.AddWithValue(param[i],values[i]);
}
return myCmd;
}
/// <summary>
/// 根据SQL指令返回相应查询阅读器,在阅读器使用完后请及时关闭
/// </summary>
/// <param name="cmdText">查询语句</param>
/// <param name="param">参数列表,无参可设置为null</param>
/// <param name="values">参数值列表,只有当参数不为空时有效</param>
/// <returns></returns>
public SqlDataReader ExecuteReader(string cmdText,string [] param,object [] values)
{
OpenConn();
SqlCommand myCmd;
if (param != null)
{
myCmd = this.CreateCommand(cmdText, param, values);
}
else
{
myCmd = new SqlCommand(cmdText,sqlConn);
}
return myCmd.ExecuteReader(CommandBehavior.CloseConnection);
} /// <summary>
/// 根据存储过程返回相应查询阅读器,在阅读器使用完后请及时关闭
/// </summary>
/// <param name="cmdText">存储过程名</param>
/// <param name="parms">参数列表</param>
/// <returns></returns>
public SqlDataReader ExecuteReaderBySP(string cmdText, SqlParameter[] parms)
{
OpenConn();
SqlCommand myCmd = new SqlCommand(cmdText, sqlConn);
myCmd.CommandType = CommandType.StoredProcedure;
if (parms != null)
{
myCmd.Parameters.AddRange(parms);
}
return myCmd.ExecuteReader(CommandBehavior.CloseConnection);
} /// <summary>
/// 根据SQL指令返回受影响行数,主要用于数据库的更新、插入、删除等操作
/// </summary>
/// <param name="cmdText">sql命令语句</param>
/// <param name="param">参数数组,若没有参数可以设置为空</param>
/// <param name="values">参数值数组,只有当param不为空时有效</param>
/// <returns></returns>
public int ExecuteNoneQuery(string cmdText, string[] param, object[] values)
{
OpenConn();
SqlCommand myCmd;
if (param != null)
{
myCmd = this.CreateCommand(cmdText, param, values);
}
else
{
myCmd = new SqlCommand(cmdText,sqlConn);
}
try
{
return myCmd.ExecuteNonQuery();
}
catch (Exception ex)
{
throw ex;
}
finally
{
CloseConn();
}
} /// <summary>
/// 根据SQL指令返回第一行第一列结果
/// </summary>
/// <param name="cmdText">sql命令语句</param>
/// <param name="param">参数数组,若没有参数可以设置为空</param>
/// <param name="values">参数值数组,只有当param不为空时有效</param>
/// <returns></returns>
public object ExecuteScalar(string cmdText, string[] param, object[] values)
{
OpenConn();
SqlCommand myCmd;
if (param != null)
{
myCmd = this.CreateCommand(cmdText, param, values);
}
else
{
myCmd = new SqlCommand(cmdText,sqlConn);
}
try
{
return myCmd.ExecuteScalar();
}
catch (Exception ex)
{
throw ex;
}
finally
{
CloseConn();
}
}
/// <summary>
/// 带事务执行存储过程,该方法主要用于执行用于数据维护类的存储过程执行
/// </summary>
/// <param name="cmdText">存储过程名称</param>
/// <param name="parms">SQL参数数组</param>
public int ExecuteNoneQueryBySP(string cmdText, SqlParameter[] parms)
{
OpenConn();
SqlTransaction tran = sqlConn.BeginTransaction();
SqlCommand myCmd = new SqlCommand(cmdText, sqlConn);
myCmd.CommandType = CommandType.StoredProcedure;
if (parms != null)
{
myCmd.Parameters.AddRange(parms);
}
myCmd.Transaction = tran;
try
{
int result=myCmd.ExecuteNonQuery();
tran.Commit();
return result;
}
catch (Exception ex)
{
tran.Rollback();
throw ex;
}
finally
{
CloseConn();
}
}
/// <summary>
/// 根据命令语句返回数据集
/// </summary>
/// <param name="cmdText">命令语句</param>
/// <param name="param">参数数组,若没有参数可以设置为空</param>
/// <param name="values">参数值数组,只有当param不为空时有效</param>
/// <returns></returns>
public DataSet FillDataSet(string cmdText, string[] param, object[] values)
{
OpenConn();
SqlCommand myCmd;
if (param != null)
{
myCmd = this.CreateCommand(cmdText, param, values);
}
else
{
myCmd = new SqlCommand(cmdText,sqlConn);
}
SqlDataAdapter myAdp = new SqlDataAdapter(myCmd);
DataSet ds = new DataSet();
try
{
myAdp.Fill(ds);
return ds;
}
catch (Exception ex)
{
throw ex;
}
finally
{
CloseConn();
}
} /// <summary>
/// 执行特定存储过程并返回查询后的数据结果,该方法用于执行查询类的存储过程
/// </summary>
/// <param name="cmdText">存储过程名</param>
/// <param name="parms">SQL参数数组,若没有参数可以设置为空</param>
/// <returns></returns>
public DataSet FillDataSetBySP(string cmdText, SqlParameter[] parms)
{
OpenConn();
SqlCommand myCmd = new SqlCommand(cmdText, sqlConn);
myCmd.CommandType = CommandType.StoredProcedure;
if (parms != null)
{
myCmd.Parameters.AddRange(parms);
}
SqlDataAdapter myAdp = new SqlDataAdapter(myCmd);
DataSet ds = new DataSet();
try
{
myAdp.Fill(ds);
return ds;
}
catch (Exception ex)
{
throw ex;
}
finally
{
CloseConn();
}
}
/// <summary>
/// 执行存储过程返回输出参数
/// </summary>
/// <param name="cmdText">存储过程名</param>
/// <param name="parms">参数数组</param>
/// <returns>包含所有输出值的ArrayList</returns>
public ArrayList ExecuteSp(string cmdText, SqlParameter[] parms)
{
OpenConn();
SqlCommand myCmd = new SqlCommand(cmdText, sqlConn);
myCmd.CommandType = CommandType.StoredProcedure;
if (parms != null)
{
myCmd.Parameters.AddRange(parms);
}
try
{
myCmd.ExecuteNonQuery();
ArrayList al = new ArrayList();
for (int i = ; i < parms.Length; i++)
{
if (parms[i].Direction == ParameterDirection.Output)
{
al.Add(parms[i]);
}
}
return al;
}
catch (Exception ex)
{
throw ex;
}
finally
{
CloseConn();
}
} #region 批处理操作
/// <summary>
/// 批量数据导入操作
/// </summary>
/// <param name="dt">要批量导入的数据表</param>
/// <param name="destTableName">目标表名</param>
/// <param name="columnMappings">列映射集合</param>
public void BulkInsert(DataTable dt, string destTableName, List<SqlBulkCopyColumnMapping> columnMappings)
{
SqlBulkCopy bulkCopy = new SqlBulkCopy(this.sqlConn);
bulkCopy.DestinationTableName = destTableName;
bulkCopy.BatchSize = dt.Rows.Count;
foreach (SqlBulkCopyColumnMapping map in columnMappings)
{
bulkCopy.ColumnMappings.Add(map);
}
try
{
OpenConn();
bulkCopy.WriteToServer(dt);
}
catch (Exception ex)
{
throw ex;
}
finally
{
this.CloseConn();
bulkCopy.Close();
}
}
#endregion
}
}

类里边不是有个数据库访问方法,那里是在配置文件里配置了数据库连接字符串,所以就没有在方法里写连接字符串

例如

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" /> </startup>
<connectionStrings>
<add name="UserData" connectionString="Data Source=.;Initial Catalog=账号表;User ID=sa;Password=密码"/>
</connectionStrings>
</configuration>

SqlHelper

私有方法有四个,AssignParameterValues方法有一个重载:

AttachParameters:添加参数数组到指定的SqlCommand中 
AssignParameterValues:为SqlParameters(参数)数组赋值 
PrepareCommand:用于对SqlCommand(命令)的属性(如连接、事务环境等)进行初始化。

公有方法有十三个:这当中每个查询数据库的方法用到了大量的重载,每个方法用到了八个左右的重载。

ExecuteNonQuery

此方法用于执行不返回任何行或值的命令。这些命令通常用于执行数据库更新,但也可用于返回存储过程的输出参数。

ExecuteDataset

此方法返回DataSet对象,该对象包含由某一命令返回的结果集。

ExecuteReader

此方法用于返回SqlDataReader对象,该对象包含由某一命令返回的结果集。

ExecuteScalar

此方法返回一个值。该值始终是该命令返回的第一行的第一列。

ExecuteXmlReader

此方法返回 FOR XML 查询的 XML 片段。

FillDataset

此方法向DataSet填充数据。

UpdateDataset

此方法用于执行向DataSet增、删、改的命令。

CreateCommand

此方法用于创建SqlCommand。

3.DAL层

建好第一步就先把sqlhelper类和model实体类引进来(以创建对象的方式)

我这里只写了登录时用到的,别的就要靠你自己摸索了

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace DAL
{
public class userDAL {
//实例化DBbase 对象
DBbase db = new DBbase(); //用户登录的方法
public int userLogin(string name, string psw)
{
string strsql = "select * from users where username = '" + name + "' and password = '" + psw + "'";
return db.returnRowCount(strsql);
}
}
}

登录的sqlhelper类,在这里叫DBbase

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data;
using System.Data.SqlClient; namespace DAL
{
public class DBbase
{
//读取配置文件 连接数据库语句
public static string strCon = System.Configuration.ConfigurationManager.ConnectionStrings["TestDB"].ConnectionString;
//public static string strCon = "Data Source=.;Initial Catalog=threeLayer;Persist Security Info=True;User ID=sa;Password=123"; //实例化连接对象 con
SqlConnection con = new SqlConnection(strCon); //检测连接是否打开
public void chkConnection()
{
if (this.con.State == ConnectionState.Closed)
{
this.con.Open();
}
} //执行语句,返回该语句查询的数据行的总行数
public int returnRowCount(string strSQL)
{
chkConnection();
try
{
SqlDataAdapter da = new SqlDataAdapter(strSQL, con);
DataSet ds = new DataSet();
da.Fill(ds);
return ds.Tables[].Rows.Count;
}
catch
{
return ;
}
}
}
}

4.BLL层 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace BAL
{
public class userBLL
{
DAL.userDAL d_userAccess = new DAL.userDAL();
public int userLogin(Model.userInfo m_userInfo)//把model层的值传过来进行比对
{
return d_userAccess.userLogin(m_userInfo.username, m_userInfo.psw);//如果有返回值则登录成功
}
}
}

到这里就大功告成了,登录效果

最新文章

  1. seajs的使用
  2. .NET开发的大型网站列表、各大公司.NET职位精选,C#王者归来
  3. 编译安装 LLVM
  4. Java 工程师的学习线路图。
  5. Inno Setup执行SQL脚本的方法
  6. HDU 2149 (巴什博奕) Public Sale
  7. 初始化windows窗口
  8. uva 469 - Wetlands of Florida
  9. 3.linux常用软件的安装方法
  10. 「luogu2680」[NOIp2015] 运输计划
  11. UML作业第二次:类在类图中的表示
  12. mybatis的逆向工程和中文注解
  13. springboot学习一:快速搭建springboot项目
  14. P1605 迷宫 dfs回溯法
  15. Centos7系统初始化脚本
  16. Python常用模块--datetime
  17. sqlite数据导入mysql
  18. 1、Maven安装教程详解
  19. Kafka版本升级 ( 0.10.0 -&gt; 0.10.2 )
  20. 爬虫Scrapy指令学习

热门文章

  1. requests模块的一些总结
  2. ASP.NET Core文件上传、下载与删除
  3. Java 写入pdf文件
  4. oracle执行update语句卡住不动
  5. js动态实现时分秒
  6. Oracle 基础系列之1.1 oracle的安装
  7. Java for循环的几种用法分析
  8. java中构造函数的特点
  9. hibernate离线查询DetachedCriteria清除上次的查询条件
  10. 【数据库】Oracle中删除新建并授权用户