一开始接触七层和大家一样都很茫然,到目前为止花了一个多星期终于登陆成功了,多亏了几位小伙伴的无私帮助,没有他们就没有这篇博客。
我理解的七层是根据三层出发,逐步的增加需求和代码优化成型的,据说不必拘泥于一定要七层,只要在三层的框架上即可了。这里的七层分别是:
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); } } } }