在第一篇《一步步实现自己的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>)
{
Console.WriteLine(tableAttr[].Name);
}

运行结果

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

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

            for (int i = ; i < properties.Length; i++)
{
var pi = properties[i];
var columnAttrs = (ColumnAttribute[])pi.GetCustomAttributes(typeof(ColumnAttribute), true);
if (columnAttrs.Length>)
{
Console.WriteLine("字段名:{0},是否为自动生成:{1}", columnAttrs[].Name, columnAttrs[].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 > )
{
tableName = tableAttrs[].Name;
}
else {
tableName = type.Name;
} /*将所有的列放到集合里*/
List<ColumnAttribute> columns = new List<ColumnAttribute>();
for (int i = ; i < properties.Length; i++)
{
var pi = properties[i];
var attrs = (ColumnAttribute[])pi.GetCustomAttributes(typeof(ColumnAttribute), true);
if (attrs.Length > )
{
columns.Add(attrs[]);
}
} StringBuilder sql = new StringBuilder();
sql.Append("INSERT INTO [").Append(tableName).Append("](");
int paramIndex = ; for (int i = ; i < properties.Length; i++)
{
var pi = properties[i];
var attrs = (ColumnAttribute[])pi.GetCustomAttributes(typeof(ColumnAttribute), true);
if (attrs.Length > && attrs[].IsGenerated == false)
{
if (paramIndex > )
sql.Append(","); sql.Append(attrs[].Name);
paramIndex++;
}
} sql.Append(") VALUES (");
paramIndex = ;
for (int i = ; i < properties.Length; i++)
{
var pi = properties[i];
var attrs = (ColumnAttribute[])pi.GetCustomAttributes(typeof(ColumnAttribute), true);
if (attrs.Length > && attrs[].IsGenerated == false)
{
if (paramIndex > )
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 > )
{
tableName = tableAttrs[].Name;
}
else
{
tableName = type.Name;
} StringBuilder sql = new StringBuilder();
sql.Append("UPDATE [").Append(tableName).Append("] SET ");
int paramIndex = ; for (int i = ; i < properties.Length; i++)
{
var pi = properties[i];
var idAttrs = (IdAttribute[])pi.GetCustomAttributes(typeof(IdAttribute), true);
if (idAttrs.Length > ) //如果是主键 跳过
continue; var columnAttrs = (ColumnAttribute[])pi.GetCustomAttributes(typeof(ColumnAttribute), true);
if (columnAttrs.Length > )
{
if (paramIndex > )
sql.Append(",");
// 字段 = @p
sql.Append(columnAttrs[].Name).Append("=").Append("@p" + paramIndex); /*参数*/
parameters.Add("@p" + paramIndex, pi.GetValue(user, null));
paramIndex++;
}
} sql.Append(" WHERE ");
int keyIndex = ;
for (int i = ; i < properties.Length; i++)
{
var pi = properties[i];
var idAttrs = (IdAttribute[])pi.GetCustomAttributes(typeof(IdAttribute), true);
if (idAttrs.Length > )
{
if (keyIndex > ) //考虑到有多个主键
sql.Append(" AND ");
sql.Append(idAttrs[].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 = ,
Email = "new@new.com",
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 > )
{
tableName = tableAttrs[].Name;
}
else
{
tableName = type.Name;
} /*将所有的列放到集合里*/
List<IdAttribute> columns = new List<IdAttribute>();
for (int i = ; i < properties.Length; i++)
{
var pi = properties[i];
var attrs = (IdAttribute[])pi.GetCustomAttributes(typeof(IdAttribute), true);
if (attrs.Length > )
{
columns.Add(attrs[]);
}
}
if (columns.Count != values.Length)
throw new ArgumentException("参数个数和主键数不一致"); StringBuilder sql = new StringBuilder();
sql.Append("DELETE FROM [").Append(tableName).Append("] ").Append(" WHERE "); for (int i = ; i < columns.Count; i++)
{
if (i > ) //考虑到有多个主键
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();

运行结果

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

    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 > )
{
tableName = tableAttrs[].Name;
}
else
{
tableName = type.Name;
} /*将所有的列放到集合里*/
List<ColumnAttribute> columns = new List<ColumnAttribute>();
for (int i = ; i < properties.Length; i++)
{
var pi = properties[i];
var attrs = (ColumnAttribute[])pi.GetCustomAttributes(typeof(ColumnAttribute), true);
if (attrs.Length > )
{
columns.Add(attrs[]);
}
} StringBuilder sql = new StringBuilder();
sql.Append("INSERT INTO [").Append(tableName).Append("](");
int paramIndex = ; for (int i = ; i < properties.Length; i++)
{
var pi = properties[i];
var attrs = (ColumnAttribute[])pi.GetCustomAttributes(typeof(ColumnAttribute), true);
if (attrs.Length > && attrs[].IsGenerated == false)
{
if (paramIndex > )
sql.Append(","); sql.Append(attrs[].Name);
paramIndex++;
}
} sql.Append(") VALUES (");
paramIndex = ;
for (int i = ; i < properties.Length; i++)
{
var pi = properties[i];
var attrs = (ColumnAttribute[])pi.GetCustomAttributes(typeof(ColumnAttribute), true);
if (attrs.Length > && attrs[].IsGenerated == false)
{
if (paramIndex > )
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 > )
{
tableName = tableAttrs[].Name;
}
else
{
tableName = type.Name;
} StringBuilder sql = new StringBuilder();
sql.Append("UPDATE [").Append(tableName).Append("] SET ");
int paramIndex = ; for (int i = ; i < properties.Length; i++)
{
var pi = properties[i];
var idAttrs = (IdAttribute[])pi.GetCustomAttributes(typeof(IdAttribute), true);
if (idAttrs.Length > ) //如果是主键 跳过
continue; var columnAttrs = (ColumnAttribute[])pi.GetCustomAttributes(typeof(ColumnAttribute), true);
if (columnAttrs.Length > )
{
if (paramIndex > )
sql.Append(",");
// 字段 = @p
sql.Append(columnAttrs[].Name).Append("=").Append("@p" + paramIndex); /*参数*/
parameters.Add("@p" + paramIndex, pi.GetValue(entity, null));
paramIndex++;
}
} sql.Append(" WHERE ");
int keyIndex = ;
for (int i = ; i < properties.Length; i++)
{
var pi = properties[i];
var idAttrs = (IdAttribute[])pi.GetCustomAttributes(typeof(IdAttribute), true);
if (idAttrs.Length > )
{
if (keyIndex > ) //考虑到有多个主键
sql.Append(" AND ");
sql.Append(idAttrs[].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 > )
{
tableName = tableAttrs[].Name;
}
else
{
tableName = type.Name;
} /*将所有的列放到集合里*/
List<IdAttribute> columns = new List<IdAttribute>();
for (int i = ; i < properties.Length; i++)
{
var pi = properties[i];
var attrs = (IdAttribute[])pi.GetCustomAttributes(typeof(IdAttribute), true);
if (attrs.Length > )
{
columns.Add(attrs[]);
}
}
if (columns.Count != values.Length)
throw new ArgumentException("参数个数和主键数不一致"); StringBuilder sql = new StringBuilder();
sql.Append("DELETE FROM [").Append(tableName).Append("] ").Append(" WHERE "); for (int i = ; i < columns.Count; i++)
{
if (i > ) //考虑到有多个主键
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 = "abc@123.com",
CreatedTime = DateTime.Now
}); EntityHelper.Update(new User()
{
UserId = ,
Email = "new@new.com",
CreatedTime = DateTime.Now
}); EntityHelper.DeleteByKey<User>();

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

  1. 一步步实现自己的ORM(三)

    章节列表: <一步步实现自己的ORM(一)> <一步步实现自己的ORM(二)> 通过前面两篇文章,我们大致了解了ORM的基本原理,是通过Attribute+反射获取表的基本信息 ...

  2. 一步步实现自己的ORM(一)

    最近在研究ORM,尝试着自己开发了一个简单的ORM.我个人不喜欢EF因为跟不上EF升级太快了,再说公司里还停留在c# 3.5时代,对于NHibernate配置太复杂看到就头晕,就心生自己做一个ORM的 ...

  3. ORM(二)

    一.ORM简介         对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术.简单的说,ORM是通过使 ...

  4. Django ORM (二) 增加操作

    数据库表结构生成完毕后,可以使用工具连接上去 在 app01_author 表创建基础记录 在 app01_publisher 表创建基础记录 添加 data_oper 方法 在 urls.py 文件 ...

  5. 一步步写STM32 OS【二】环境搭建

    一.安装IAR for ARM6.5 二.新建工程 1.选择处理器:STM32F407VG,暂不使用FPU 2.必要的路径配置和宏定义 3.使用SWO重定向IO输出 4.使用ST-LINK仿真器 5. ...

  6. 从MySQL到ORM(二):MySQL基础

    一.基本概念 1.数据库: 数据库(DataBase)就是一个存储数据的仓库,为了方便数据的存储和管理,它将数据按照特定的规律存储在磁盘上.通过数据库管理系统,可以有效的组织和管理存储在数据库中的数据 ...

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

    上一张优化了ORM的INSERT.UPDATE.DELETE,但将数据库里的值填充到实体类这块还没优化.另外有博友在网上咨询说你这个都是查询所有字段的,而他的需求是按需查询字段,不是一次性取出来所有字 ...

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

    通过前3章文章,大致对ORM有一定的了解,但也存在效率低下(大量用了反射)和重复代码,今天我们要对ORM进行优化. 具体流程如下: 我们优化的第一个就是减少反射调用,我的思路是定义一个Mapping, ...

  9. flask 中的ORM ( 二 )

    1 关系映射 1 多对多 1 什么是多对多 A表中的一条数据可以与B表中任意多条数据相关联 B表中的一条数据可以与A表中任意多条数据相关联 2 实现 在数据库中使用第三张表(关联表) 在编程语言中,可 ...

随机推荐

  1. javacv实现实时视频截图和录像服务easyCV

    本项目维护于github:https://github.com/eguid/easyCV 1.介绍 本项目基于javaCV1.4.x. 其中实现了 (1)实时视频截图服务(支持rtsp/rtmp/ht ...

  2. JZOJ 1667【AHOI2009】中国象棋——dp

    题目:https://jzoj.net/senior/#main/show/1667 只注重0.1.2的列有多少个,不注重它们的位置,就能记录了. #include<iostream> # ...

  3. Java远程调用

    一.  概念: RMI全称是Remote Method Invocation-远程方法调用,其威力就体现在它强大的开发分布式网络应用的能力上,是纯Java的网络分布式应用系统的核心解决方案之一.它支持 ...

  4. javascript 中的深复制 和 其实现方法

    首先,我们需要明白什么是深复制(侧重指对象方面)? 在javascript中,复制分为浅复制和深复制,个人理解,浅复制就是直接将引用复制,复制前后的两个对象指向同一个内存地址,对其中一个进行操作,另外 ...

  5. 原生及jq方式使用ajax

    1.原生js实现Ajax方法: // 封装ajax()方法 function ajax(url,fnSucc,fnFaild){ //1.创建Ajax 对象 if(window.XMLHttpRequ ...

  6. php中的PCRE 函数,正则表达式处理函数。

    有时候在一些特定的业务场景中需要匹配,或者提取一些关键的信息,例如匹配网页中的一些链接, 提取一些数据时,可能会用到正则匹配. 下面介绍一下php中的一些常用的正则处理函数. 一.preg_repla ...

  7. 网络性能优化GSO/GIO研究

    性能检测工具安装 # curl -O http://downloads.es.net/pub/iperf/iperf-3.0.6.tar.gz # tar axf iperf-3.0.6.tar.gz ...

  8. 基于unity3d游戏的android版本逆向初探

    https://bbs.pediy.com/thread-212532.htm [文章标题]: 基于unity3d游戏的android版本逆向初探 [文章作者]: dreaman [作者邮箱]: [e ...

  9. 有线电视网(树形dp)

    有线电视网 某收费有线电视网计划转播一场重要的足球比赛.他们的转播网和用户终端构成一棵树状结构,这棵树的根结点位于足球比赛的现场,树叶为各个用户终端,其他中转站为该树的内部节点.从转播站到转播站以及从 ...

  10. 2017-9-13 NOIP模拟赛[xxy]

    全排列 (permutation.cpp/c/pas)Description从 n 个不同元素中任取 m(m≤n)个元素,按照一定的顺序排列起来,叫做从 n个不同元素中取出 m 个元素的一个排列.当 ...