为初学者写ORM,ORM的原理及测试案例

发表于:2017-8-11 11:47

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:李福涛    来源:博客园

  一 、什么是ORM?
  概念: 对象关系映射(Object Relational Mapping,简称ORM,或O/RM,或O/R mapping),是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。
  详细介绍: 让我们从O/R开始。字母O起源于"对象"(Object),而R则来自于"关系"(Relational)。几乎所有的程序里面,都存在对象和关系数据库。在业务逻辑层和用户界面层中,我们是面向对象的。当对象信息发生变化的时候,我们需要把对象的信息保存在关系数据库中。
  当你开发一个应用程序的时候(不使用O/R Mapping),你可能会写不少数据访问层的代码,用来从数据库保存,删除,读取对象信息,等等。你在DAL中写了很多的方法来读取对象数据,改变状态对象等等任务。而这些代码写起来总是重复的。
  ORM解决的主要问题是对象关系的映射。域模型和关系模型分别是建立在概念模型的基础上的。域模型是面向对象的,而关系模型是面向关系的。一般情况下,一个持久化类和一个表对应,类的每个实例对应表中的一条记录,类的每个属性对应表的每个字段。
  ORM技术特点:
  1.提高了开发效率。由于ORM可以自动对Entity对象与数据库中的Table进行字段与属性的映射,所以我们实际可能已经不需要一个专用的、庞大的数据访问层。
  2.ORM提供了对数据库的映射,不用sql直接编码,能够像操作对象一样从数据库获取数据。
  二、反射以及Attribute在ORM中的应用。
  什么是反射?
  简单点吧,反射就是在运行时动态获取对象信息的方法,比如运行时知道对象有哪些属性,方法,委托等等等等。
  反射有什么用呢?
  反射不但让你在运行是获取对象的信息,还提供运行时动态调用对象方法以及动态设置、获取属性等的能力。
  反射在ORM中有什么用呢?
  我 这里所讨论的ORM实现是通过自定义Attribute的方式进行映射规则的描述的。但是我们并不知道具体哪个对象需要对应哪个表,并且这些对象是独立于 我们的ORM框架的,所以我们只能通过自定义Attribute来定义映射规则,然后通过反射来动态获取这些映射规则。
  (这里只简单说明下概念:具体如何实现过程请看第四项。)
  三、创建一个数据库表和表对应的实体model。
  传统的创建表和model实体的创建过程。
  1.创建数据库表
  create table TB_People
  (
       Pl_ID Int identity(1,1) primary key ,       
   
      PL_Age Int,
   
      Pl_Sex Nvarchar(4),
   
       Pl_LoginName nvarchar(30),
   
       Pl_TrueName  nvarchar(30),
   
       PL_Pwd  nvarchar(60)
  )
  2.根据表结构一般我们会创建如下model实体
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;
   
  namespace FuzhuKeji
  {
      public class M_People
      {
   
          string _Pl_ID;
   
          public string Pl_ID
          {
              get { return _Pl_ID; }
              set { _Pl_ID = value; }
          }
   
          int _PL_Age;
   
          public int PL_Age
          {
              get { return _PL_Age; }
              set { _PL_Age = value; }
          }
   
          string _Pl_Sex;
   
          public string Pl_Sex
          {
              get { return _Pl_Sex; }
              set { _Pl_Sex = value; }
          }
   
          string _Pl_LoginName;
   
          public string Pl_LoginName
          {
              get { return _Pl_LoginName; }
              set { _Pl_LoginName = value; }
          }
   
          string _Pl_TrueName;
   
          public string Pl_TrueName
          {
              get { return _Pl_TrueName; }
              set { _Pl_TrueName = value; }
          }
          string _PL_Pwd;
          public string PL_Pwd
          {
              get { return _PL_Pwd; }
              set { _PL_Pwd = value; }
          }
      }
  }
  现在看到了表结构 和model实体,那如何根据model实体映射出表的插入语句及结构呢?下面我们就介绍有model映射到数据库表。
  四、实体model如何映射出数据库表。
  上面简单介绍了反射以及Attribute在ORM中的应用,那如何通过这些进行映射出来的呢?
  方法一:
  看到 题纲三中的model实体了,下面我们就通过反射的方法来动态获取此映射规则:
  /// 
          ///  测试映射
          /// </summary>
          /// <param name="sender"></param>
          /// <param name="e"></param>
          private void button1_Click(object sender, EventArgs e)
          {
              M_People mp = new M_People();
              mp.PL_Age = 26;
              mp.Pl_ID = "001";
              mp.Pl_LoginName = "Test1";
              mp.PL_Pwd = "123";
              mp.Pl_Sex = "男";
              mp.Pl_TrueName = "张三";
              PropertyInfo[] infos = mp.GetType().GetProperties();
              string Message_shuxing1 = "";
              foreach (PropertyInfo info in infos)
              {
                  //获取属性并打印
                  Message_shuxing1 = Message_shuxing1 + (info.Name + ":" + info.GetValue(mp, null));
              }
              MessageBox.Show("这里看到可以获得属性名称和属性值
  (是不是对ORM有点慢慢明白了):"+Message_shuxing1);
              // 上面info.GetValue(mp, null)获得属性的值。
              //info.SetValue(mp, "XX", null);  赋值
          }
  测试效果图如下:
  是不是有点思路了,知道如何搞了,呵呵。
  看到红色部分了吗
  有感觉了没有:是不是和数据库的名称一样,而且还获得了值。为什么会出现这种情况呢?
  属性是来至那?--Model实体吧,属性的名称也是model实体属性名称吧。所以我们只要把属性的名称按某个规则定义就可以获得其对应的数据库字段名和类型。
  方法二:
  备注下:其实不只这种方法可以完成ORM的映射,而且还可以通过Attribute:
  Attribute中文翻译虽然也号称“属性”,但是她和对象的属性(Property)其实是完全不同的两概念。她是在运行时对对象或者对象属性、方法、委托等等进行描述的类,用于在运行时描述你的代码或者在运行时影响你的程序的行为。
  其 实我们在c#的编程中经常看到Attribute,只不过我们没有注意罢了。比如Main函数前的“[STAThread]”这个其实就是一个 Attribute。全程为[STAThreadAttribute]。另外指定类可序列化的[Serializable]等等。是不是都很熟悉啊?只不 过平时估计没有用到,所以没有注意罢了。
  既然Attribute是类,那么她的定义方法和类就没有两样了,唯一的不同就是自定义Attribute类必须继承于System.Attribute。
  那我们改下M_People实体的东西如下:
  下面我们来简单定义一个描述数据库字段信息的Attribute,在此类中我们采用更省略的方式,仅仅提供“字段名”,“字段类型”:
  public class DataFieldAttribute : Attribute
      {
          private string _FieldName;
          private string _FieldType;
          public DataFieldAttribute(string fieldname, string fieldtype)
          {
              this._FieldName = fieldname;
              this._FieldType = fieldtype;
          }
          public string FieldName
          {
              get { return this._FieldName; }
              set { this._FieldName = value; }
          }
          public string FieldType
          {
              get { return this._FieldType; }
              set { this._FieldType = value; }
          }
      }
  那我们把Mode更改下改为如下:
 public class M_People
      {
   
          string _Pl_ID;
          [DataFieldAttribute("Pl_ID", "Int")]
          public string Pl_ID
          {
              get { return _Pl_ID; }
              set { _Pl_ID = value; }
          }
          int _PL_Age;
          [DataFieldAttribute("PL_Age", "Int")]
          public int PL_Age
          {
              get { return _PL_Age; }
              set { _PL_Age = value; }
          }
          string _Pl_Sex;
          [DataFieldAttribute("Pl_Sex", "nvarchar")]
          public string Pl_Sex
          {
              get { return _Pl_Sex; }
              set { _Pl_Sex = value; }
          }
          string _Pl_LoginName;
          [DataFieldAttribute("Pl_LoginName", "nvarchar")]
          public string Pl_LoginName
          {
              get { return _Pl_LoginName; }
              set { _Pl_LoginName = value; }
          }
          string _Pl_TrueName;
          [DataFieldAttribute("Pl_TrueName", "nvarchar")]
          public string Pl_TrueName
          {
              get { return _Pl_TrueName; }
              set { _Pl_TrueName = value; }
          }
          string _PL_Pwd;
          [DataFieldAttribute("PL_Pwd", "nvarchar")]
          public string PL_Pwd
          {
              get { return _PL_Pwd; }
              set { _PL_Pwd = value; }
          }
      }
  通过自定义Attribute,我们定义了类属性和数据库字段的一一对应关系。
  那我们通过事件测试下方法案例:
  /// 
          /// 反射+Attribute 映射出数据库表
          /// </summary>
          /// <param name="sender"></param>
          /// <param name="e"></param>
          private void button2_Click(object sender, EventArgs e)
          {
              M_People mp = new M_People();
              mp.PL_Age = 26;
              mp.Pl_ID = "001";
              mp.Pl_LoginName = "Test1";
              mp.PL_Pwd = "123";
              mp.Pl_Sex = "男";
              mp.Pl_TrueName = "张三";
              PropertyInfo[] infos = mp.GetType().GetProperties();
              string Str_TestAtrrubute = "";
              object[] objDataFieldAttribute = null;
              foreach (PropertyInfo info in infos)
              {
                  objDataFieldAttribute = info.GetCustomAttributes(typeof(DataFieldAttribute), false);
                  if (objDataFieldAttribute != null)
                  {
                    Str_TestAtrrubute=Str_TestAtrrubute+(info.Name + "->数据库字段:"
   + ((DataFieldAttribute)objDataFieldAttribute[0]).FieldName)+" --------";
                  }
              }
              MessageBox.Show(Str_TestAtrrubute);
          }
  测试的效果图如下:
  哈哈,你是不是很想动手了啊?
  加油!下面我们就介绍如何实现插入语句的映射!

21/212>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号