zoukankan      html  css  js  c++  java
  • 一步步实现自己的ORM(一)

     最近在研究ORM,尝试着自己开发了一个简单的ORM。我个人不喜欢EF因为跟不上EF升级太快了,再说公司里还停留在c# 3.5时代,对于NHibernate配置太复杂看到就头晕,就心生自己做一个ORM的念头,现在把开发过程中的点点滴滴记录下来,供自己和新手参考,大神请直接忽略这篇文章。

      ORM(object relation mapping)对象关系映射,常用来映射数据库里表、字段等信息。ORM的原理无非就是用配置文件、Attribute来映射数据关系。我们一步步来来创建ORM,定义一个实体类,然后用反射获得类名(表名)、属性(对应是字段名称),例子如下:

    CREATE TABLE [dbo].[User](
        [UserId] [int] NOT NULL primary key,
        [Email] [nvarchar](100) NULL,
        [CreatedTime] [datetime] NULL
    )

      在这里我创建一张表,其中UserId是主键,为什么不定义成自增长的呢?请保留这个疑问,继续看下去。表创建好了,下面就是定义一个类,如下:

        public class User
        {
            public int UserId { get; set; }
    
            public string Email { get; set; }
    
            public DateTime CreatedTime { get; set; }
        }

    现在我们要对这个类写一个INSERT操作,直接传入User对象然后插入到数据库。我们要用系统自动构建SQL语句,不需要手写SQL语句,在这我们先分析下INSERT INTO SQL语句。

    INSERT INTO 表名
               (字段1
               ,字段2
               ,字段3...)
         VALUES
               值1,
               值2,
               值3...

    我们需要知道表名、字段名和值才能够完成SQL拼接,怎么得到这些信息呢,万能的反射该出场。 

                string className = typeof(User).Name;
                Console.WriteLine("类名:{0}", className);
                Console.WriteLine("-----获取属性信息-----");
                var properties = typeof(User).GetProperties();
                foreach (var item in properties)
                {
                    Console.WriteLine("属性名:{0}",item.Name);
                }
                Console.WriteLine("-----end-----");

    运行结果如下

     

    LOOK,User类的信息都显示出来了,属性的值该如何显示?也就是SQL语句的VALUE,还是要靠万能的反射,请看官门继续往下看。

                string className = typeof(User).Name;
                Console.WriteLine("类名:{0}", className);
                var properties = typeof(User).GetProperties();
                User user = new User() { 
                     UserId = 123,
                      Email = "abc@123.com",
                       CreatedTime = DateTime.Now
                };
    
                foreach (var item in properties)
                {
                    Console.WriteLine("{0}:{1}", item.Name, item.GetValue(user, null));
                }

    类名、属性名、属性值我们都得到了,那下面要做的就是拼写SQL语句了

            public static void Main()
            {
                PritSql(new User()
                {
                    UserId = 123,
                    Email = "abc@123.com",
                    CreatedTime = DateTime.Now
                });
    
            }
    
            public static void PritSql(User user)
            {
    
                string className = typeof(User).Name;
                var properties = typeof(User).GetProperties();
    
                StringBuilder sql = new StringBuilder();
                sql.Append("INSERT INTO ").Append(className).Append("(");
    
                for (int i = 0; i < properties.Length; i++)
                {
                    var pi = properties[i];
                    if (i > 0)
                        sql.Append(",");
                    sql.Append(pi.Name);
                }
                sql.Append(") VALUES (");
                for (int i = 0; i < properties.Length; i++)
                {
                    var pi = properties[i];
                    if (i > 0)
                        sql.Append(",");
                    sql.Append("'").Append(pi.GetValue(user,null)).Append("'");
                }
                sql.Append(")");
                Console.WriteLine(sql);
    
            }
    View Code

     为避免SQL注入,我把SQL改成参数的信息

    INSERT INTO 表名
               (字段1
               ,字段2
               ,字段3...)
         VALUES
               @p1,
               @p2,
               @p3...

    修改后的c#代码

        public static void Main()
            {
                PritParameterSql(new User()
                {
                    UserId = 123,
                    Email = "abc@123.com",
                    CreatedTime = DateTime.Now
                });
    
            }
            public static int PritParameterSql<T>(T user)
            {
                Dictionary<string, object> parameters = new Dictionary<string, object>();
                string className = typeof(T).Name;
                var properties = typeof(T).GetProperties();
    
                StringBuilder sql = new StringBuilder();
                sql.Append("INSERT INTO [").Append(className).Append("](");
    
                for (int i = 0; i < properties.Length; i++)
                {
                    var pi = properties[i];
                    if (i > 0)
                        sql.Append(",");
                    sql.Append(pi.Name);
                }
                sql.Append(") VALUES (");
                for (int i = 0; i < properties.Length; i++)
                {
                    var pi = properties[i];
                    if (i > 0)
                        sql.Append(",");
                    sql.Append("@p").Append(i);
    
                    parameters.Add("@p" + i, pi.GetValue(user, null));
                }
                sql.Append(")");
                Console.WriteLine(sql);
    
                SqlConnection conn = new SqlConnection(connectionString);
                var cmd = conn.CreateCommand();
                cmd.CommandText = sql.ToString();
                foreach (var item in parameters)
                {
                    var pa = cmd.CreateParameter();
                    pa.ParameterName = item.Key;
                    pa.Value = item.Value ?? DBNull.Value;
                    cmd.Parameters.Add(pa);
                }
    
    
                conn.Open();
                return cmd.ExecuteNonQuery();
            }
    View Code

    执行后的结果

    还记得前面提到为什么UserId不设置成自增长吗?因为这种单纯的反射无法识别哪个是自增长字段,如果想用自增长主键怎么办?请看后续文章。

  • 相关阅读:
    利用CWinThread实现跨线程父子MFC窗口
    GetForgroundWindow函数的不确定性——BUG笔记
    DLL动态链接库的创建
    php正则讲解 及与 js的正则比较
    cookie和session的讲解
    两种排序的方法 冒泡法 插入法 封装版
    php中获取当前时间
    文件下载及header方法介绍
    文件上传
    二分法封装版
  • 原文地址:https://www.cnblogs.com/sobaby/p/4341858.html
Copyright © 2011-2022 走看看