一开始接触七层和大家一样都很茫然,到目前为止花了一个多星期终于登陆成功了,多亏了几位小伙伴的无私帮助,没有他们就没有这篇博客。
我理解的七层是根据三层出发,逐步的增加需求和代码优化成型的,据说不必拘泥于一定要七层,只要在三层的框架上即可了。这里的七层分别是:
- 1、实体层(Entity):封装数据,为了能让数据在其他层次中运转流转。
- 2、数据访问层(DAL):针对数据的增添、删除、修改、查找,仅限于跟数据源打交道。
- 3、接口层(IDAL):接口层用来定义一个统一的接口,解除B层和D层的耦合。
- 4、工厂层(Factory):工厂来创建接口,返回接口,用到了抽象工厂+反射+配置文件,作用是灵活的实现数据库的连接,进一步解耦合。
- 5、业务逻辑层(BLL):主要负责一些逻辑判断和处理。
- 6、外观层(Facade):这里是给B层和U层提供统一的入口,层与层之间不直接产生联系,降低层之间的耦合度。
- 7:界面层(UI):收集用户信息,以及把用户的指令进行翻译。
代码:
1.Entity层
namespace Entity
{
public partial class UserInfo
{
public string userId { get; set; }//用户名字段
public string PWD { get; set; }//密码
public string Level { get; set; }//用户级别
}
}
2.IDAL层
using System.Data;
namespace IDAL
{
public interface IUserInfo
{
//用户登录
//DataTable是类型,selectUser是DataTable类型的方法名,括号里是参数
DataTable selectUser(Entity.UserInfo userInfo);
}
}
3.DAL层:
SQLHepler类
using System;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
namespace DAL
{
public class SQLHelper
{
//表示到 SQL Server 数据库的连接
private SqlConnection conn = null;
//表示要对 SQL Server 数据库执行的一个 T-SQL 语句或存储过程,简单来说方便执行SQL语句的命令,内有多个构造方法、属性和函数
private SqlCommand cmd = null;
//提供一种从 SQL Server 数据库中读取只进的行流的方式,只进是从上往下、从左往右按顺序读数据,只往前看,不走回头路。
private SqlDataReader sdr = null;
//ConfigurationManager:提供对客户端应用程序配置文件的访问
//AppSettings:获取当前应用程序默认配置的 AppSettingsSection 数据
string connStr = ConfigurationManager.AppSettings["ConnStr"];//配置文件中写了key="ConnStr"
public SQLHelper()//构造函数
{
conn = new SqlConnection(connStr);//实例化一个SQL连接对象conn
}
private SqlConnection GetConn()//定义一个打开SQL连接的方法
{
//SqlConnection.State 属性:最近在连接上执行网络操作时表示 SqlConnection 的状态。
//ConnectionState 枚举:描述与数据源连接的当前状态。
if (conn.State == ConnectionState.Closed)
{
conn.Open();//连接打开
}
return conn;
}
//CommandType 枚举:指定如何解释命令字符串
//try:尝试执行
//catch:try执行有误后运行
//finally:不管try是否执行有误,最后都会执行finally,之前执行的返回值在finally中不会变化
//SqlCommand(String, SqlConnection) :使用查询的文本和 SqlConnection 初始化 SqlCommand 类的新实例
//ExecuteNonQuery() 方法:对连接执行 Transact-SQL 语句,并返回受影响的行数
#region 执行不带参数的增删改查语句
public int ExecuteNonQuery(string cmdText, CommandType ct)
{
int res;
try
{
cmd = new SqlCommand(cmdText, GetConn());//实例化SqlCommand类的对象cmd
cmd.CommandType = ct;//cmd的sql命令类型为ct,ct需要外面传值
res = cmd.ExecuteNonQuery();//返回受影响的行的数目给res
}
catch (Exception ex)
{
throw ex;//try有错误就抛出
}
finally
{
if (conn.State == ConnectionState.Open)//最后,如果连接状态是开启的,就关闭
{
conn.Close();
}
}
return res;
}
#endregion
//SqlParameter 类 :表示 SqlCommand 的参数,数组类型表示有多个参数传入
//using(){}: 在退出{...}代码块后,会自动释放资源
//using()括起来的类必须实现接口
#region 执行带参数的增删改连接
public int ExecuteNonQuery(string cmdText, SqlParameter[] ps, CommandType ct)
{
int res;
using (cmd = new SqlCommand(cmdText, GetConn()))//执行完后自动释放资源
{
cmd.CommandType = ct;
cmd.Parameters.AddRange(ps);//向参数末尾添加元素
res = cmd.ExecuteNonQuery();
}
return res;//返回受影响的行数
}
#endregion
//SqlCommand.ExecuteReader 方法:将 CommandText 发送到 Connection,并生成 SqlDataReader
//CommandBehavior.CloseConnection:表示你关闭dataReader时,同时也把与它相关联的Connection连接也一起关闭
//DataTable.Load:用某个数据源的值填充 DataTable。 如果 DataTable 已经包含行,则从数据源传入的数据与现有行合并。
#region 执行不带参数的查询连接
public DataTable ExecuteQuery(string cmdText, CommandType ct)
{
DataTable dt = new DataTable();//实例化DataTable类型的对象dt
cmd = new SqlCommand(cmdText, GetConn());//实例化SqlCommand类型的对象cmd
cmd.CommandType = ct;
using (sdr = cmd.ExecuteReader(CommandBehavior.CloseConnection))
{
dt.Load(sdr);
}
return dt;
}
#endregion
#region 执行带参数的查询连接
public DataTable ExecuteQuery(string cmdText, SqlParameter[] ps, CommandType ct)
{
DataTable dt = new DataTable();
cmd = new SqlCommand(cmdText, GetConn());
cmd.CommandType = ct;
cmd.Parameters.AddRange(ps);//向参数末尾添加元素
using (sdr = cmd.ExecuteReader(CommandBehavior.CloseConnection))
{
dt.Load(sdr);
}
return dt;
}
}
}
UserInfoDal类:
using System.Data;
using System.Data.SqlClient;
namespace DAL
{
public class UserInfoDal:IDAL.IUserInfo
{
//1.实例化sqlHelper类
SQLHelper sqlhelper =new SQLHelper();
//2.实现了IDAL的接口,所以要重写里面的方法
public DataTable selectUser(Entity.UserInfo userInfo)
{
//3、构造SQL语句
//@后的内容完全按照字符串处理,不进行转义等操作
//userid和pwd是数据库里的字段
string sql = @"select Level from User_Info where UserId=@uid and PassWord=@pwd ";
//4、把用户名和密码分别实例化存在ps[]数组里
//userId和PWD是Entity层里的属性,userInfo是Entity类型的对象,所以可以使用
SqlParameter[] ps =
{
new SqlParameter("@uid",userInfo.userId),
new SqlParameter("@pwd",userInfo.PWD),
};
//5、新建一个数据表Dt去接收sql语句、用户名密码和、要执行一个文本的提示
//CommandType是SqlCommand对象的bai一个属性,用于指定执行动作的形式,它告诉程序接下来执行的是一个文本(text)、存储过程(StoredProcedure)还是表名shu称(TableDirect)
//ExecuteQuery:执行带参数的查询连接
DataTable dt = sqlhelper.ExecuteQuery(sql, ps, CommandType.Text);
//6、返回查询结果集
return dt;
}
}
}
配置文件:App.config:在UI层中
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="ConnStr" value="server=.; database=Charge_Sys;user id = sa ; pwd=123456" />
<!-- server是自己数据库的名字或者用.代表本地;把database,uid,pwd修改为与自己数据库对应的关系-->
<add key="DB" value="DAL" />
</appSettings>
</configuration>
4.Fatory:
using System.Reflection;//引入反射
namespace Factory//这里工厂方法的作用很小,仅仅用于实例化DAL.UserInfoDal这一个类型的对象,但如有需要,继续扩展就非常方便
{
public partial class UserInfoFactory
{
//1、ConfigurationManager--配置管理器
//2、AppSettings--配置文件,获取当前应用程序默认配置的 AppSettingsSection (配置文件段)数据
string StrDB = System.Configuration.ConfigurationManager.AppSettings["DB"];//从app.config配置文件中获取key值为"DB"的Value,StrDB=DAL
//反射加工厂的应用
//StrDB是程序集名称
//ClassName是程序集名称.类名
//此方法下返回的是DAL.UserInfoDal类型的实例
public IDAL.IUserInfo CreateUser()
{
string ClassName = StrDB + "." + "UserInfoDal";//UserInfoDal是DAL层的类名,ClassName=DAL.UserInfoDal
return (IDAL.IUserInfo)Assembly.Load(StrDB).CreateInstance(ClassName);
}
}
}
5.BLL:
using Entity;
using System.Data;
namespace BLL
{
public partial class UserInfoBll
{
public DataTable UserBLL(UserInfo userInfo)
{
//实例化工厂
Factory.UserInfoFactory fact = new Factory.UserInfoFactory();
//fact.CreateUser()返回的结果是DAL.UserInfoDal类型的对象,给了idal对象,DAL实现了IDAL接口,这里就实现代码复用
//fact=DAL idal=SQLHelper.DAL
IDAL.IUserInfo idal = fact.CreateUser();
//右边返回的是根据用户名和密码查询的用户等级的结果集,赋值给了左边
DataTable dt = idal.selectUser(userInfo);
//返回结果集
return dt;
}
}
}
6.Facade:
using System.Data;
namespace Facade//外观模式,这里只写了框架,原作用是想要选择用户类型
{
public partial class UserInfoFacade
{
//实例化B层
BLL.UserInfoBll userB = new BLL.UserInfoBll();
//自己写的方法
public DataTable SelectUser(Entity.UserInfo userInfo)
{
//UserBLL()方法:返回查询用户名和密码的结果集
DataTable dt = userB.UserBLL(userInfo);
//返回结果集
return dt;
}
}
}
7.UI:
using System;
using System.Data;
using System.Windows.Forms;
namespace UI
{
public partial class FormLogin : Form
{
public FormLogin()
{
InitializeComponent();
}
private void lblLogin_Click(object sender, EventArgs e)
{
try
{
if (txtUserID.Text.Trim() == "" || txtPWD.Text.Trim() == "")
{
MessageBox.Show("用户名或密码不能为空", "提示");
return;
}
//实例化外观层
Facade.UserInfoFacade flogin = new Facade.UserInfoFacade();
//实例化实体层(用于核对用户数据的数据与数据库中的数据)
Entity.UserInfo user = new Entity.UserInfo();
//接收用户名和密码
user.userId = txtUserID.Text.Trim();
user.PWD = txtPWD.Text.Trim();
//返回查询用户名和密码的结果集给dt
DataTable dt = flogin.SelectUser(user);
//判断记录集是否不为0
if (dt.Rows.Count != 0)
{
//有数据,隐藏本窗体
this.Hide();
//DialogResult.OK:对话框的返回值是 OK,通常是判断点击了“确定”的按钮
this.DialogResult = System.Windows.Forms.DialogResult.OK;
//Rows[0][0]——第一项中的第一个字段
if (dt.Rows[0][0].ToString().Trim() == "用户")
{
//实例化用户登录主窗体
FormManagerMainInfo main = new FormManagerMainInfo();
//显示主窗体
main.Show();
}
else if (dt.Rows[0][0].ToString().Trim() == "管理员" || dt.Rows[0][0].ToString().Trim() == "操作员")//显示相应的主窗体
{
FormManagerMainInfo formmaner = new FormManagerMainInfo();
formmaner.Show();
}
}
else//记录集为0
{
MessageBox.Show("用户名或密码错误,请重新输入", "提示");
txtUserID.Text = "";
txtPWD.Text = "";
}
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
}