七层登陆——内有详细注释

简介: 七层登陆——内有详细注释

  一开始接触七层和大家一样都很茫然,到目前为止花了一个多星期终于登陆成功了,多亏了几位小伙伴的无私帮助,没有他们就没有这篇博客。

  我理解的七层是根据三层出发,逐步的增加需求和代码优化成型的,据说不必拘泥于一定要七层,只要在三层的框架上即可了。这里的七层分别是:


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);
            }
        }
    }
}
相关文章
|
6月前
|
开发工具
练习客户机地址控制 基于用户验证访问网站
练习客户机地址控制 基于用户验证访问网站
36 0
|
数据安全/隐私保护
CobaltStrike 流量隐藏
CobaltStrike 流量隐藏
96 0
|
2月前
|
数据挖掘
Emlog程序屏蔽用户IP拉黑名单插件
Emlog程序屏蔽用户IP拉黑名单插件
38 9
Emlog程序屏蔽用户IP拉黑名单插件
|
2月前
|
Ubuntu 应用服务中间件 数据库
Nginx配置:阻止非国内IP地址访问的设置方法
此外,出于用户隐私和法律合规性的考虑,应慎重考虑阻止特定国家或地区IP地址的决策。在某些情况下,这可能被视为歧视性或违反当地法律。
155 2
|
3月前
|
运维 安全 Shell
不要再内耗了,这么乱的IP地址,是时候用脚本了!
不要再内耗了,这么乱的IP地址,是时候用脚本了!
821 2
|
4月前
|
JavaScript
端口,自定义端口,端口写死,Vue如何自定义端口号,这里很关键,因为前后端数据交互,如果在WebConfig中写死了,端口号不对,会导致访问数据失败
端口,自定义端口,端口写死,Vue如何自定义端口号,这里很关键,因为前后端数据交互,如果在WebConfig中写死了,端口号不对,会导致访问数据失败
|
6月前
|
API
如何随机切换代理IP以避免被封禁?
如何随机切换代理IP以避免被封禁?
120 1
|
6月前
|
弹性计算 Kubernetes 中间件
基于 Traefik 如何实现向后转发自动去掉前缀?
基于 Traefik 如何实现向后转发自动去掉前缀?
|
Windows
网络基础 图解Windows系统下单网卡设置双IP访问不同网段的方法
网络基础 图解Windows系统下单网卡设置双IP访问不同网段的方法
860 0
|
存储 缓存 前端开发
一看就会的动态权限路由,还不赶紧学起来~
任何一个管理系统几乎都存在权限管理和动态路由,它一般和用户管理一同出现,可能有的人觉得这个东西每个管理系统都存在,觉得这个模块没有这么的重要;而在我的认知里,像权限控制和用户管理是管理系统中最重要的一
335 0