• ORM映射框架总结代码生成器


    年前发布了一些文章,是关于.NET数据操作(点击查看)的。刚开始学习编程的时候,总感觉Java中的Hibernate 功能好强大,现在也不可否认它的确强大,特别是它在数据关系处理上,却是那样的让人称叹。

             当我那时还不知道.net 中的Linq的时候,一直想自己能够简单的写个ORM映射框架。去年花费了几个月的业务时间终于算是整出来了,一些基本操作都能够实现了,自己号称从数据库操作冗余代码中解脱出来,其实自己很天真,那个框架是多么的不完善。总结了一下,最近超体力透支的准备将其扩展,同时也碰到了一下很头疼的问题,那就是那个实体的生成。

    每次都要去受到生成那些代码,是一件非常消耗体力的事情,而李老师的动软代码生成器也不能生成我想要的代码,于是自己查阅了一些资料,写了一个自己需要的代码生成器,在此共享一下。

    1.效果图 

    看东西总是效果图比较直接,首先看看这个代码生成器的效果图。

     

    2.生成代码规则

     因为这个实体是服务于自己写的框架,所以自己也规定了一些规则。相关文件可以参考本人 ORM映射解析 相关系列文章。这些文章代码太多,似乎有些难懂。这个框架正在升级版本中,后续继续讲解。

    对于数据操作,制定了一系列特性,用于修饰实体类和实体的相关属性,这些特性能够很好的描述对象和数据库表之间的关系 。这个就是数据库和对象之间的桥梁,用特性建立他们之间的关系,用对象管理关系,用对象管理数据库表。

    规则一: 

    [TableAttribute(DBName = "StuDB", Name = "Student", PrimaryKeyName = "Id", IsInternal = false)]

    这个特性是用于描述实体类的,映射实体类和数据库表之间的关系。这里的特性定义暂不做讲解,将在后续文章中讲解。 先看看如下表格对此特性的描述

    属性

    作用

    name

    数据表名

    该字段用于描述数据库对应表的名称,而且该值最好与

    数据表名大小写相同。该值有两种类型。

    (1)直接自定表的名称

    (2)[数据库名].[表名]

    如果是(2)情况,则需要分割字符串,将数据库名分割

    出来赋值给dBName

    dBName

    数据库名

    该字段用于描述数据的名称,而且该值最好与

    数据库名称大小写相同

    primaryKeyName

    主键字段名

    该实体必须指定对应数据库表的主键

    information

    表实体描述信息

    isInternal

    表实体是否国际化

    默认为false

    version

    表实体版本号

    默认为 "V1.0"

    规则二: 

    [ColumnAttribute(Name = "Id", IsPrimaryKey = true, AutoIncrement = true, DataType = DataType.Int, CanNull = false)   该特性是用于修饰属性的,该特性描述了数据库字段和实体对象属性之间的关系。

    name

    表字段名称,该属性的值最好与数据表的字段的名称相同。

    该字段的值有两种格式:

     (1) [表名].[字段名]

     (2) [字段名]

      如果该字段的值为(1)情况,则应分割字符串,将字段名

      赋值给name属性,表名则赋值给tableName

    tableName

    表字段对应表名称

    该值是可以为空的,如果name的值的情况满足(1)情况,

    可以分割的值赋值给该属性

    dataType

    表字段的数据类型

    该属性的类型为自定义类型,该字段是一个枚举类型。

    该字段描述了25中数据类型

    length

    表字段的长度

    控制该字段对应的数据库表字段值的最大长度

    可以不指定该值

    canNull

    表字段是否可以为空

    true 可以为空

    false 不能为空

    defaultValue

    表字段的默认值

    默认情况为null

    isPrimaryKey

    表字段是否为主键

    true  为主键

    false 不是外键

    autoIncrement

    表字段是否为自动增长列

    true  是自动增长列

    false 不是自动增长列

    isUnique

    确定某个字段是否唯一

    true  是唯一的

    false 不是唯一

    regularExpress

    表字段的匹配规则

    字段匹配规则正则表达式

    isForeignKey

    表字段是否为外键

    true  为外键

    false 不是外键

    foreignTabName

    表字段外键对应的表名称

    如果isForeignKey true,则需要指定其值

    information

    表字段的描述信息

     

     


    3. 生成代码展示

    代码

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using CommonData.Entity;
     6 using CommonData.Model.Core;
     7 
     8 namespace Entity.School
     9 {
    10     [TableAttribute(DBName = "StuDB", Name = "Student", PrimaryKeyName = "Id", IsInternal = false)]
    11     public class Student:BaseEntity
    12     {
    13         public Student()
    14         { 
    15         }
    16 
    17         private int id;
    18 
    19         [ColumnAttribute(Name = "Id", IsPrimaryKey = true, AutoIncrement = true, DataType = DataType.Int, CanNull = false)]
    20         public int Id
    21         {
    22             get { return id; }
    23             set { id = value; }
    24         }
    25 
    26         private string sname;
    27 
    28         [ColumnAttribute(Name = "Sname", IsPrimaryKey = false, AutoIncrement = false, DataType = DataType.Nvarchar, CanNull = false)]
    29         public string Sname
    30         {
    31             get { return sname; }
    32             set { sname = value; }
    33         }
    34 
    35         private string sex;
    36 
    37         [ColumnAttribute(Name = "Sex", IsPrimaryKey = false, AutoIncrement = false, DataType = DataType.Nvarchar, CanNull = false)]
    38         public string Sex
    39         {
    40             get { return sex; }
    41             set { sex = value; }
    42         }
    43 
    44         private DateTime birthday;
    45 
    46         [ColumnAttribute(Name = "Birthday", IsPrimaryKey = false, AutoIncrement = false, DataType = DataType.Datetime, CanNull = false)]
    47         public DateTime Birthday
    48         {
    49             get { return birthday; }
    50             set { birthday = value; }
    51         }
    52 
    53         private string addr;
    54 
    55         [ColumnAttribute(Name = "Addr", IsPrimaryKey = false, AutoIncrement = false, DataType = DataType.Nvarchar, CanNull = false)]
    56         public string Addr
    57         {
    58             get { return addr; }
    59             set { addr = value; }
    60         }
    61 
    62         private string telephone;
    63 
    64         [ColumnAttribute(Name = "Telephone", IsPrimaryKey = false, AutoIncrement = false, DataType = DataType.Nvarchar, CanNull = false)]
    65         public string Telephone
    66         {
    67             get { return telephone; }
    68             set { telephone = value; }
    69         }
    70     }
    71 }
    72 

      其实生成代码很简单,写文件而已。生成一个实体也很简答,查询一下数据库知道数据表有多少个字段以及字段的名称即可。但是这里的实体不仅仅是我们看到的实体那么简单。这个实体描述了数据库表,数据库表字段的属性信息。要生成这样的一个实体并不简单。当大多数人去研究对数据库操作的时候,眼前要实现的功能迷惑了我们的心智,数据操作还有更多不为人知的东西却被我们悄悄的丢弃。下面我们简单的介绍几个数据库系统操作的sql语句,让我们也感受一下代码生成器的神秘之处。

    (a).获取数据库服务名

     代码

     1 public static IList<string> GetSource()
     2         {
     3             DataTable tableDataSouce=SqlClientFactory.Instance.CreateDataSourceEnumerator().GetDataSources();
     4             IList<string> listServerName = new List<string>();
     5             for (int i = 0; i < tableDataSouce.Rows.Count; i++)
     6             {
     7                 listServerName.Add(tableDataSouce.Rows[i]["ServerName"].ToString()+"\\"+tableDataSouce.Rows[i]["InstanceName"].ToString());
     8             }
     9             return listServerName;
    10         }

     我们是使用客户端连接数据库服务器的,在System.Data.SqlClient中为为我们提供了一个实例,来获得服务器的名称和数据库实例。SqlClientFactory.Instance.CreateDataSourceEnumerator().GetDataSources() 这个方法能够返回一个DataTable实例,这个DataTable 中就包含了数据库服务名称和数据库实例。tableDataSouce.Rows[i]["ServerName"]就是客户端能够获取的服务名称,tableDataSouce.Rows[i]["InstanceName"]能够获取数据库实例的名称。

    (b).获得服务器上的数据库名称

    代码
     1 public static DataTable GetDataBase(string servername, string userid, string password)
     2         {
     3             using (IDbProvider provider = new SqlProvider())
     4             {
     5                 if (string.IsNullOrEmpty(userid) && string.IsNullOrEmpty(password))
     6                 {
     7                     provider.ConnectionString = "server=" + servername + ";database=master;Integrated Security=true";
     8                 }
     9                 else
    10                 {
    11                     provider.ConnectionString = "server=" + servername + ";database=master;uid=" + userid + ";pwd=" + password;
    12                 }
    13                 IBaseHelper baseHelper = new BaseHelper();
    14                 string sql="select name from sysdatabases where dbid>4";
    15                 return baseHelper.ExecuteTable(provider,sql);
    16             }
    17         }

      sql server 数据库我们不能忘却的一个数据库那就是master数据库,这个数据库包含了整个服务所包含的大多数信息,其中该数据库中的SysDatabases表就记录了该服务中存在的数据库名称以及相关信息。该表中的子弹dbid记录的是数据库的编号。当dbid>4的时候查询得到的就是用户数据库,那小于4的就不用多说了,就是我们常见的那四个系统数据库了。

    (c).查询数据库中的表

    代码
     1 public static DataTable GetTable(string servername, string databasename, string userid, string password)
     2         {
     3             using (IDbProvider provider = new SqlProvider())
     4             {
     5                 if (string.IsNullOrEmpty(userid) && string.IsNullOrEmpty(password))
     6                 {
     7                     provider.ConnectionString = "server=" + servername + ";database="+databasename+";Integrated Security=true";
     8                 }
     9                 else
    10                 {
    11                     provider.ConnectionString = "server=" + servername + ";database="+databasename+";uid=" + userid + ";pwd=" + password;
    12                 }
    13                 IBaseHelper baseHelper = new BaseHelper();
    14                 string sql = "select name from sysobjects where type='U'";
    15                 return baseHelper.ExecuteTable(provider, sql);
    16             }
    17         }

      在每个数据库中都隐含了一个表,我们在建立数据表的时候通常写一句代码 

              if exists (select * from sysobjects where name='tablename') 写到这里不多大家明白了,表Sysobjects,这张表存储了该数据库中其他表的表信息。我们可以查询该张表得到该数据库的其他表信息。当然如果要查询用户表我们就得指定条件了。该表的字段type='U' 的时候就说明该张表是用户表。


     (d). 字段属性查询

    代码

     1 select 
     2 syscolumns.name as ColName ,
     3 systypes.name as ColTypeName ,
     4 syscolumns.length,
     5 sys.extended_properties.value as Mark ,
     6 AllowNull=case
     7 when (syscolumns.isnullable=0then 'false'
     8 else 'true' end,
     9 IsAuto=case
    10     when ( (SELECT COLUMNPROPERTYOBJECT_ID('TabRule'),syscolumns.name,'IsIdentity')) =1)
    11         then 'true' 
    12     else 'false' end,
    13 IsPK = Case 
    14     when exists ( select 1 from sysobjects inner join sysindexes on sysindexes.name = sysobjects.name inner join sysindexkeys on sysindexes.id = sysindexkeys.id and sysindexes.indid = sysindexkeys.indid where xtype='PK' and parent_obj = syscolumns.id and sysindexkeys.colid = syscolumns.colid )  
    15         then 'true' 
    16     else 'false' end ,
    17 IsIdentity = Case syscolumns.status when 128 then 'true' else 'false' end from syscolumns inner join systypes on ( syscolumns.xtype = systypes.xtype and systypes.name <>'_default_' and systypes.name<>'sysname' ) left outer join sys.extended_properties on ( sys.extended_properties.major_id=syscolumns.id and minor_id=syscolumns.colid ) where syscolumns.id = (select id from sysobjects where name='TabRule')  
    18 order by syscolumns.colid

      上面的这段sql语句可以查询出表字段的详细信息。包括字段名称,数据类型,长度,主键等等信息。查询该信息是为了在生成实体的时候,匹配相应的规则。自己查阅了好多资料终于写出了这段sql语句。后来才发现其实网上有很多这种相关的代码,浪费了我好多时间。不过能写出了就好了。ColName 记录的是字段名称,ColTypeName记录的是字段类型,length记录了字段的长度,Mark记录了字段的描述信息,AllowNull 记录字段是否允许为空,IsAuto记录字段是否自动增长,IsPK 记录字段是否为主键,IsIdentity记录知道是否为标识列

    代码
     1 string servername = ddlServerName.SelectedItem.ToString();
     2             string tableName = ddlTableNames.SelectedItem.ToString();
     3             string connection = null;
     4             SqlConnection con = null;
     5             if (rbWindows.Checked)
     6             {
     7                 connection = "server=" + servername + ";database=" + ddlDataBaseName.SelectedItem.ToString() + ";Integrated Security=true";
     8             }
     9             else
    10             {
    11                 connection = "server=" + servername + ";database=master;uid=" + txtUserName.Text + ";pwd=" + txtPassword.Text;
    12             }
    13 
    14             try
    15             {
    16                 con = new SqlConnection(connection);
    17                 con.Open();
    18                 StringBuilder sb = new StringBuilder("select ");
    19                 sb.Append("syscolumns.name as ColName ,");
    20                 sb.Append("systypes.name as ColTypeName ,");
    21                 sb.Append("syscolumns.length,");
    22                 sb.Append("sys.extended_properties.value as Mark ,");
    23                 sb.Append("IsAuto=case ");
    24                 sb.Append("when ( (SELECT COLUMNPROPERTY( OBJECT_ID('" + tableName + "'),'syscolumns.name','IsIdentity')) =1) ");
    25                 sb.Append("then 'true' else 'false' end,");
    26                 sb.Append("AllowNull=case ");
    27                 sb.Append("when (syscolumns.isnullable=0) then 'false' ");
    28                 sb.Append("else 'true' end,");
    29                 sb.Append("IsPK = Case ");
    30                 sb.Append(" when exists ( select 1 from sysobjects inner join sysindexes on sysindexes.name = sysobjects.name inner join sysindexkeys on sysindexes.id = sysindexkeys.id and sysindexes.indid = sysindexkeys.indid where xtype='PK' and parent_obj = syscolumns.id and sysindexkeys.colid = syscolumns.colid )  ");
    31                 sb.Append(" then 'true' else 'false' end ,");
    32                 sb.Append(" IsIdentity = Case syscolumns.status when 128 then 1 else 0 end from syscolumns inner join systypes on ( syscolumns.xtype = systypes.xtype and systypes.name <>'_default_' and systypes.name<>'sysname' ) left outer join sys.extended_properties on ( sys.extended_properties.major_id=syscolumns.id and minor_id=syscolumns.colid ) where syscolumns.id = (select id from sysobjects where name='"+tableName+"')  ");
    33                 sb.Append(" order by syscolumns.colid");
    34                 SqlCommand command = new SqlCommand(sb.ToString(), con);
    35                 SqlDataReader reader = command.ExecuteReader();
    36                 ddlTableNames.Items.Clear();
    37                 StringBuilder sbCode = new StringBuilder("");
    38                 sbCode.Append("using System;\n");
    39                 sbCode.Append("using System.Collections.Generic;\n");
    40                 sbCode.Append("using System.Linq;\n");
    41                 sbCode.Append("using System.Text;\n");
    42                 sbCode.Append("using CommonData.Entity;\n");
    43                 sbCode.Append("using CommonData.Model.Core;\n");
    44                 sbCode.Append("\n");
    45                 sbCode.Append("namespace Entity\n");
    46                 sbCode.Append("{\n");
    47 
    48                 sbCode.AppendFormat("\t[Serializable]\n");
    49                 sbCode.AppendFormat("\t[TableAttribute(DBName = \"\", Name = \"{0}\", PrimaryKeyName = \"@PrimaryKeyName\", IsInternal = false)]\n", tableName);
    50                 sbCode.AppendFormat("\tpublic class {0}:BaseEntity\n", tableName.FirstToUpper(tableName));
    51                 sbCode.Append("\t{\n");
    52                 sbCode.AppendFormat("\t\tpublic {0}()\n", tableName.FirstToUpper(tableName));
    53                 sbCode.Append("\t\t{\n");
    54                 sbCode.Append("\t\t}\n\n");
    55                 string pkName="Id";
    56                 while (reader.Read())
    57                 {
    58                     if(reader["IsPK"].ToString()=="true")
    59                     {
    60                         pkName=reader["ColName"].ToString();
    61                     }
    62                     sbCode.AppendFormat("\t\tprivate {0} {1};\n", GetType(reader["ColTypeName"].ToString()), reader["ColName"].ToString().FirstToLower(reader["ColName"].ToString()));
    63                     sbCode.AppendFormat("\t\t[ColumnAttribute(Name = \"{0}\", IsPrimaryKey = {1}, AutoIncrement = {2}, DataType = DataType.{3}, CanNull = {4})]\n", reader["ColName"].ToString(), reader["IsPK"].ToString(), reader["IsAuto"].ToString(), GetDataType(reader["ColTypeName"].ToString()), reader["AllowNull"].ToString());
    64                     sbCode.AppendFormat("\t\tpublic {0} {1}\n", GetType(reader["ColTypeName"].ToString()), reader["ColName"].ToString().FirstToUpper(reader["ColName"].ToString()));
    65                     sbCode.Append("\t\t{\n");
    66                     sbCode.Append("\t\t\tget { return " + "".FirstToLower(reader["ColName"].ToString()) + "; }\n");
    67                     sbCode.Append("\t\t\tset { " + "".FirstToLower(reader["ColName"].ToString()) + " = value; }\n");
    68                     sbCode.Append("\t\t}\n\n");
    69                 }
    70                 sbCode.Append("\t}\n");
    71                 sbCode.Append("}\n");
    72                 sbCode.Replace("@PrimaryKeyName", pkName);
    73                 rtxtCode.Text = sbCode.ToString();
    74             }
    75             catch
    76             {
    77                 MessageBox.Show("连接失败");
    78             }
    79             finally
    80             {
    81                 if (con != null)
    82                 {
    83                     con.Close();
    84                 }
    85             }

      这段代码就是实现了上面实体的生成,这里已经没有多少东西了,只是一个拼字符串的工作。只要仔细认真就可以了。到此位置该介绍的东西就介绍完了。

    当我做完这个东西的时候发现代码生成其核心并不是很难,掌握这些要点就能够写出一个实用的代码生成器。学习东西也是如此,掌握了核心,无论外形怎么变都是万变不离其宗。在后续的文章中将讲解个人框架了,相对于年前的那个来说这个ORM有了很大的改进,以后慢慢与大家分享自己的程序心得。

    源码其实很简单,对于自动获取服务器名称这个功能还没有添加,还有那个自动附加数据库文件。这里上传一下源码,大家有兴趣看一下。 /Files/qingyuan/CodeCreate.zip

  • 相关阅读:
    JQuery
    CSS
    函数装饰器
    函数
    模块和运算符
    前端编程基础
    MySQL优化指南-大表优化思路
    Linux命令find讲解
    LeetCode每日题解(0324)
    Kmeans算法的经典优化——mini-batch和Kmeans++
  • 原文地址:https://www.cnblogs.com/qingyuan/p/1689888.html
Copyright © 2020-2023  润新知