不使用反射,“一行代码”实现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,如需转载请自行联系原作者



相关文章
|
22天前
|
缓存 NoSQL 关系型数据库
在Python Web开发过程中:数据库与缓存,MySQL和NoSQL数据库的主要差异是什么?
MySQL是关系型DB,依赖预定义的表格结构,适合结构化数据和复杂查询,但扩展性有限。NoSQL提供灵活的非结构化数据存储(如JSON),无统一查询语言,但能横向扩展,适用于大规模、高并发场景。选择取决于应用需求和扩展策略。
112 1
|
25天前
|
存储 Oracle 关系型数据库
Dataphin常见问题之想要周期执行任务如何解决
Dataphin是阿里云提供的一站式数据处理服务,旨在帮助企业构建一体化的智能数据处理平台。Dataphin整合了数据建模、数据处理、数据开发、数据服务等多个功能,支持企业更高效地进行数据治理和分析。
|
2天前
|
存储 关系型数据库 MySQL
如何处理爬取到的数据,例如存储到数据库或文件中?
处理爬取的数据,可存储为txt、csv(适合表格数据)或json(适合结构化数据)文件。若需存储大量数据并执行复杂查询,可选择关系型(如MySQL)或非关系型(如MongoDB)数据库。以MySQL为例,需安装数据库和Python的pymysql库,创建数据库和表,然后编写Python代码进行数据操作。选择存储方式应考虑数据类型、数量及后续处理需求。
7 1
|
3天前
|
SQL 关系型数据库 MySQL
关系型数据库插入数据的语句
使用SQL的`INSERT INTO`语句向关系型数据库的`students`表插入数据。例如,插入一个`id`为1,`name`为&#39;张三&#39;,`age`为20的记录:`INSERT INTO students (id, name, age) VALUES (1, &#39;张三&#39;, 20)。如果`id`自增,则可简化为`INSERT INTO students (name, age) VALUES (&#39;张三&#39;, 20)`。
5 2
|
3天前
|
SQL 存储 Oracle
关系型数据库查询数据的语句
本文介绍了关系型数据库中的基本SQL查询语句,包括选择所有或特定列、带条件查询、排序、分组、过滤分组、表连接、限制记录数及子查询。SQL还支持窗口函数、存储过程等高级功能,是高效管理数据库的关键。建议深入学习SQL及相应数据库系统文档。
6 2
|
9天前
|
人工智能 Cloud Native 算法
数据之势丨AI时代,云原生数据库的最新发展趋势与进展
AI与云数据库的深度结合是数据库发展的必然趋势,基于AI能力的加持,云数据库未来可以实现更快速的查询和决策,帮助企业更好地利用海量数据进行业务创新和决策优化。
数据之势丨AI时代,云原生数据库的最新发展趋势与进展
|
29天前
|
安全 Java 数据库连接
jdbc实现批量给多个表中更新数据(解析Excel表数据插入到数据库中)
jdbc实现批量给多个表中更新数据(解析Excel表数据插入到数据库中)
153 0
|
16天前
|
SQL 数据可视化 关系型数据库
轻松入门MySQL:深入探究MySQL的ER模型,数据库设计的利器与挑战(22)
轻松入门MySQL:深入探究MySQL的ER模型,数据库设计的利器与挑战(22)
|
16天前
|
存储 关系型数据库 MySQL
轻松入门MySQL:数据库设计之范式规范,优化企业管理系统效率(21)
轻松入门MySQL:数据库设计之范式规范,优化企业管理系统效率(21)
|
16天前
|
关系型数据库 MySQL 数据库
轻松入门MySQL:精准查询,巧用WHERE与HAVING,数据库查询如虎添翼(7)
轻松入门MySQL:精准查询,巧用WHERE与HAVING,数据库查询如虎添翼(7)

热门文章

最新文章