首页 > 技术文章 > 三层架构的搭建

dyxd 2014-12-14 00:31 原文

                今天又是我们上课需要停留片刻的尾声啦,顿时不知道该讲些什么啦,总起来讲,我们这周学习啦SQL Server语言以及ADO.net,学习啦这个也就意味着我们可以做些小程序啦,嘿嘿,我知道我现在做出来的程序还是错误居多,而且最重要的是一点都不严谨,我也不给自己找理由啦,还是自己练习的项目太少啦,接下来我们就会要项目啦,我想我会认真去做练习的,经过最近的程序发现我是很粗心啊,每次只要发生错误,都是没有自己解决啊,我感觉我能够帮其他的人找错,可是自己的错误却是难以发现啊,这是我现在最需要解决的问题啦,嘿嘿,说下今天吧,今天我们学习啦怎么创建三层架构以及三层架构的好处,另外,也做啦一下小例题啦,下面简单总结下我前天发的那个小程序,我今天把它用三成架构方法做的。

          一.三层架构的了解    

—                三层:UI(界面,User Interface)、BLL、DAL。Model是在三层之间进行数据传递的。在创建时间都是添加的类库,和数据库中的表一一对应的啦。UI层调用BLL、BLL调用DAL,数据用Model传递,UI不能直接调用DAL。Model不是一个层。在这里需要注意添加啦三层,就需要添加引用,UI层需要引用BLL层,BLL层需要引用DAL层,而不论是UI层,BLL层还是DAL层都要记得添加Model的引用啦。
          —DAL层只有SQL语句和数据处理,其他层一般不应该出现SQL语句以及和ADO.net相关的类。简单的BLL只是调用DAL,但是BLL不是打酱油的:数据校验应该放到BLL;BLL也会组合DAL成新的操作,比如ChangePassword。
—          DAL层中只有和数据库操作的代码。BLL中才有具体的逻辑
—          三层和所有代码写在一起的区别就像分工明确的麦当劳和收钱、做饭一起的卖煎饼的区别。
—          三层一般会比不分层慢,但是为了分工明确,这样的少量的性能下降是可以的,很多时候效率不是唯一追求的因素。
          下面就简单的写下我把前面写的登陆注册以及显示注册用户信息的那个小程序改写的三层架构啦。
          二.具体的实例以及遇到的问题
          先写一下搭建的三层的模式,UI层其实就是我们启动程序时间看到的界面所以先写一下BLL,DAL以及Model层啦。
public class Registerbll
    {
        Registerdal dal = new Registerdal();
        /// <summary>
        /// 登录
        /// </summary>
        /// <param name="name">用户名参数</param>
        /// <param name="pwd">密码参数</param>
        /// <returns></returns>
        public bool GetLogin(string name, string pwd)
        {
            return dal.GetLogin(name, pwd);
        }
        /// <summary>
        /// 获取用户的注册信息
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public bool GetReg( Registermodel model )
        {
            return dal.GetReg(model);
        }
        /// <summary>
        /// 获取所有注册用户的信息
        /// </summary>
        /// <returns></returns>
        public DataTable GetAllInfo()
        {
            return dal.GetAllInfo();
        }
        /// <summary>
        /// 删除选中用户的注册信息
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public bool GetUserInfo(int id)
        {
            return dal.GetUserInfo(id);
        }

        /// <summary>
        /// 修改注册用户的注册信息
        /// </summary>
        /// <param name="model">调用的model类</param>
        /// <returns></returns>
        public bool GetUpdateInfo( Registermodel model)
        {
            return dal.GetUpdateInfo(model);
        }
        /// <summary>
        /// 获取选中的用户的信息显示在更新页面上
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public SqlDataReader Get(int id)
        {
            return dal.Get(id);
        }
    }

                 

             这里就是创建的Bll层,其实我个人认为他就是一个过渡啦,只用来调用DAL层的方法啦,不过嘛桥梁的作用啦也是至关重要的啦,所以在这里我们注意啦。下面看下DAL层的代码:

public class Registerdal
    {
        /// <summary>
        /// 用聚合函数形式查询用户名和密码是否存在的方法(登录)
        /// </summary>
        /// <param name="name">用户名</param>
        /// <param name="pwd">密码</param>
        /// <returns></returns>
        public  bool GetLogin(string name,string pwd)
        {
            string  sql= "select count(*) from Register where username=@name and pwd=@pwd";
            SqlParameter[] sp ={
                                    new SqlParameter("@name",name),
                                    new SqlParameter("@pwd",pwd)
                                };
            int i = SqlHelper.ExcuteScalar(sql,sp);
            if (i > 0)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        /// <summary>
        /// 获取用户的注册信息
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public bool GetReg(Registermodel model)
        {
            //Id, UserName, Pwd, Age, Address, Phone, Sex
            string sql = "insert into Register(UserName,Pwd,Age,Address,Phone,Sex)values(@name,@pwd,@age,@address,@phone,@sex)";
            SqlParameter[]  sp ={
                                    new SqlParameter("@name",model.UserName),
                                    new SqlParameter("@pwd",model.Pwd),
                                    new SqlParameter("@age",model.Age),
                                    new SqlParameter("@address",model.Address),
                                    new SqlParameter("@phone",model.Phone),
                                    new SqlParameter("@sex",model.Sex),
                               };
            int i = SqlHelper.ExcuteNonQuery(sql,sp);
            if (i > 0)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        /// <summary>
        /// 获取所有用户的信息
        /// </summary>
        /// <returns></returns>
        public DataTable GetAllInfo()
        {
            string sql = "select * from  Register";
            return SqlHelper.ExcuteDataTable(sql);
        }
        /// <summary>
        /// 删除选中的用户的信息
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public bool GetUserInfo(int id)
        {
            string sql = "delete from register where Id=@id";
            SqlParameter sp = new SqlParameter("@id",id);
            if (SqlHelper.ExcuteNonQuery(sql, sp) > 0)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        /// <summary>
        /// 更改用户已注册过的信息
        /// </summary>
        /// <param name="id">用户注册信息的Id</param>
        /// <returns></returns>
        public bool GetUpdateInfo(Registermodel model)
        {
            string sql = "update Register set UserName=@name,pwd=@pwd,age=@age,sex=@sex,address=@address,phone=@phone where id=@id";
            SqlParameter[] sp ={ 
                                   new SqlParameter("@name",model.UserName),
                                   new SqlParameter("@pwd",model.Pwd),
                                   new SqlParameter("@age",model.Age),
                                   new SqlParameter("@sex",model.Sex),
                                   new SqlParameter("@address",model.Address),
                                   new SqlParameter("@phone",model.Phone),
                                   new SqlParameter("id",model.Id)
                               };
            if (SqlHelper.ExcuteNonQuery(sql, sp) > 0)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        /// <summary>
        /// 获取选中的用户的信息显示在更新页面上
        /// </summary>
        /// <param name="id">用户注册信息的Id</param>
        /// <returns>返回SqlDataReader类型</returns>
        public SqlDataReader Get(int id)
        {
            string sql = "select * from register where id=@id";
            SqlParameter sp = new SqlParameter("id",id);
            return SqlHelper.ExcuteSqlDataReader(sql,sp);
        }
    }

               

                 在DAL层中主要就是显示我们的方法的,例如,sql语句啦,以及传参啦这些方法是存储在我们的DAL层的,而在DAL层中我们的SqlHelper类也是存在这里的,这个我之前是不知道的,也甚是奇怪的,但是通过接下来的学习我知道啦,在DAL层是写方法的,而方法中存在与服务器的链接的啦,而把sq语句一般在DAL层中,这样调用起来SqlHelper也是很方便的啦,嘿嘿。下面就详细的说看下我的SqlHelper的类啦。

public class SqlHelper
    {
        /// <summary>
        /// 在配置文件中获取连接字符串
        /// </summary>
        static string connection = ConfigurationManager.ConnectionStrings["sql"].ToString();
        /// <summary>
        /// 连接数据库
        /// </summary>
        /// <param name="sql">sql语句</param>
        /// <param name="sp">sql语句中的参数</param>
        /// <returns>单行单列的值</returns>
        public static int ExcuteScalar(string sql, params SqlParameter[] sp)
        {
            using (SqlConnection conn = new SqlConnection(connection))
            {
                conn.Open();
                using (SqlCommand cmd = new SqlCommand(sql, conn))
                {
                    cmd.Parameters.AddRange(sp);
                    return Convert.ToInt32(cmd.ExecuteScalar());
                }
            }
        }
        /// <summary>
        /// 返回一个非查询的int类型
        /// </summary>
        /// <param name="sql">sql查询语句参数</param>
        /// <param name="sp">参数</param>
        /// <returns>受影响的行数</returns>
        public static int ExcuteNonQuery(string sql, params SqlParameter[] sp)
        {
            using (SqlConnection conn = new SqlConnection(connection))
            {
                conn.Open();
                using (SqlCommand cmd = new SqlCommand(sql, conn))
                {
                    cmd.Parameters.AddRange(sp);
                    return cmd.ExecuteNonQuery();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="sp"></param>
        /// <returns>DataTable类型的值</returns>
        public static DataTable ExcuteDataTable(string sql, params SqlParameter[] sp)
        {
            using (SqlConnection conn = new SqlConnection(connection))
            {
                conn.Open();
                using (SqlCommand cmd = new SqlCommand(sql, conn))
                {
                    cmd.Parameters.AddRange(sp);

                    DataTable dt = new DataTable();
                    using (SqlDataAdapter adapter = new SqlDataAdapter(cmd))
                    {
                        adapter.Fill(dt);
                    }
                    return dt;
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="sp"></param>
        /// <returns>返回一个SqlDataReader类型的值</returns>
        public static SqlDataReader ExcuteSqlDataReader(string sql, params SqlParameter[] sp)
        {
            SqlConnection conn = new SqlConnection(connection);
            conn.Open();
            using (SqlCommand cmd = new SqlCommand(sql, conn))
            {
                cmd.Parameters.AddRange(sp);
                SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
                return reader;
            }
        }
    }

 

                  在SqlHelp这个类中我们需要注意的有三点啦,第一,就是using的使用,使用using其实就是释放啦他的资源,感觉使用这个释放资源很方便的啦,而不需要在使用Dispose啦在程序最后;第二,就是最后一个方法在SqlConnection处就不能释放资源啦,SqlDataReader是每次仅仅读取一条信息,而且独占以资源,所以我们只需要在他完全读取完信息释放资源即可,所以就是使用的CommandBehavior.CloseConnection;第三,就是我自己的错误啦,之前记得很清楚params SqlParameter[] 是一个可以传多参同类型的,但是却忘记啦,也可以不传参数的,结果就重载啦一个方法,这就是我下次再写程序时间需要注意的地方啦。接下来说一下Model层的使用啦。

public class Registermodel
    {
        public int Id { set; get; }
        public string UserName { set; get; }
        public string Pwd { set; get; }
        public int  Age { set; get; }
        public string Address { set; get; }
        public string Phone { set; get; }
        public bool Sex { set; get; }
    }

                 

            我们只是刚接触程序啦,据说在在做项目前是要先创建表的啦,然后接下啦程序员就要先创建Model啦,而Model层创建时间一般就是和表的列一一对应的啦,这样我们在调用很多参数时间可以直接使用Model实例化对象啦,嘿嘿,这样会更简单啦。下面就说下我们的UI层啦,首先写一下UI层的登录页面啦。

                       

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        
        private void Form1_Load(object sender, EventArgs e)
        {

        }
        Registerbll bll = new Registerbll();
        /// <summary>
        /// 用户登录
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnlog_Click(object sender, EventArgs e)
        {
            string username = this.txtname.Text.Trim();
            string pwd = this.textBox1.Text.Trim();
            MD5 md5 = new MD5CryptoServiceProvider();   //创建MD5的加密对象
            byte[] bytes = Encoding.Default.GetBytes(pwd);//把要进行MD5加密的字符串转换成字节数组
            byte[] bytess = md5.ComputeHash(bytes);
            pwd = BitConverter.ToString(bytess).Replace("-", "");

            if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(pwd))
            {
                MessageBox.Show("用户名或者密码不能为空");
            }
            else
            {
                if (bll.GetLogin(username, pwd))
                {
                    MessageBox.Show("登录成功");
                    UserInfo info = new UserInfo();
                    info.Show();
                    this.Hide();
                }
                else
                {
                    MessageBox.Show("登录失败");
                }
            }
        }
        /// <summary>
        /// 跳转到注册信息界面
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnreg_Click(object sender, EventArgs e)
        {
            Register r = new Register();
            r.Show();
            this.Hide();
        }

        /// <summary>
        /// 发生鼠标点击事件则使文本框中的“用户名”三个字去掉
        /// 字体颜色为黑色
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void txtname_MouseEnter(object sender, EventArgs e)
        {
            this.txtname.Text = string.Empty;
            this.txtname.ForeColor = Color.Black;
        }
        /// <summary>
        /// 发生鼠标点击事件则使文本框中的“密码”两个字去掉
        /// 设置文本框密码信息样式为*显示
        /// 字体颜色为黑色
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void textBox1_MouseEnter(object sender, EventArgs e)
        {
            this.textBox1.Text = string.Empty;
            this.textBox1.PasswordChar = '*';
            this.textBox1.ForeColor = Color.Black;
        }
    }

                   

            这就是我使用三层写的登录界面的方法啦,嘿嘿,在这离需要注意的是我的密码通过一个方法加密成啦32位的密码啦,所以我们在登录时间也需要把密码加密啦,这样才能和注册时间存储在数据库的密码信息匹配,致使登陆成功啦。这个是一定要注意的啦。当然,如果还没用注册用户,那么我们可以直接点击注册按钮注册信息啦。下面我们就来看下UI层的注册页面啦。          

public partial class Register : Form
    {
        public Register()
        {
            InitializeComponent();
        }
          Registerbll bll = new Registerbll();
          Registermodel model = new Registermodel();
        /// <summary>
        /// 用户注册
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
          bool b = false;
          int  id = 0;
          public Register(int id,string name,string pwd,int age,bool sex,string address,string phone)
          {
              InitializeComponent();
              b = true;

              this.txtUserName.Text = name;
              this.txtpwd.Text = pwd;
              this.txtphone.Text = phone;
              string birthday = this.dateTimePicker1.Text;
              this.txtaddress.Text = address;
              this.id = id;
              if (sex.Equals("false"))
              {
                  this.radioButton2.Checked = true;
              }
              else
              {
                  this.radioButton1.Checked = true;
              }
          }

        private void btnreg_Click(object sender, EventArgs e)
        {
            string name = this.txtUserName.Text.Trim();
            string pwd = this.txtpwd.Text.Trim();
            MD5 md5 = new MD5CryptoServiceProvider();   //创建MD5的加密对象
            byte[] bytes = Encoding.Default.GetBytes(pwd);//把要进行MD5加密的字符串转换成字节数组
            byte[] bytess = md5.ComputeHash(bytes);
            pwd = BitConverter.ToString(bytess).Replace("-", "");

            string birthday=this.dateTimePicker1.Text;
            string address = this.txtaddress.Text;
            string phone = this.txtphone.Text;
            int age = DateTime.Now.Year - DateTime.Parse(birthday).Year;
            bool sex=this.radioButton1.Checked ? true : false;
            if (!b)
            {
                if (string.IsNullOrEmpty(name) && string.IsNullOrEmpty(pwd) && string.IsNullOrEmpty(birthday) && string.IsNullOrEmpty(address) && string.IsNullOrEmpty(phone))
                {
                    MessageBox.Show("注册信息为空");
                }
                else
                {
                    model.UserName = name;
                    model.Pwd = pwd;
                    model.Age = age;
                    model.Address = address;
                    model.Phone = phone;
                    model.Sex = sex;
                    if (bll.GetReg(model))
                    {
                        MessageBox.Show("注册成功");
                    }
                    else
                    {
                        MessageBox.Show("注册失败");
                    }
                }
            }
            else
            {
                model.UserName = name;
                model.Pwd = pwd;
                model.Age = age;
                model.Address = address;
                model.Phone = phone;
                model.Sex = sex;
                if (bll.GetUpdateInfo(model))
                {
                    MessageBox.Show("更新成功");
                }
                else
                {
                    MessageBox.Show("更新失败");
                }
            }
        }
    }

 

                  在注册页面我们需要注意一个非常重要的问题就是注册信息需要获取很多参数,而每次我们要传参需要写很多参数也是很麻烦的啦,那么这时间我们搭建的三层呢过架构中的model层就可以只用啦,我们只需要在UI层实例化一个对象,然后再传参时间使用对象model来传参,这样使用起来更加说明啦三层是很方便的啦,嘿嘿。下面还有一个UI层页面就是我们的显示注册用户信息的页面啦,再来总结一下啦。

public partial class UserInfo : Form
    {
        public UserInfo()
        {
            InitializeComponent();
        }
        Registerbll bll = new Registerbll();
        /// <summary>
        /// 心事注册过的用户的所有信息
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void UserInfo_Load(object sender, EventArgs e)
        {
            DataTable dt = bll.GetAllInfo();
            //像get什么的一般返回的是集合或者是datatable 
            //增删改一般返回bool类型的  
            foreach (DataRow item in dt.Rows)
            {
                string format = string.Format("{0},{1},{2},{3},{4},{5},{6}", item[0], item[1], item[2], item[3], item[4], item[5], item[6]);
                this.listBox1.Items.Add(format);
            }
        }
        /// <summary>
        /// 删除用户信息
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btndel_Click(object sender, EventArgs e)
        {
            string aa = this.listBox1.SelectedItem.ToString();
            string[] bb = aa.Split(',');
            int id = int.Parse(bb[0]);
            if (bll.GetUserInfo(id))
            {
                UserInfo form = new UserInfo();
                MessageBox.Show("删除成功");
            }
            else
            {
                MessageBox.Show("删除失败");
            }
        }
        /// <summary>
        /// 添加用户信息,在这里调用啦注册页面的方法
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void butadd_Click(object sender, EventArgs e)
        {
            Register r = new Register();
            r.Show();
            this.Hide();
        }
        /// <summary>
        /// 更改注册过的用户信息
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnupd_Click(object sender, EventArgs e)
        {
            string aa = this.listBox1.SelectedItem.ToString();
            string[] bb = aa.Split(',');
            int id = int.Parse(bb[0]);
            SqlDataReader reader = bll.Get(id);
            if (reader.HasRows)
            {
                if (reader.Read())                //在这里我需要判断是否读取啦
                {
                    string name = Convert.ToString(reader[0]);
                    string pwd = Convert.ToString(reader[1]);
                    int age = Convert.ToInt32(reader[2]);
                    bool sex = Convert.ToInt32(reader[3]) == 0 ? false : true;
                    string address = Convert.ToString(reader[4]);
                    string phone = Convert.ToString(reader[5]);
                    Register r = new Register(id,name, pwd, age, sex, address, phone);
                    r.Show();
                    r.Text = "修改";
                    r.Btnreg.Text = "更新";
                    this.Hide();
                }
            }
        }
    }

 

                     在这个页面中也是遇到啦很多问题,不知道是不是应该心存侥幸现在遇到的问题越多越好啦,然后我就可以发现更多的问题那,嘿嘿。在这个页面中首先是在做ListBox显示注册用户信息时间写的DAL和DLL层的返回值类型不对,导致在UI层使用DataTable类型的参数就不能便利出每一项啦,在这里我的返回值类型使用起来还是不熟悉啦,这个要多加练习的啦;然后就是在修改信息上面SqlDataReader reader = bll.Get(id);这个本来是先通过获取选中的用户的id来读取要修改的用户的注册信息,然后显示在页面上,进而我们进行更改的,但是那时间可能着急啦就直接调用啦更改用户信息的方法,结果数在Get方法中传啦所有参数(id,name, pwd, age, sex, address, phone),后来通过调试才发现的,然后就再次添加啦一个查询用户信息的方法,就是这样我们这个小程序才完成的。

                      这个就是winform的三层架构方法的创建啦,感觉还好啦,虽然在做这个时间遇到啦很多问题,我还是一个一个给它解决啦,感觉蛮高兴的啦,嘿嘿,还是感觉无论过程怎么样,最后完成啦,所有的一切都满是欢乐啦,嘿嘿。

推荐阅读