不使用反射,“一行代码”实现Web、WinForm窗体表单数据的填充、收集、清除,和到数据库的CRUD

简介:

问题篇:

    昨天在CSDN看到这样一个帖子:“苦逼的三层代码”:

复制代码
采用传统的三层架构写代码,每个数据表都要定义一个实体对象,编写后台的时候,
Web层需要针对页面的用户输入逐个手动编写赋值到实体对象的各个属性,然后DAL层还要用SqlHelper 进行各个存储过程对应参数的实体赋值,
我的天呀,写几个表还好,多个表呢,
写的后台都没力气,
典型的苦逼代码工没营养,各位有啥好的处理方法或开发方式。。
复制代码

 

    看到跟帖,大部分都说使用ORM解决这个问题,但我觉得ORM还是没有解决贴主的几个问题:

  1. 每个数据表都要定义一个实体对象
  2. 页面的用户输入逐个手动编写赋值到实体对象的各个属性
  3. 表很多,代码重复量大,典型的苦逼代码工

    另外跟帖中也有不少上用动软的三层代码生成器,这个方法看似能够解决一部分问题,但必须使用代码生成器规定的那种三层结构,不利于灵活扩展,而且遇到业务稍复杂的情况,也不是代码生成器能够解决的问题。

    实际上,对于问题1,问题2,我们按照一定规则,使用反射是可以解决对象属性手工逐个赋值、取值的过程的,需要我们自己好好制定这个规则。这里我采用另外一种方案,不使用反射,“一行代码”实现Web、WinForm窗体表单数据的填充、收集、清除,和到数据库的CRUD,而秘诀就是对表单控件进行扩展。

原理篇:


    我们常用的表单控件主要有以下几个:

  • CheckBox、
  • DropDownList、
  • Label、
  • ListBox、
  • RadioButton、
  • TextBox

    我们对这些控件进行扩展,让它统一继承一个数据接口 IDataControl:

IDataControl接口

    稍后我们来讲这个接口的具体应用,下面,我们定义几个新的数据控件,来继承这个接口:
    注:下面以WinForm控件为例子,WebForm与之类似。

复制代码
public partial class DataCalendar : DateTimePicker, IDataControl
{
//数据日历控件
}

public partial class DataCheckBox : CheckBox, IDataControl
{
//数据复选框控件
}

public partial class DataDropDownList : ComboBox, IDataControl
{
//数据下拉选择框控件
}

public class DataLabel : Label, IDataControl
{
//数据标签控件
}

public partial class DataListBox : ListBox, IDataControl
{
//数据列表框控件
}

public partial class DataRadioButton : RadioButton, IDataControl
{
//数据选项按钮控件
}

public class DataTextBox : TextBox, IDataTextBox
{
//数据文本框控件
}
复制代码

    有了这些扩展的表单控件,我们只需要调用它的接口方法,进行赋值和取值:

DataTextBox dtb=new DataTextBox();
dtb.SetValue("text1");
string value=dtb.GetValue().ToString();//text1

    而在DataTextBox的这两个接口方法实现中,是不需要使用反射的:

复制代码
       public void SetValue(object obj)
        {
            if (obj == null || obj.ToString() == "")
            {
                this.Text = "";
                return;
            }
            //其它检测和格式控制代码略
            this.Text = obj.ToString().Trim();
        }

        public object GetValue()
        {
           //其它检测和格式控制代码略
            return this.Text.Trim();
        }
复制代码

    有了数据控件的这2个接口方法,我们对各种数据控件进行统一的数据收集、填充就很容易了,无非就是遍历一下窗体上面的数据控件,找到它们然后一个个处理即可,具体代码后面的实例会说到。


    既然说到表单数据的填充,将查询出来的数据集中哪个表的某个字段和哪个控件对应呢?
    这就用到了IDataControl接口的下面2个属性了:

string LinkProperty{get;set;}//对应字段名或者实体类的属性名
string LinkObject{get;set;}//对应表名或者实体类的类名称

    OK,有了IDataControl接口的这几个接口方法和属性,不使用反射,封装一下,“一行代码”实现Web、WinForm窗体表单数据的填充、收集、清除,和到数据库的CRUD,也就不是难事了。

实战篇:


    按照这个方法,我在PDF.NET开发框架中实现了本文标题说的功能,最近还做了一个简单的例子,大家可以去开源项目网站下载:
    项目网址: http://pwmis.codeplex.com 到下载页,选择“ PDF.Net_V4.6 WinForm 数据表单实例 ”这个下载链接即可。

    下面说说这个小程序的搭建过程,

1,新建项目

    首先创建一个WinForm程序项目,引入下面几个DLL类库:

 

2,添加数据控件到工具箱

    因为是WinForm项目,所以我们引用了PWMIS.Windows.dll, 它包含了我们需要的数据控件。
    找到该文件,将它拖入我们的工具箱:

    添加前,在工具箱中增加一个项:PDF.NET DataForm,然后在资源管理器中选择Windows数据控件组件的文件,将它“拖放”到刚才建立的 PDF.NET DataForm下面

 

    这是拖放后,添加PDF.NET Windows 数据控件成功后的工具箱样子。

 

3,添加数据窗体

    我们在主窗体上放置几个按钮和一个网格控件,以便增、删、改、查询数据:

    然后我们再新建立一个窗体 Form2 ,在上面放置几个我们需要的表单控件并设置好我们需要保存的表名称和对应的字段名称:

 

4,编写代码

    4.1,基础CRUD代码

    窗体建立好了,现在开始写代码,刚开始还没有数据库呢,这里我们是有Access数据库文件,方便我们测试,在“创建数据库”按钮事件里面写如下代码:

复制代码
private void btnCreateDB_Click(object sender, EventArgs e)
        {
            string dbpath = Application.StartupPath + "\\TEST.mdb";
            if (!File.Exists(dbpath))
            {
                //创建数据库文件
                PWMIS.AccessExtensions.AccessUility.CreateDataBase(dbpath);
                //创建表
                Access access = new Access();
              access.ConnectionString = "Provider=Microsoft.Jet.Oledb.4.0;Data Source=" + dbpath;

              PWMIS.AccessExtensions.AccessUility.CreateTable(access, new User());
                //配置连接
                PWMIS.AccessExtensions.AccessUility.ConfigConnectionSettings("AccessConn", dbpath);

                MessageBox.Show("创建数据成功!");
                this.btnInsert.Enabled = true;
                this.btnUpdate.Enabled = true;
                this.btnDelete.Enabled = true;
            }
            else
            {
                MessageBox.Show("数据库已经创建过了,如需重新创建,请先删除数据库文件。");
            }
           
        }
复制代码

    注意,我们并没有手工去创建数据表,而是利用事先定义好的PDF.NET实体类 User,在Access数据库中自动创建了一个数据表的:

 PWMIS.AccessExtensions.AccessUility.CreateTable(access, new User());

    User实体类的定义很简单,它内部指明了实体类将要映射到的表名和实体类属性映射的字段名:

User实体类定义

   实体类是事先手写好的,表结构是后来程序运行时创建的,这也算是PDF.NET的CodeFirst 功能吧!

    下面,写主窗体的数据加载代码:

 List<User> list = OQL.From<User>().Select().END.ToList<User>();
this.dataGridView1.DataSource =list;

    这里用上了PDF.NET框架的OQL扩展,一行代码查询数据,需要项目引用PWMIS.Core.Extensions.dll 以及
    using PWMIS.Core.Extensions;

 

    修改数据也是一行代码:

 User user = this.dataGridView1.CurrentRow.DataBoundItem as User;
 EntityQuery<User>.Instance.Update(user);

 

    重头戏在我们的Form2.cs 中,我们看看提交按钮里面,是怎么收集、更新表单数据的:

复制代码
 private void btnSubmit_Click(object sender, EventArgs e)
{
  //前面检查数据的代码略
   var ibCommandList = MyWinForm.Instance.AutoUpdateIBFormData(this.Controls);
}
复制代码

    就这一行代码就足够了,不需要使用任何实体类之类的,直接保存(Insert、Update)数据到数据库,框架会自动判断当前是新增还是修改,而根据就是看“主键数据控件”是否有值。


    如果要清除表单数据,重新录入数据也很简单:

  private void btnClear_Click(object sender, EventArgs e)
        {
            WinFormControlDataMap.ClearData(this.Controls);
        }

 

    4.2,多窗体之间的数据同步   

    在我们这个小例子中,表单窗体(Form2)的数据变化后(新增、修改),可以立即反应到主窗体(Form1)上,而不用主窗体去重新加载数据,这里就必须用到数据绑定集合:

 private BindingList<User> UserBindingList = new BindingList<User>();
//填充集合的代码,就是将数据从数据库查询出来,然后放到该集合中,代码略
this.dataGridView1.DataSource = UserBindingList;

    光有BindingList<T> 集合还不够,它的成员对象还必须实现“属性更改通知”接口INotifyPropertyChanged,而PDF.NET的实体类正好实现了该接口:

public abstract class EntityBase : INotifyPropertyChanged, IEntity, ICloneable
{
//... 略
}

    因此用PDF.NET的实体类来做WinForm、WPF、SL等窗体的数据Model是很合适的,适合在MVVM,MVP模式的项目中使用。

   

    下面,使用框架提供的表单数据收集功能,就很容易的将数据收集到实体类,然后同步更新主窗体的列表数据了,也是一行代码:

 Form1 form1 = this.Owner as Form1;
 User user = form1.GetUserByID(int.Parse(dlbUID.Text));
 //收集数据到实体类中
WinFormControlDataMap.CollectDataToEntityClass(user, this.Controls);

 

5,实例效果

最后,我们来看看这个功能的运行效果图:

增加数据,在新窗体中录入数据

 

单击按钮保存数据,主窗体列表中自动增加一行数据

 

新窗口先不关闭,修改下消费金额,确定,发现主窗口列表的数据被同步修改了。

整个过程没有从数据库去重新刷新数据到主窗口网格控件的,实现了多个窗体之见的数据同步。

 

 

 -----------分界线------------------------

 欢迎加入PDF.NET开发框架 开源技术团队

PDF.NET Ver4.6 开源稳定版发布



    本文转自深蓝医生博客园博客,原文链接:http://www.cnblogs.com/bluedoctor/archive/2013/03/28/2986580.html,如需转载请自行联系原作者



相关文章
docker快速部署OS web中间件 数据库 编程应用
通过Docker,可以轻松地部署操作系统、Web中间件、数据库和编程应用。本文详细介绍了使用Docker部署这些组件的基本步骤和命令,展示了如何通过Docker Compose编排多容器应用。希望本文能帮助开发者更高效地使用Docker进行应用部署和管理。
88 19
HTML5 Web IndexedDB 数据库详解
IndexedDB 是一种高效的浏览器存储方案,允许在本地存储大量结构化数据,支持索引和事务,适用于需要离线和大数据处理的应用。它由数据库、对象仓库等组成,通过键值对存储数据,确保数据一致性和完整性。本介绍展示了如何创建、读取、更新和删除数据,以及事务和错误处理的最佳实践。
Python Web应用中的WebSocket实战:前后端分离时代的实时数据交换
在前后端分离的Web应用开发模式中,如何实现前后端之间的实时数据交换成为了一个重要议题。传统的轮询或长轮询方式在实时性、资源消耗和服务器压力方面存在明显不足,而WebSocket技术的出现则为这一问题提供了优雅的解决方案。本文将通过实战案例,详细介绍如何在Python Web应用中运用WebSocket技术,实现前后端之间的实时数据交换。
174 0
ASP.NET Web Pages - 添加 Razor 代码
ASP.NET Web Pages 使用 Razor 标记添加服务器端代码,支持 C# 和 Visual Basic。Razor 语法简洁易学,类似于 ASP 和 PHP。例如,在网页中加入 `@DateTime.Now` 可以实时显示当前时间。
Web应用防火墙(WAF)与数据库应用防火墙有什么区别?
Web应用防火墙(WAF)专注于Web应用系统和网站的应用层防护,可有效应对OWASP Top 10等常见攻击,防止SQL注入、CC攻击等。而数据库应用防火墙则位于应用服务器与数据库之间,提供数据库访问控制、攻击阻断、虚拟补丁等高级防护功能,直接保护数据库免受攻击。两者分别针对Web层和数据库层提供不同的安全保护。
97 4
PHP与Ajax在Web开发中的交互技术。PHP作为服务器端脚本语言,处理数据和业务逻辑
本文深入探讨了PHP与Ajax在Web开发中的交互技术。PHP作为服务器端脚本语言,处理数据和业务逻辑;Ajax则通过异步请求实现页面无刷新更新。文中详细介绍了两者的工作原理、数据传输格式选择、具体实现方法及实际应用案例,如实时数据更新、表单验证与提交、动态加载内容等。同时,针对跨域问题、数据安全与性能优化提出了建议。总结指出,PHP与Ajax的结合能显著提升Web应用的效率和用户体验。
125 3
Flask学习笔记(六):基于Flask的摄像头-web显示代码(可直接使用)
这篇文章是关于如何使用Flask框架结合OpenCV库,通过电脑摄像头实现视频流在网页上的实时显示,并提供了单摄像头和多摄像头的实现方法。
259 2
Flask学习笔记(六):基于Flask的摄像头-web显示代码(可直接使用)
JSON与现代Web开发:数据交互的最佳选择
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于人阅读和编写,同时也便于机器解析和生成。它以文本格式存储数据,常用于Web应用中的数据传输,尤其是在客户端和服务器之间。
293 1
HTML5 Web IndexedDB 数据库常用数据存储类型
IndexedDB 支持多种数据存储类型,满足复杂数据结构的存储需求。它包括基本数据类型(如 Number、String、Boolean、Date)、对象(简单和嵌套对象)、数组、Blob(用于二进制数据如图像和视频)、ArrayBuffer 和 Typed Arrays(处理二进制数据)、结构化克隆(支持 Map 和 Set 等复杂对象),以及 JSON 数据。尽管不直接支持非序列化数据(如函数和 DOM 节点),但可以通过转换实现存储。开发者应根据具体需求选择合适的数据类型,以优化性能和使用体验。

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等