宏观:要明确是如何从三层转七层
引用图片自:图片作者原文章
实体层(entity)
:用来封装一些功能性代码,定义实体类型和实体集合,用来各层次传递参数用来存放用户输入的信息。用getset实现达到传递参数的方法达到可以可读可写,同时又有封装性。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Entity
{
public class UserInfo
{
//定义用户ID字段
private int userid;
public int UserId
{
get { return userid; }
set { userid = value; }
}
//定义用户名
private string username;
public string UserName
{
get { return username; }
set { username = value; }
}
//定义密码字段
private string password;
public string PassWord
{
get { return password;}
set { password = value; }
}
}
}
接口层(IDAL)
:定义一个统一的接口方法selectuser,解除B层和D层的耦合,具体代码靠DAL
实现。
注意:因为返回值类型为DataTable ,所以应该引用数据的命名空间。
using System.Data;
Data是数据的意思。
顾名思义,引用这个dll即表示你的命名空间下有需要使用数据、数组的地方,可以直接使用数组类型,而不需要再添加前缀。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data;
namespace IDAL
{
public interface LoginIDAL
{
DataTable selectUser(Entity.UserInfo userInfo);
}
}
数据访问层(DAL)
:提供基本的数据访问,该层将有关数据库的操作放在单独的一个类中,针对数据的增删改查。
DAL层会封装一个sqlHelper。目的是连接数据库实现对数据库的增删改查,这是为了服务于DAL层的。
首先有个构造函数连接上数据库,然后重载了对数据库的增删改查有无参数的方法,
LoginSqlHelper
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.SqlClient;
using System.Data;
using System.Configuration;
namespace DAL
{
///<summary>
///SqlHhelper created by ZGY 2021年11月22日20:25:30
///</summary>
public class SqlHelper
{
private SqlConnection Conn = null;
private SqlCommand Cmd = null;
private SqlDataReader SqlSdr = null;
public SqlHelper()
{
//利用反射连接数据库
//除了要在上边添加命名空间,也要手动在类的引用里边添加引用才能生效
//这里的AppSetting要注意大小写
string strconn = ConfigurationManager.AppSettings["strConn"];
//实例化一个连接
Conn = new SqlConnection(strconn);
}
///<summary>
///打开数据库
///<summary/>
/// <returns>sqlConnection</returns>
private SqlConnection GetConn()
{
if (Conn.State != ConnectionState.Open)
{
try
{
Conn.Open();
}
catch (Exception ex)
{
Conn.Dispose();//没有成功打开,释放资源
throw new Exception("打开数据库失败!");
}
}
return Conn;
}
///<summary>
///执行不带参数的增删改操作
///<summary/>
///<param name="CmdText">执行的sql语句或者存储过程</param>
///<param name="CmdType">语句类型</param>
///
/// <returns>sqlConnection</returns>
public int ExecuteNonQuery(string CmdText, CommandType CmdType)
{
//数据库连接语句
int res;
try
{
Cmd = new SqlCommand(CmdText, GetConn());
Cmd.CommandType = CmdType;
res = Cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (Conn.State == ConnectionState.Open)
{
Conn.Close();
}
}
return res;
}
/// <summary>
/// 执行带参数的增删改操作
/// </summary>
/// <param name="CmdText">执行的SQL语句或存储过程</param>
/// <param name="parameters">参数</param>
/// <param name="CmdType">(SQL语句或存储过程)的类型</param>
/// <returns>返回增删改的操作数(受影响的行数)</returns>
public int ExecuteNonQuery(string CmdText, SqlParameter[] parameters,CommandType CmdType)
{
int res;
try
{
Cmd = new SqlCommand(CmdText, GetConn()); //先建立sql语句
Cmd.Parameters.AddRange(parameters); //再进行参数匹配
Cmd.CommandType = CmdType;
res = Cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (Conn.State==ConnectionState.Open)
{
Conn.Close();
}
}
return res;
}
/// <summary>
/// 执行不带参数的查询操作
/// </summary>
/// <param name="CmdText">执行的SQL语句或存储过程</param>
/// <param name="CmdType">(SQL语句或存储过程)类型</param>
/// <returns>查询后的数据</returns>
public DataTable ExecuteQuery(string CmdText, CommandType CmdType)
{
DataTable dt = new DataTable();
using (SqlSdr = Cmd.ExecuteReader(CommandBehavior.CloseConnection))
{
Cmd = new SqlCommand(CmdText, GetConn());
Cmd.CommandType = CmdType;
dt.Load(SqlSdr);
}
return dt;
}
/// <summary>
/// 执行带参数的查询
/// </summary>
/// <param name="CmdText">执行的SQL语句或存储过程</param>
/// <param name="parameters">(SQL语句或存储过程)类型</param>
/// <param name="CmdType"></param>
/// <returns>查询后的数据</returns>
public DataTable ExecuteQuery(string CmdText, SqlParameter[] parameters, CommandType CmdType)
{
DataTable dt = new DataTable();
Cmd = new SqlCommand(CmdText, GetConn());
Cmd.CommandType = CmdType;
Cmd.Parameters.AddRange(parameters);
using (SqlSdr = Cmd.ExecuteReader(CommandBehavior.CloseConnection))
{
dt.Load(SqlSdr);
}
return dt;
//DataTable dt = new DataTable();
//using (SqlSdr = Cmd.ExecuteReader(CommandBehavior.CloseConnection))
//{
// Cmd = new SqlCommand(CmdText, GetConn());
// Cmd.Parameters.AddRange(parameters);
// Cmd.CommandType = CmdType;
// dt.Load(SqlSdr);
//}
//return dt;
}
}
}
LoginDAL
这里的代码和配置文件息息相关,容易出现错误,容易出现的错误和解决办法都会放在文章最后奥。
DAL是为了实现IDAL层中的方法。对数据进行增删改查。
首先实例化一个sqlhelper,对数据库进行连接
然后声明一个sql参数的数组,将实体层中的参数值传给我们定义的参数中
然后再数据库中进行语句查询。
然后大家都返回到sqlhelper中的一个带参数查询的方法。
添加命名空间
using System.Data.SqlClient;
using System.Data;
using System.Configuration;
同时在DAL层里边要手动添加上面的引用
不然就会出现confurgationManager找不到的情况发生,
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 LoginDAL : IDAL.LoginIDAL
{
public DataTable selectUser(Entity.UserInfo UserInfo)
{
//实例化一个数据查询对象uu
SqlHelper sqlHelper = new SqlHelper();
//传参
SqlParameter[] sqlParams = { new SqlParameter("@UserID", UserInfo.UserId), new SqlParameter("@PassWord", UserInfo.PassWord) };
//数据库选择要查询的数据
string sql = @"SELECT* FROM Users WHERE UserName = @UserID AND PassWord = @PassWord";
//将参数放在语句中
DataTable table = sqlHelper.ExecuteQuery(sql, sqlParams, CommandType.Text);
return table;
}
}
}
Factory(工厂层)
:定义一个接口层,实现BLL层和DAL层之间的数据传递,工厂层用到了抽象工厂+反射+配置文件,实现了不同数据库之间的连接,反射+配置文件
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Configuration;
using System.Reflection;
namespace Factory
{
public class LoginFactory
{
string StrDB = System.Configuration.ConfigurationManager.AppSettings["DB"]; //接收来自配置文件的数据
public IDAL.LoginIDAL CreateUser()
{
string ClassName = StrDB + "." + "LoginDAL";//DAL层的类名
return (IDAL.LoginIDAL)Assembly.Load(StrDB).CreateInstance(ClassName);//反射+工厂的应用
}
}
}
BLL层:
执行特定的业务逻辑。
登陆界面BLL层:1.对D层返回来的信息进行判断,用户是否存在的问题,2.如果存在还会产生,返回更加具体的信息给U层
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data;
namespace BLL
{
public class LoginBLL
{
public bool UserBLL(Entity.UserInfo UserInfo)
{
Factory.LoginFactory fact = new Factory.LoginFactory(); //实例化工厂
IDAL.LoginIDAL idal = fact.CreateUser(); //调用工厂方法创建接口
DataTable table = idal.selectUser(UserInfo); //接收D层的返回值
bool flag;
if (table.Rows.Count == 0)
{
flag = false;
}
else
{
flag = true;
}
return flag;
}
}
}
Facade层,
提供一个门面,一个UI层可访问多个BLL层,提供一个外观层,好比提供了一个简单的入口,降低UI层和BLL层之间的耦合度。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Facade
{
public class LoginFacade
{
public Boolean SelectUser(Entity.UserInfo user)
{
bool flag;
BLL.LoginBLL userBLL = new BLL.LoginBLL(); //实例化一个b层的对象
flag = userBLL.UserBLL(user); //接收B层传过来的信号
return flag;
}
}
}
UI层:
1、提供一个登陆界面,获取用户输入的信息
2、给用户提供特定的业务功能
LoginUI
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 UI
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public void btnLogin_Click(object sender, EventArgs e)
{
if (txtUserID.Text.Trim() == "")
{
MessageBox.Show("用户名不能为空,请输入用户名", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
if (txtPassword.Text == "")
{
MessageBox.Show("密码不能为空,请输入密码", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
//try
//{
Entity.UserInfo user = new Entity.UserInfo();
user.UserId = Convert.ToInt32(txtUserID.Text.Trim());
user.PassWord = txtPassword.Text;
Boolean flag = false;
Facade.LoginFacade FLogin = new Facade.LoginFacade(); //实例化外观
flag = FLogin.SelectUser(user);//调用外观方法,返回给user
if (flag != false)
{
MessageBox.Show("登录成功");
this.Hide();//隐藏当前窗体
MessageBox.Show("登录成功", "恭喜", MessageBoxButtons.OK);
}
else
{
MessageBox.Show("密码或用户名错误");
}
//}
//catch (Exception)
//{
// throw;
//}
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void butCancel_Click_1(object sender, EventArgs e)
{
System.Environment.Exit(0);//这是最彻底的退出方式,不管什么线程都能被强制退出
//this.Close();//只是关闭当前窗口,若不是主窗体的话,是无法退出程序的,另外,若有托管线程(非主线程),也无法干净退出
//Application.Exit();//强制所有消息终止,退出所有的窗体,但是若有托管线程(非主线程),也是无法干净退出的;
//Application.ExitThread();//强制终止调用线程上的所有消息,同样面临其他线程无法正确退出的难题
}
private void butCancel_Click(object sender, EventArgs e)
{
}
}
}
配置文件
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
</startup>
<appSettings>
<add key ="DB" value ="DAL"/>
<add key ="strConn" value ="server =ZGY;Database= Login;User ID = sa; Password = 123456"/>
</appSettings>
</configuration>
这中间也遇见很多问题,大家可以去看这一系列的文章,一定有你遇见的那个错误呦!
System.NullReferenceException:“未将对象引用设置到对象的实例。” System.Configuration.ConnectionStringSettingsCollect配置系统未能初始化
System.IO.FileNotFoundException:“未能加载文件或程序集“loginDAL”或它的某一个依赖项。系统找不到指定的文件。”
源文件与模块生成时的文件不同,是否希望调试器使用它?如何解决
System.ArgumentNullException:“值不能为 null。 Arg_ParamName_Name”
一定要先好好学习配置文件的写法奥!