一步步实现自己的ORM(二)

在第一篇《一步步实现自己的ORM(一)》里,我们用反射获取类名、属性和值,我们用这些信息开发了简单的INSERT方法,在上一篇文章里我们提到主键为什么没有设置成自增长类型,单单从属性里我们无法识别哪个是主键,今天我们用Attribute来标识列,关于Attribute,引用MSDN里描述

     MADN的定义为:公共语言运行时允许添加类似关键字的描述声明,叫做attributes, 它对程序中的元素进行标注,如类型、字段、方法和属性等。Attributes和Microsoft .NET Framework文件的元数据(metadata)保存在一起,可以用来向运行时描述你的代码,或者在程序运行的时候影响应用程序的行为。     我们简单的总结为:定制特性attribute,本质上是一个类,其为目标元素提供关联附加信息,并在运行期以反射的方式来获取附加信息。

简单来说Attribute就是描述类、方法、属性参数等信息的。

可参考《浅析C#中的Attribute

在这里我们定义2个Attribute,用来描述表和字段。

    [AttributeUsage(AttributeTargets.Class)]
    class TableAttribute : Attribute
    {
        /// <summary>
        /// 表名
        /// </summary>
        public string Name { get; private set; }

        public TableAttribute(string name)
        {
            this.Name = name;
        }
    }

    [AttributeUsage(AttributeTargets.Property)]
    class ColumnAttribute : Attribute
    {
        /// <summary>
        /// 是否为数据库自动生成
        /// </summary>
        public bool IsGenerated { get; set; }

        /// <summary>
        /// 列名
        /// </summary>
        public string Name { get; private set; }

        public ColumnAttribute(string name)
        {
            this.Name = name;
        }
    }

修改后的实体类如下:

    [Table("tb_Users")]
    public class User
    {
        [Column("UserId",IsGenerated = true)]
        public int UserId { get; set; }

        [Column("Email")]
        public string Email { get; set; }

        [Column("CreatedTime")]
        public DateTime CreatedTime { get; set; }
    }

我们把表结构也修改下

CREATE TABLE [dbo].[tb_Users](
    [UserId] [int] NOT NULL identity(1,1) PRIMARY KEY ,
    [Email] [nvarchar](100) NULL,
    [CreatedTime] [datetime] NULL) 

下面的问题,就是我们如何得到表名和字段名呢?我们还是采用反射来实现,先获取表名:

         TableAttribute[] tableAttr = (TableAttribute[])typeof(User).GetCustomAttributes(typeof(TableAttribute), true);
            if (tableAttr.Length>0)
            {
                Console.WriteLine(tableAttr[0].Name);
            }

运行结果

再获取列名,先查找property,然后找Attribute,代码如下:

            var properties = typeof(User).GetProperties();

            for (int i = 0; i < properties.Length; i++)
            {
                var pi = properties[i];
                var columnAttrs = (ColumnAttribute[])pi.GetCustomAttributes(typeof(ColumnAttribute), true);
                if (columnAttrs.Length>0)
                {
                    Console.WriteLine("字段名:{0},是否为自动生成:{1}", columnAttrs[0].Name, columnAttrs[0].IsGenerated);
                }
            }

运行结果

再来修改INSERT 方法如下:

public static int Insert(User user)
        {
            var type = typeof(User);
            Dictionary<string, object> parameters = new Dictionary<string, object>();
            var properties = type.GetProperties();

            string tableName = string.Empty;
            TableAttribute[] tableAttrs = (TableAttribute[])typeof(User).GetCustomAttributes(typeof(TableAttribute), true);
            if (tableAttrs.Length > 0)
            {
                tableName = tableAttrs[0].Name;
            }
            else {
                tableName = type.Name;
            }

            /*将所有的列放到集合里*/
            List<ColumnAttribute> columns = new List<ColumnAttribute>();
            for (int i = 0; i < properties.Length; i++)
            {
                var pi = properties[i];
                var attrs = (ColumnAttribute[])pi.GetCustomAttributes(typeof(ColumnAttribute), true);
                if (attrs.Length > 0)
                {
                    columns.Add(attrs[0]);
                }
            }

            StringBuilder sql = new StringBuilder();
            sql.Append("INSERT INTO [").Append(tableName).Append("](");
            int paramIndex = 0;

            for (int i = 0; i < properties.Length; i++)
            {
                var pi = properties[i];
                var attrs = (ColumnAttribute[])pi.GetCustomAttributes(typeof(ColumnAttribute), true);
                if (attrs.Length > 0 && attrs[0].IsGenerated == false)
                {
                    if (paramIndex > 0)
                        sql.Append(",");

                    sql.Append(attrs[0].Name);
                    paramIndex++;
                }
            }

            sql.Append(") VALUES (");
            paramIndex = 0;
            for (int i = 0; i < properties.Length; i++)
            {
                var pi = properties[i];
                var attrs = (ColumnAttribute[])pi.GetCustomAttributes(typeof(ColumnAttribute), true);
                if (attrs.Length > 0 && attrs[0].IsGenerated == false)
                {
                    if (paramIndex > 0)
                        sql.Append(",");

                    sql.Append("@p").Append(paramIndex);
                    parameters.Add("@p" + paramIndex, pi.GetValue(user, null));
                    paramIndex++;
                }
            }

            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();
        }

运行结果

我们再定义一个IdAttribute 类用来表示主键,它不需要任何属性

    [AttributeUsage(AttributeTargets.Property)]
    class IdAttribute :ColumnAttribute
    {
        public IdAttribute(string name) : base(name)
        {
        }
    }

有了主键我们下面可以写UPDATE和DELETE 方法:

UPDATE:

  public static int Update(User user)
        {
            var type = typeof(User);
            Dictionary<string, object> parameters = new Dictionary<string, object>();
            var properties = type.GetProperties();

            string tableName = string.Empty;
            TableAttribute[] tableAttrs = (TableAttribute[])typeof(User).GetCustomAttributes(typeof(TableAttribute), true);
            if (tableAttrs.Length > 0)
            {
                tableName = tableAttrs[0].Name;
            }
            else
            {
                tableName = type.Name;
            }

            StringBuilder sql = new StringBuilder();
            sql.Append("UPDATE [").Append(tableName).Append("] SET ");
            int paramIndex = 0;

            for (int i = 0; i < properties.Length; i++)
            {
                var pi = properties[i];
                var idAttrs = (IdAttribute[])pi.GetCustomAttributes(typeof(IdAttribute), true);
                if (idAttrs.Length > 0) //如果是主键 跳过
                    continue;

                var columnAttrs = (ColumnAttribute[])pi.GetCustomAttributes(typeof(ColumnAttribute), true);
                if (columnAttrs.Length > 0)
                {
                    if (paramIndex > 0)
                        sql.Append(",");
                    // 字段 = @p
                    sql.Append(columnAttrs[0].Name).Append("=").Append("@p" + paramIndex);

                    /*参数*/
                    parameters.Add("@p" + paramIndex, pi.GetValue(user, null));
                    paramIndex++;
                }
            }

            sql.Append(" WHERE ");
            int keyIndex = 0;
            for (int i = 0; i < properties.Length; i++)
            {
                var pi = properties[i];
                var idAttrs = (IdAttribute[])pi.GetCustomAttributes(typeof(IdAttribute), true);
                if (idAttrs.Length > 0)
                {
                    if (keyIndex > 0) //考虑到有多个主键
                        sql.Append(" AND ");
                    sql.Append(idAttrs[0].Name).Append("=").Append("@p").Append(paramIndex);

                    /*参数*/
                    parameters.Add("@p" + paramIndex, pi.GetValue(user, null));
                    paramIndex++;
                    keyIndex++;
                }
            }
            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();
        }

调用代码:

            Update(new User()
            {
                UserId = 1,
                Email = "[email protected]",
                CreatedTime = DateTime.Now
            });

运行结果:

DELETE方法:

        public static int DeleteByKey(params object[] values)
        {
            var type = typeof(User);
            Dictionary<string, object> parameters = new Dictionary<string, object>();
            var properties = type.GetProperties();

            string tableName = string.Empty;
            TableAttribute[] tableAttrs = (TableAttribute[])typeof(User).GetCustomAttributes(typeof(TableAttribute), true);
            if (tableAttrs.Length > 0)
            {
                tableName = tableAttrs[0].Name;
            }
            else
            {
                tableName = type.Name;
            }

            /*将所有的列放到集合里*/
            List<IdAttribute> columns = new List<IdAttribute>();
            for (int i = 0; i < properties.Length; i++)
            {
                var pi = properties[i];
                var attrs = (IdAttribute[])pi.GetCustomAttributes(typeof(IdAttribute), true);
                if (attrs.Length > 0)
                {
                    columns.Add(attrs[0]);
                }
            }
            if (columns.Count != values.Length)
                throw new ArgumentException("参数个数和主键数不一致");

            StringBuilder sql = new StringBuilder();
            sql.Append("DELETE FROM [").Append(tableName).Append("] ").Append(" WHERE ");

            for (int i = 0; i < columns.Count; i++)
            {
                if (i > 0) //考虑到有多个主键
                    sql.Append(" AND ");

                sql.Append(columns[i].Name).Append("=").Append("@p").Append(i);

                /*参数*/
                parameters.Add("@p" + i, values[i]);
            }
            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();
        }

调用代码:

 DeleteByKey(1);

运行结果

最后我们把增删改方法放在一个泛型类里。

    class EntityHelper
    {
        private const string connectionString = "";

        public static int Insert<T>(T entity)
        {
            var type = typeof(T);
            Dictionary<string, object> parameters = new Dictionary<string, object>();
            var properties = type.GetProperties();

            string tableName = string.Empty;
            TableAttribute[] tableAttrs = (TableAttribute[])type.GetCustomAttributes(typeof(TableAttribute), true);
            if (tableAttrs.Length > 0)
            {
                tableName = tableAttrs[0].Name;
            }
            else
            {
                tableName = type.Name;
            }

            /*将所有的列放到集合里*/
            List<ColumnAttribute> columns = new List<ColumnAttribute>();
            for (int i = 0; i < properties.Length; i++)
            {
                var pi = properties[i];
                var attrs = (ColumnAttribute[])pi.GetCustomAttributes(typeof(ColumnAttribute), true);
                if (attrs.Length > 0)
                {
                    columns.Add(attrs[0]);
                }
            }

            StringBuilder sql = new StringBuilder();
            sql.Append("INSERT INTO [").Append(tableName).Append("](");
            int paramIndex = 0;

            for (int i = 0; i < properties.Length; i++)
            {
                var pi = properties[i];
                var attrs = (ColumnAttribute[])pi.GetCustomAttributes(typeof(ColumnAttribute), true);
                if (attrs.Length > 0 && attrs[0].IsGenerated == false)
                {
                    if (paramIndex > 0)
                        sql.Append(",");

                    sql.Append(attrs[0].Name);
                    paramIndex++;
                }
            }

            sql.Append(") VALUES (");
            paramIndex = 0;
            for (int i = 0; i < properties.Length; i++)
            {
                var pi = properties[i];
                var attrs = (ColumnAttribute[])pi.GetCustomAttributes(typeof(ColumnAttribute), true);
                if (attrs.Length > 0 && attrs[0].IsGenerated == false)
                {
                    if (paramIndex > 0)
                        sql.Append(",");

                    sql.Append("@p").Append(paramIndex);
                    parameters.Add("@p" + paramIndex, pi.GetValue(entity, null));
                    paramIndex++;
                }
            }

            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();
        }

        public static int Update<T>(T entity)
        {
            var type = typeof(T);
            Dictionary<string, object> parameters = new Dictionary<string, object>();
            var properties = type.GetProperties();

            string tableName = string.Empty;
            TableAttribute[] tableAttrs = (TableAttribute[])type.GetCustomAttributes(typeof(TableAttribute), true);
            if (tableAttrs.Length > 0)
            {
                tableName = tableAttrs[0].Name;
            }
            else
            {
                tableName = type.Name;
            }

            StringBuilder sql = new StringBuilder();
            sql.Append("UPDATE [").Append(tableName).Append("] SET ");
            int paramIndex = 0;

            for (int i = 0; i < properties.Length; i++)
            {
                var pi = properties[i];
                var idAttrs = (IdAttribute[])pi.GetCustomAttributes(typeof(IdAttribute), true);
                if (idAttrs.Length > 0) //如果是主键 跳过
                    continue;

                var columnAttrs = (ColumnAttribute[])pi.GetCustomAttributes(typeof(ColumnAttribute), true);
                if (columnAttrs.Length > 0)
                {
                    if (paramIndex > 0)
                        sql.Append(",");
                    // 字段 = @p
                    sql.Append(columnAttrs[0].Name).Append("=").Append("@p" + paramIndex);

                    /*参数*/
                    parameters.Add("@p" + paramIndex, pi.GetValue(entity, null));
                    paramIndex++;
                }
            }

            sql.Append(" WHERE ");
            int keyIndex = 0;
            for (int i = 0; i < properties.Length; i++)
            {
                var pi = properties[i];
                var idAttrs = (IdAttribute[])pi.GetCustomAttributes(typeof(IdAttribute), true);
                if (idAttrs.Length > 0)
                {
                    if (keyIndex > 0) //考虑到有多个主键
                        sql.Append(" AND ");
                    sql.Append(idAttrs[0].Name).Append("=").Append("@p").Append(paramIndex);

                    /*参数*/
                    parameters.Add("@p" + paramIndex, pi.GetValue(entity, null));
                    paramIndex++;
                    keyIndex++;
                }
            }
            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();
        }

        public static int DeleteByKey<T>(params object[] values)
        {
            var type = typeof(T);
            Dictionary<string, object> parameters = new Dictionary<string, object>();
            var properties = type.GetProperties();

            string tableName = string.Empty;
            TableAttribute[] tableAttrs = (TableAttribute[])type.GetCustomAttributes(typeof(TableAttribute), true);
            if (tableAttrs.Length > 0)
            {
                tableName = tableAttrs[0].Name;
            }
            else
            {
                tableName = type.Name;
            }

            /*将所有的列放到集合里*/
            List<IdAttribute> columns = new List<IdAttribute>();
            for (int i = 0; i < properties.Length; i++)
            {
                var pi = properties[i];
                var attrs = (IdAttribute[])pi.GetCustomAttributes(typeof(IdAttribute), true);
                if (attrs.Length > 0)
                {
                    columns.Add(attrs[0]);
                }
            }
            if (columns.Count != values.Length)
                throw new ArgumentException("参数个数和主键数不一致");

            StringBuilder sql = new StringBuilder();
            sql.Append("DELETE FROM [").Append(tableName).Append("] ").Append(" WHERE ");

            for (int i = 0; i < columns.Count; i++)
            {
                if (i > 0) //考虑到有多个主键
                    sql.Append(" AND ");

                sql.Append(columns[i].Name).Append("=").Append("@p").Append(i);

                /*参数*/
                parameters.Add("@p" + i, values[i]);
            }
            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();
        }
    }

大功告成,我们修改下调用代码

           EntityHelper.Insert(new User()
            {
                Email = "[email protected]",
                CreatedTime = DateTime.Now
            });

            EntityHelper.Update(new User()
            {
                UserId = 1,
                Email = "[email protected]",
                CreatedTime = DateTime.Now
            });

            EntityHelper.DeleteByKey<User>(1);
时间: 2024-08-02 06:59:59

一步步实现自己的ORM(二)的相关文章

ORM(二)

一.ORM简介         对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术.简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中.那么,到底如何实现持久化呢?一种简单的方案是采用硬编码方式,为每一种可能的数据库访问操作提供单独的方法.         这种方案存在以下不足:         1.持久化层缺乏弹性.一旦出现业务需求的变更,就必须修改持久化

一步步实现自己的ORM(四)

通过前3章文章,大致对ORM有一定的了解,但也存在效率低下(大量用了反射)和重复代码,今天我们要对ORM进行优化. 具体流程如下: 我们优化的第一个就是减少反射调用,我的思路是定义一个Mapping,把表名.字段名信息缓存起来,EntityMapping 表示实体类信息对应数据库中的table,MemberMapping表示实体类的属性对应数据库中的Column. EntityMapping代码: class EntityMapping { /// <summary> /// 对应实体类类型

一步步实现自己的ORM(五)

上一张优化了ORM的INSERT.UPDATE.DELETE,但将数据库里的值填充到实体类这块还没优化.另外有博友在网上咨询说你这个都是查询所有字段的,而他的需求是按需查询字段,不是一次性取出来所有字段的,在这里我请这位朋友耐心等待,这个会在后面章节提到的.这次我们先优化datareader->entity,将数据库里的值Mapping到实体类里,我们常用的有两种办法第一种是采用EMIT方式,第二种是采用Expression tree表达式,这两种方案在性能上差异不大,但写法上第二种较第一种比较

Python Day74 ORM二

一.查询表记录 1.查询相关API 现将主要的查询API使用实例介绍如下,其中Student为定义的表类: (1).Student.objects.all() #返回的QuerySet类型 查询所有记录 [obj1,obj2....] (2).Student.objects.filter() #返回的QuerySet类型 查询所有符合条件的记录 (3).Student.objects.exclude() #返回的QuerySet类型 查询所有不符合条件的记录,过滤条件之外的 (4).Studen

flask 中的ORM ( 二 )

1 关系映射 1 多对多 1 什么是多对多 A表中的一条数据可以与B表中任意多条数据相关联 B表中的一条数据可以与A表中任意多条数据相关联 2 实现 在数据库中使用第三张表(关联表) 在编程语言中,可以不编写对应的实体类 1 创建第三张表 student_course = db.Table( 'student_course',# 在数据库中的表名 db.Column('id',db.Integer,primary_key = True), db.Column('student_id',db.In

Django之ORM(二)

必知必会13条 准备数据 from django.db import models # Create your models here. class MyCharField(models.Field): """ 自定义的char类型的字段类 """ def __init__(self, max_length, *args, **kwargs): self.max_length = max_length super(MyCharField, sel

C#_自定义简单ORM(二)代码下载

测试实体: [TableAttribute("TEST_STUDENT")] public class Student : MyBaseClass { /// <summary> /// /// </summary> [ColumnAttribute("GUID", DbType.AnsiString, IsPK = true)] public string Guid { get; set; } [ColumnAttribute("

一步步手动实现热修复(二)-类的加载机制简要介绍

本节内容是为了给下节内容做知识铺垫,所以如果要需要了解热修复技术,本节内容的知识点必不可少. 一个类在被加载到内存之前要经过加载.验证.准备等过程.经过这些过程之后,虚拟机才会从方法区将代表类的运行时数据结构转换为内存中的Class. 我们这节内容的重点在于一个类是如何被加载的,所以我们从类的加载入口开始. 类的加载是由虚拟机触发的,类的加载入口位于ClassLoader的loadClassInternal()方法: // This method is invoked by the virtua

【SSH之旅】一步步学习Hibernate框架(二):一对一关联映射中的主键关联

一对一的映射在对象模型中是经常见到的,主要是将对象模型转换为关系模型就必须在映射文件中进行配置,重点是<one-to-one>标签的使用,有两种方式,第一是主键关联,第二是唯一外键关联,现在先来看第一种方式. 一对一的关联映射中的主键关联,就是说两个对象具有相同的主键值,以表明它们之间的一一对应的关系,数据库表不会有额外的字段来维护它们之间的关系,仅仅是通过表的主键来关联. 上图的Person类和IdCard类之间是一一对应关系,一个人就只能有一个身份证,一个身份证只能对应一个人,那么身份证的