首先是项目分层,三层架构分别为:
1、表现层(UI):通俗讲就是展现给用户的界面,即用户在使用一个系统的时候他的所见所得。
2、业务逻辑层(BLL):针对具体问题的操作,也可以说是对数据层的操作,对数据业务逻辑处理。
3、数据访问层(DAL):该层所做事务直接操作数据库,针对数据的增添、删除、修改、更新、查找等。
3个层次中,系统主要功能和业务逻辑都在业务逻辑层进行处理。
第一个窗体类是表示层,右键引用,勾选业务逻辑层和数据访问层
第二个类库是充血模型也是业务逻辑层,右键引用,引用数据访问层
第三个类库是帮助类也是数据访问层
接下来是连接数据库所需要的配置,首先进入窗体配置类App.config创建连接字符串
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="Constr" connectionString="Data Source=LAPTOP-24VJ6OS7;database=Commodity;uid=sa;pwd=123456"/>
</connectionStrings>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
</startup>
</configuration>
来到数据库帮助类SqlHelper.cs;当业务逻辑层调用sqlhelper类时,需要传入查询语句,查询会返回结果,增删改会返回影响行数,这个数据库帮助类可以应用到后续开发的所有数据库连接项目中
泛型反射类也可以应用到后续开发的所有项目中
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Commodity_System.Utility
{
public class SqlHelper
{
public static string Constr { get; set; }//数据库链接字符串
/// <summary>
/// 查询数据库
/// 1个参数传入sql语句
/// </summary>
/// <param name="cmdText">sql语句</param>
/// <returns>返回查询结果</returns>
public static DataTable ExecuteTable(string cmdText)
{
//var:推断数据类型,随机应变
using (var conn = new SqlConnection(Constr))//创建Sqlconnection对象并传入连接字符串ConStr
{
conn.Open();//打开数据库连接
SqlCommand cmd = new SqlCommand(cmdText, conn); //创建SqlCommand对象,并传入要执行的SQL查询语句cmdText和连接对象conn(管家)
SqlDataAdapter sda = new SqlDataAdapter(cmd);//创建SqlDataAdapter对象,并传入SqlCommand对象(推车)
DataSet ds = new DataSet();//创建DataSet对象,用于存储查询结果集(货车)
sda.Fill(ds);//将查询结果填充到DataSet中(推车的东西填充到货车)
return ds.Tables[0];//返回DataSet中的第一个表格(Tables[0]),即查询结果作为DataTable对象返回
}
}
/// <summary>
/// 增删改
/// </summary>
/// <param name="cmdText">sql语句</param>
/// <returns>返回受影响的行数</returns>
/// <exception cref="Exception">数据库操作失败</exception>
public static int ExecuteNonQuery(string cmdText, params SqlParameter[] sqlParameter)//操作数据库并返回受影响的行数
{
using (SqlConnection conn = new SqlConnection(Constr))//创建SqlConnection对象并传入连接字符串ConStr
{
conn.Open();//打开数据库连接
SqlCommand cmd = new SqlCommand(cmdText, conn);//创建SqlCommand对象,并传入要执行的SQL查询语句cmdText和连接对象conn
cmd.Parameters.AddRange(sqlParameter);
int rows = cmd.ExecuteNonQuery();//执行SQL命令并返回受影响的行数
if (rows == 0)
{
throw new Exception("数据库操作失败");
}
return rows;//返回受影响的行数
}
}
}
}
泛型反射类ToModel:
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Commodity_System.Utility
{
public static class ToModel
{
public static ToModel DataRowToModel<ToModel>(this DataRow dr) //ToModel被定义为了泛型
{
Type type = typeof(ToModel);
ToModel md = (ToModel)Activator.CreateInstance(type);
foreach (var prop in type.GetProperties())
{
if (dr[prop.Name] != DBNull.Value)//如果有null传入,就不做类型转换,防止报错
{
prop.SetValue(md, dr[prop.Name]);
}
}
return md;
}
}
}
接下来在业务逻辑层中的CAdmin.cs(业务逻辑层类的命名和数据库中的表单名字一致,文章最后附上数据库的表单命名)调用数据库帮助类,完成查询数据
using Commodity_System.Utility;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Commodity_System.Models
{
public class CAdmin
{
/// <summary>
/// 唯一识别Id
/// </summary>
public int Id { get; set; }
/// <summary>
/// 用户账号
/// </summary>
public string UserId { get; set; }
/// <summary>
/// 姓名
/// </summary>
public string name { get; set; }
/// <summary>
/// 用户密码
/// </summary>
public string password { get; set; }
/// <summary>
/// 性别
/// </summary>
public string sex { get; set; }
/// <summary>
/// 部门
/// </summary>
public string dept { get; set; }
/// <summary>
/// 授权码
/// </summary>
public string authorizationCode { get; set; }
/// <summary>
/// 获取CAdmin表所有的数据
/// </summary>
/// <returns>CAdmin表所有的数据</returns>
public static List<CAdmin> ListAll()
{
DataTable dt = SqlHelper.ExecuteTable("SELECT" +
"* from CAdmin");//调用帮助类传sql语句并用DataTable接收数据
List<CAdmin> cAdmins = new List<CAdmin>();//存入泛型列表
foreach (DataRow dr in dt.Rows)//这里用到了泛型反射
{
cAdmins.Add(dr.DataRowToModel<CAdmin>());
}
return cAdmins;//返回实体集
}
查询到的数据需要返回给表示层,在表示层中的Login.cs窗体中调用业务逻辑层的方法验证账号密码正确性,当点击登录按钮时触发的事件,前面总结过账号密码的隐藏,大体框架在上篇文章
public static CAdmin admin;//定义业务逻辑层窗体类,方便调用里面设置好的参数
private void btnLogin_Click(object sender, EventArgs e)
{
string userId = txtUserId.Text.Trim();
string psw = txtPsw.Text.Trim();
if (userId == "" || psw == "")
{
MessageBox.Show("用户名或密码不可为空", "提示");
}
else
{
int i = CAdmin.ListAll().FindIndex(t => t.UserId == userId && t.password == psw);//查询账号和相对应密码,获取其索引值(id)
if(i > -1)
{
admin = CAdmin.ListAll()[i];
DateTime date = DateTime.Now;
string day = date.ToShortDateString();
MessageBox.Show($"你好,{admin.name}\n今天是{day}","欢迎使用");
this.Owner.Visible = true;//owen表示作为此窗体的所有者的窗体,即父窗体
this.Owner.WindowState = FormWindowState.Normal;
//解除下面关联的事件
this.FormClosed -= new System.Windows.Forms.FormClosedEventHandler(this.Login_FormClosed);
Close();
}
else
{
MessageBox.Show("登陆失败,账号或密码错误", "提示");
}
}
}
private void Login_FormClosed(object sender, FormClosedEventArgs e)
{
Application.Exit();//退出程序
}
由于应用程序的主入口点设置的是Home.cs,只是隐藏起来了,先显示登录窗口,登录后显示主页
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 Commodity_System
{
public partial class Home : Form
{
public Home()
{
InitializeComponent();
}
private void Home_Shown(object sender, EventArgs e)
{
this.Visible = false;//每当窗体第一次显示时先隐藏
new Login().Show(this);//显示登录窗体
}
private void Home_FormClosing(object sender, FormClosingEventArgs e)
{
if (Visible)//只有当主页是可见状态时,才弹窗
{
if (MessageBox.Show("确定要关闭吗", "提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.Cancel)
{
e.Cancel = true;
}
}
}
登录后想在主页或其他窗体中调用登录时用的Id,UserId或姓名可以直接用以下方法直接拿,因为已经存入了Login的业务逻辑层窗体类admin中,这里只做提示
Id= Login.admin.Id
UserId= Login.admin.UserId
Name = Login.admin.name
数据库关系表:
最终效果:
登录功能(数据库连接)