C# Winform 三层架构

news2025/4/20 14:23:34

一、介绍

三层架构是 C# 桌面开发中比较常用的框架,是由 表示层(UI)、业务逻辑层(BLL)和数据访问层(DAL)三层架构组成,目的是为了 “高内聚,低耦合”。开发人员分工更明确,将精力更专注于应用系统核心业务逻辑的分析、设计和开发,加快项目的进度,提高了开发效率,有利于项目的更新和维护工作。

从三层架构可以看到,很类似于 Web 前端开发的 MVC 框架(视图View,模型Model,控制Contorller),但本质上也有不同的地方,比如都有视图(三层中叫 UI),Model ,三层中没有 Contorller,但 BLL 层和 Contorller 很类似。

一个项目如果用到了三层架构,这就必然要涉及到数据库,否则就没有必要用三层架构了,下面用一张图来表示,我百度看了很多的帖子,三层架构写的基本是有一些差异的,如果你看的资料和我写的不一样,那都是正常的。

另外,各种项目根据自己的需求来,并不是所有的PC软件都要求用三层架构,用程序集划分了代码的逻辑,才是三层架构真正的意义所在。现在网上已经有很多三层架构自动生成生成软件,可以根据 sqlserver 数据库一键生成,但基本都是2016年之前的软件了,后面也没再更新了,有需要的话可以去试试。

二、搭建三层架构

新建一个基于.net6 的 winform 项目,就以登陆功能作为演示,界面如下

1.Model

添加一个 .net6 类库,取名 Model(存储数据库字段),在里面添加一个类 UserInfo

代码:

namespace Model
{
    public class UserInfo
    {
        public string? UserName { get; set; }
        public string? Password { get; set; }
    }
}

一般你数据库用户表的所有字段都要写出来,主要是方便后面将查询的数据以类对象的方式返回回来。

2.DAL

添加一个 .net6 类库,取名 DAL(数据访问层),在里面添加一个类 SqlServerHelper,这里看你用的什么数据库,如果是 mysql 就用 mysql 的查询方式,c# 查询 mysql 和 sqlserver 数据库我都有写相关的教程,有需要的可以去看看

C# 连接 SqlServer 数据库_熊思宇的博客-CSDN博客_c连接sqlserver数据库

C# 连接 MySQL 数据库_熊思宇的博客-CSDN博客_c# mysql

SqlServerHelper.cs

using System.Data;
using System.Data.SqlClient;
 
namespace SqlServer
{
    internal class SqlServerHelper
    {
        /// <summary>
        /// 连接字符串
        /// </summary>
        private string strconn = string.Empty;
 
        public SqlServerHelper(string conn)
        {
            //读取配置文件
            //strconn = ConfigurationManager.AppSettings["Conn"].ToString();
            //strconn = ConfigurationManager.ConnectionStrings["ConnectionString"].ToString();
            strconn = conn;
        }
 
        /// <summary>
        /// 执行增删改SQL语句
        /// </summary>
        /// <param name="cmdText">SQL语句</param>
        /// <returns></returns>
        public int ExecuteNonQuery(string cmdText)
        {
            using (SqlConnection conn = new SqlConnection(strconn))
            {
                conn.Open();
                return ExecuteNonQuery(conn, cmdText);
            }
        }
 
        /// <summary>
        /// 执行增删改SQL语句
        /// </summary>
        /// <param name="conn">SqlConnection</param>
        /// <param name="cmdText">SQL语句<</param>
        /// <returns></returns>
        public int ExecuteNonQuery(SqlConnection conn, string cmdText)
        {
            int res;
            using (SqlCommand cmd = new SqlCommand(cmdText, conn))
            {
                cmd.CommandType = CommandType.Text;
                res = cmd.ExecuteNonQuery();
                if (conn.State == ConnectionState.Open)
                {
                    conn.Close();
                    conn.Dispose();
                }
            }
            return res;
        }
 
        /// <summary>
        /// 执行查询SQL语句
        /// </summary>
        /// <param name="cmdText">SQL语句</param>
        /// <returns></returns>
        public DataTable ExecuteDataTable(string cmdText)
        {
            using (SqlConnection conn = new SqlConnection(strconn))
            {
                conn.Open();
                return ExecuteDataTable(conn, cmdText);
            }
        }
 
        /// <summary>
        /// 执行查询SQL语句
        /// </summary>
        /// <param name="conn">SqlConnection</param>
        /// <param name="cmdText">SQL语句</param>
        /// <returns></returns>
        private DataTable ExecuteDataTable(SqlConnection conn, string cmdText)
        {
            DataTable dt = new DataTable();
            using (SqlCommand cmd = new SqlCommand(cmdText, conn))
            {
                cmd.CommandType = CommandType.Text;
                using (SqlDataAdapter sda = new SqlDataAdapter(cmd))
                {
                    sda.Fill(dt);
                    if (conn.State == ConnectionState.Open)
                    {
                        conn.Close();
                        conn.Dispose();
                    }
                }
            }
            return dt;
        }
 
        /// <summary>
        /// 执行查询SQL语句
        /// </summary>
        /// <param name="cmdText">SQL语句</param>
        /// <returns></returns>
        public DataTable ExecuteQuery(string cmdText)
        {
            using (SqlConnection conn = new SqlConnection(strconn))
            {
                conn.Open();
                return ExecuteQuery(conn, cmdText);
            }
        }
 
        /// <summary>
        /// 执行查询SQL语句
        /// </summary>
        /// <param name="conn">SqlConnection</param>
        /// <param name="cmdText">SQL语句</param>
        /// <returns></returns>
        public DataTable ExecuteQuery(SqlConnection conn, string cmdText)
        {
            DataTable dt = new DataTable();
            using (SqlCommand cmd = new SqlCommand(cmdText, conn))
            {
                using (SqlDataReader sdr = cmd.ExecuteReader())
                {
                    dt.Load(sdr);
                    sdr.Close();
                    sdr.Dispose();
                    if (conn.State == ConnectionState.Open)
                    {
                        conn.Close();
                        conn.Dispose();
                    }
                }
            }
            return dt;
        }
 
    }
}

如果确少对应的插件,可以自己装一个

新建一个类 UserHandle,用来查询数据库并返回数据

using System.Data;

namespace DAL
{
    public class UserHandle
    {
        private const string Conn = "server=.;dataBase=Test;uid=sa;pwd=123456";
        private static SqlServerHelper SqlServerHelpers = new SqlServerHelper(Conn);

        /// <summary>
        /// 用户登录
        /// </summary>
        /// <param name="name"></param>
        /// <param name="pwd"></param>
        /// <returns></returns>
        public static DataTable UserLogin(string name ,string pwd)
        {
            // SQL 语句根据个人情况来写
            string sql = "SELECT * FROM **********";
            return SqlServerHelpers.ExecuteDataTable(sql);
        }

        /// <summary>
        /// 获取用户的所有数据
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public static DataTable GetUserData(string name)
        {
            // SQL 语句根据个人情况来写
            string sql = "SELECT * FROM *************";
            return SqlServerHelpers.ExecuteDataTable(sql);
        }
    }
}

3.BLL

添加一个 .net6 类库,取名 BLL(业务逻辑层),由于后面的代码需要获取到数据库数据,所以要添加 DAL 和 Model 程序集 的引用

 添加一个类 LoginHandle

using DAL;
using Model;
using System.Data;

namespace BLL
{
    public class LoginHandle
    {

        /// <summary>
        /// 用户登录
        /// </summary>
        /// <param name="username">用户名</param>
        /// <param name="password">密码</param>
        /// <returns>是否能够登录</returns>
        public static bool UserLogin(string username, string password)
        {
            DataTable dataTable = UserHandle.UserLogin(username, password);
            if (dataTable == null || dataTable.Rows.Count == 0)
            {
                Console.WriteLine("查询的数据为空");
                return false;
            }

            string? user = dataTable.Rows[0][0].ToString();
            string? pwd = dataTable.Rows[0][1].ToString();
            if (username.Equals(user) && password.Equals(pwd))
            {
                return true;
            }

            return false;
        }

        /// <summary>
        /// 获取用户的所有数据
        /// </summary>
        /// <param name="username"></param>
        /// <returns></returns>
        public static UserInfo? GetUserData(string username)
        {
            DataTable dataTable = UserHandle.GetUserData(username);
            if (dataTable == null || dataTable.Rows.Count == 0)
            {
                Console.WriteLine("查询的数据为空");
                return null;
            }

            UserInfo userInfo = new UserInfo();
            userInfo .UserName = dataTable.Rows[0][0].ToString();
            userInfo .Password = dataTable.Rows[0][1].ToString();

            return userInfo;
        }
    }
}

4.UI

UI层就是创建项目时的 winform 项目,上面的工作完成后,配置大概就如下界面

接下来就添加项目引用了,将 BLL和 Model 层添加进来

给界面的登录按钮,添加点击事件

using BLL;
using Model;

namespace 三层架构Demo
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Button_Login_Click(object sender, EventArgs e)
        {
            string username = TextBox_UserName.Text;
            string password = TextBox_Password.Text;

            if(string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
            {
                Console.WriteLine("输入框不能为空");
                return;
            }

            //第一种方式
            bool result = LoginHandle.UserLogin(username, password);
            if(result)
            {
                Console.WriteLine("登录成功!");
                //跳转界面
                //....
            }
            else
            {
                Console.WriteLine("登录失败");
            }


            //第二种方式
            UserInfo? userInfo = LoginHandle.GetUserData(username);
            if(userInfo != null)
            {
                if(userInfo.Password != password)
                {
                    Console.WriteLine("密码不正确");
                    return;
                }

                Console.WriteLine("登录成功!");
            }
        }
    }
}

 源码:点击下载

结束

如果这个帖子对你有所帮助,欢迎 关注 、点赞 、留言

end

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/104758.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深度学习入门(六十五)循环神经网络——序列到序列学习(seq2seq)

深度学习入门&#xff08;六十五&#xff09;循环神经网络——序列到序列学习&#xff08;seq2seq&#xff09;前言循环神经网络——序列到序列学习&#xff08;seq2seq&#xff09;课件机器翻译seq2seq编码器-解码器细节训练衡量生成序列的好坏的BLEU总结教材1 编码器2 解码器…

为什么世界500强企业都要求员工必须学Python编程语言?

行业的更迭与进化并非虚无缥缈&#xff0c;它就发生在我们每天的生活中。当代最具竞争力的技能是什么&#xff1f;高盛、JP 摩根、麦肯锡等顶级公司纷纷给出了答案——编程。 今年新加入摩根大通的全部资产管理分析师必须强制学习编程语言&#xff0c;Python。 摩根大通资产管…

【Java异常】处理异常

一&#xff1a;异常的处理&#xff1a;抓抛处理 过程一&#xff1a;"抛":程序在正常执行的过程中&#xff0c;一旦出现异常&#xff0c;就会在异常代码处生成一个对应异常类的对象并将此对象抛出一旦抛出对象以后&#xff0c;其后的代码不再执行 过程二&#xff1a;&…

科班演员陶弈菱:北京电影学院的系统学习是我艺术道路上最大的底气

今天的记忆是带着海水的味道………”12月18日&#xff0c;在年末岁尾之际&#xff0c;徽风皖韵熏陶下成长的新生代演员陶奕菱再次来到海南三亚&#xff0c;受邀参加第四届海南岛国际电影节&#xff0c;迫不及待地跑向沙滩面朝大海&#xff0c;感受多姿多彩的魅力三亚。 优雅端…

在 React 中应用设计模式:策略模式

这篇文章是关于我们许多人在 React 和前端开发中遇到的一个问题&#xff08;有时甚至没有意识到这是一个问题&#xff09;&#xff1a;在不同的组件、钩子、实用程序等中实现了一段逻辑。 让我们深入了解问题的详细信息以及如何解决它。正如标题所暗示的&#xff0c;我们将使用…

DPDK-收包完整过程

本篇博客作为自己了解dpdk收包过程的一个记录。在写时发现已经有很多写DPDK收包过程的博客了&#xff0c;但还是决定自己写一遍。 DPDK收包分为两个阶段&#xff0c;首先是DMA将数据包从网卡搬运到内存&#xff0c;然后是调用dpdk提供的接口rte_eth_rx_burst去取。但是具体是怎…

Oracle和其他数据库有什么区别?从引号开始了解!

无论测试或者开发&#xff0c;对数据库的增删改查都是家常便饭。但有些小知识是经常被忽略&#xff0c;却又不能不去了解的&#xff0c;例如单引号和双引号的用法和区别&#xff0c;看完这一篇&#xff0c;你肯定会有收获。 首先我们要区别一个概念&#xff0c;即单引号(‘)和…

【Java语言】— Java基础02

1.数据类型 &#xff08;1&#xff09;数据类型的作用 数据类型就是约束变量存储数据的形式。 数据类型 变量名称初始值;&#xff08;2&#xff09;数据类型的分类 引用数据类型&#xff08;除基本数据类型之外的&#xff0c;如String&#xff09;基本数据类型:4大类8种。 …

如何从 0 开始学 Python 自动化测试开发(一)

本文是「如何从 0 开始学 Python 自动化测试开发」专题系列文章第一篇&#xff0c;适合零基础入门的同学。 作者方程老师&#xff0c;是前某跨国通信公司高级测试经理&#xff0c;目前为某互联网名企资深测试技术专家&#xff0c;也是霍格沃兹测试学院特邀讲师。有十余年大型电…

项目管理软件怎么选?只需要关注4点

项目管理有许多不同的风格&#xff0c;但无论如何管理项目&#xff0c;根据企业的当前需求和未来发展轨迹选择合适的项目管理软件都很重要。 虽然大多数优秀的项目管理软件都提供相似的功能&#xff0c;但没有两个平台是完全相同的。以下是企业在选择项目管理软件时应该考虑的…

【虹科新闻】虹科与SOSLAB正式建立合作伙伴关系

近日&#xff0c;虹科与SOSLAB正式建立合作伙伴关系&#xff0c;虹科将共同与SOSLAB开展亚太地区市场开发&#xff0c;聚焦于工业领域客户开拓&#xff0c;深入本地技术支持、测试与售后服务落地&#xff0c;为客户提供高效、可靠的激光雷达解决方案。 “虹科很高兴与SOSLAB合作…

锁等待超时

问题背景 今天测试同事发现项目里面大部分接口报错&#xff0c;把日志捞出来看了下出现大量的锁等待超时的错误。 Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transactionat sun.reflect.N…

MULLS: Versatile LiDAR SLAM via Multi-metric Linear Least Square论文阅读

1. 摘要 随着自动驾驶与移动建图的快速发展&#xff0c;实际项目中对现成的激光SLAM建图方案的需求也越来越强烈&#xff0c;并且要求解决方案适用于各种不同规格的激光雷达与各种复杂场景。因此&#xff0c;我们提出了MULLS&#xff0c;一种高效&#xff0c;低漂移&#xff0…

状态观测控制器设计与仿真验证

【无限嚣张&#xff08;菜菜&#xff09;】&#xff1a;hello您好&#xff0c;我是菜菜&#xff0c;很高兴您能来访我的博客&#xff0c;我是一名爱好编程学习研究的菜菜&#xff0c;每天分享自己的学习&#xff0c;想法&#xff0c;博客来源与自己的学习项目以及编程中遇到问题…

深度学习炼丹-数据处理和增强

前言一&#xff0c;Normalization 概述 1.1&#xff0c;Normalization 定义1.2&#xff0c;什么情况需要 Normalization1.3&#xff0c;Data Normalization 方法1.4&#xff0c;示例代码 二&#xff0c;normalize images 2.1&#xff0c;图像 normalization 定义2.2&#xff0c…

[XCTF]halo(2019护网杯)(难度2)

目录 前言 一、题目重述 二、解题思路 1.Base64解密 2.难以想到的异或运算 三、flag 总结 前言 注意&#xff01;攻防世界题目有误&#xff01;&#xff01;给出题目与原题不一样但是答案却和护网杯原题答案一样&#xff01;&#xff01; 一、题目重述 aWdxNDs0NDFSOz…

NVM Express Base Specification 2.0c - 2 Theory of Operation

The interface has the following key attributes: 在命令提交或完成路径中不需要非缓存/ MMIO寄存器读取;在命令提交路径中最多需要一个MMIO寄存器写或一个64B消息;支持多达65,535个I/O队列&#xff0c;每个I/O队列支持多达65,535个未完成的命令;优先级与每个I/O队列相关联&a…

【MySQL】基于InnoDB的数据库索引

文章目录前言1、索引引入2、索引语法2.1、创建索引2.2、查看索引2.3、删除索引2.4、案例引入3、索引结构3.1、概述3.2、引擎支持3.3、BTree3.4、Hash4、索引类别4.1、分类4.2、过程分析5、性能分析5.1、执行频率5.2、慢查询日志5.3、explain6、最左前缀原则7、索引失效7.1、范围…

2023年学一门IT技术的最佳选择就是软件测试!

互联网行业的不断发展&#xff0c;也增加了IT行业的就业机会&#xff0c;作为最适合零基础小白入行、门槛低的软件测试岗位来说&#xff0c;也受到越来越多转行者的关注&#xff0c;但是耳边依然充斥着各种关于这个行业不好的言论&#xff0c;诸如“行业饱和了&#xff0c;学完…

华为OD机试真题 Python 实现【最长连续方波信号】

目录 题目 思路 考点 Code 题目 输入一串方波信号,求取最长的完全连续交替方波信号,并将其输出,如果有相同长度的交替方波信号,输出任一即可, 方波信号高位用1标识,低位用0标识,如图: 说明: 1) 一个完整的信号一定以0开始然后以0结尾,即010是一个完整信号,但10…