c#利用三层架构做一个简单的登录窗体
就个人而言,三层架构有点难理解,不知道该如何下手,各层与各层之间怎么调用
最近一直在研究三层架构,经过网上学习与多方打听写一下自己的心得。有不足之处,可以评论和私聊探讨
言归正传:
三层架构(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);//如果有返回值则登录成功
}
}
}
到这里就大功告成了,登录效果

c#利用三层架构做一个简单的登录窗体的更多相关文章
- python小练习,利用dict,做一个简单的登录。
'''利用字典实现登录'''users=[{'username':'jerry','pwd':'123456'},{'username':'tom','pwd':'1'}] def login(use ...
- 第四章 .net core做一个简单的登录
项目目标部署环境:CentOS 7+ 项目技术点:.netcore2.0 + Autofac +webAPI + NHibernate5.1 + mysql5.6 + nginx 开源地址:https ...
- 利用python+tkinter做一个简单的智能电视遥控器
要通过python实现遥控器功能分两步: 第一步:开发图形化界面,以暴风TV的遥控器按钮为例 第二步:使PC端给电视发送相应指令(此步骤需要打开电视的adb开关) 现在就开始第一步操作实现遥控器功能, ...
- python 做一个简单的登录接口
# -*- conding :utf-8 -*-# File Name: homewoe# Create Date: 2019/11/20 / 9:15# Change Activity: 2019/ ...
- 使用Multiplayer Networking做一个简单的多人游戏例子-3/3(Unity3D开发之二十七)
使用Multiplayer Networking做一个简单的多人游戏例子-1/3 使用Multiplayer Networking做一个简单的多人游戏例子-2/3 使用Multiplayer Netw ...
- 程序猿修仙之路--数据结构之你是否真的懂数组? c#socket TCP同步网络通信 用lambda表达式树替代反射 ASP.NET MVC如何做一个简单的非法登录拦截
程序猿修仙之路--数据结构之你是否真的懂数组? 数据结构 但凡IT江湖侠士,算法与数据结构为必修之课.早有前辈已经明确指出:程序=算法+数据结构 .要想在之后的江湖历练中通关,数据结构必不可少. ...
- MUI框架-05-用MUI做一个简单App
MUI框架-05-用MUI做一个简单App MUI 是一个前端框架,前端框架就像 Bootstrap,EasyUI,Vue ,为了做 app 呢,就有了更加高效的 MUI,我觉得前端框架有很多,也没有 ...
- 【Bugly干货分享】一起用 HTML5 Canvas 做一个简单又骚气的粒子引擎
Bugly 技术干货系列内容主要涉及移动开发方向,是由Bugly邀请腾讯内部各位技术大咖,通过日常工作经验的总结以及感悟撰写而成,内容均属原创,转载请标明出处. 前言 好吧,说是“粒子引擎”还是大言不 ...
- 使用React并做一个简单的to-do-list
1. 前言 说到React,我从一年之前就开始试着了解并且看了相关的入门教程,而且还买过一本<React:引领未来的用户界面开发框架 >拜读.React的轻量组件化的思想及其virtual ...
随机推荐
- PHP操作Access数据库
ADO是一项微软的技术,ADO指ActiveX数据对象(ActiveX Data Objects). 链接数据库 <?php header("Content-Type:text/htm ...
- 有关tensorflow一些问题
1.python版本 采用64位的python 2.系统不支持高版本tensorflow(>1.6),运行报错如下: 问题描述如下: ImportError: DLL load failed: ...
- python_面向对象—代码练习
"""注意:代码切勿照搬,错误请留言指出""" import re ''' class Person: name='xxx' age=20 ...
- linux 拓展之linux纯命令行界面变为图形界面
使用版本为6.x 1, 连接网络 没网络就下载不了哦 2,设置下yum源,我本机原来的yum源是网易的但是我用不了,我设置阿里云可以下载, 你们有这问题的可以试试 3, yum groupin ...
- jacob自己动生成word文档目录
任务目的 1自动生成word文档目录. 用例测试操作步骤 在一个word文档的第二页填写占位符: {目录}保存.调用程序读取目标文档,自动根据标题生成目录到{目录}位置. 效果 关键代码 insert ...
- (Frontend Newbie)JavaScript基础之函数
函数可以说是任何一门编程语言的核心概念.要能熟练掌握JavaScript,对于函数及其相关概念的学习是非常重要的一步.本篇从函数的基本知识.执行环境与作用域.闭包.this关键字等方面简单介绍Java ...
- (Frontend Newbie)JavaScript基础之常见数据类型
JavaScript中的数据类型分为两种,一种是简单数据类型,包括Undefined.Null.Boolean.Number和String,另一种是复杂数据类型,即Object,也可称作为引用类型. ...
- Go语言下载网络图片或文件
最近闲来无事, 于是就简单学习了下Go语言的基本的用法.由于实践才是最快的学习方法,所以这里就以下载网络图片或文件入手来学习Go语言 文件下载到本地,通常的思路就是先获得网络文件的 输入流 以及本地文 ...
- sql查询某字段的相同值
sql查询某字段的相同值: SELECT * FROM table WHERE col in (SELECT col FROM table GROUP BY col HAVING COUNT (col ...
- log4net写入DB2备忘 via OLEDB & ODBC
在项目中遇到需要记录操作日志的需求,由于是一个外挂系统,因此不用考虑到公司框架的限制,直接二层架构直连数据库,考虑使用log4net连接DB2.请宽恕我这个非软工科班出身的IT小白,以前一直在知道有个 ...