一步步实现自己的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>)
{
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(二)的更多相关文章
- 一步步实现自己的ORM(三)
章节列表: <一步步实现自己的ORM(一)> <一步步实现自己的ORM(二)> 通过前面两篇文章,我们大致了解了ORM的基本原理,是通过Attribute+反射获取表的基本信息 ...
- 一步步实现自己的ORM(一)
最近在研究ORM,尝试着自己开发了一个简单的ORM.我个人不喜欢EF因为跟不上EF升级太快了,再说公司里还停留在c# 3.5时代,对于NHibernate配置太复杂看到就头晕,就心生自己做一个ORM的 ...
- ORM(二)
一.ORM简介 对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术.简单的说,ORM是通过使 ...
- Django ORM (二) 增加操作
数据库表结构生成完毕后,可以使用工具连接上去 在 app01_author 表创建基础记录 在 app01_publisher 表创建基础记录 添加 data_oper 方法 在 urls.py 文件 ...
- 一步步写STM32 OS【二】环境搭建
一.安装IAR for ARM6.5 二.新建工程 1.选择处理器:STM32F407VG,暂不使用FPU 2.必要的路径配置和宏定义 3.使用SWO重定向IO输出 4.使用ST-LINK仿真器 5. ...
- 从MySQL到ORM(二):MySQL基础
一.基本概念 1.数据库: 数据库(DataBase)就是一个存储数据的仓库,为了方便数据的存储和管理,它将数据按照特定的规律存储在磁盘上.通过数据库管理系统,可以有效的组织和管理存储在数据库中的数据 ...
- 一步步实现自己的ORM(五)
上一张优化了ORM的INSERT.UPDATE.DELETE,但将数据库里的值填充到实体类这块还没优化.另外有博友在网上咨询说你这个都是查询所有字段的,而他的需求是按需查询字段,不是一次性取出来所有字 ...
- 一步步实现自己的ORM(四)
通过前3章文章,大致对ORM有一定的了解,但也存在效率低下(大量用了反射)和重复代码,今天我们要对ORM进行优化. 具体流程如下: 我们优化的第一个就是减少反射调用,我的思路是定义一个Mapping, ...
- flask 中的ORM ( 二 )
1 关系映射 1 多对多 1 什么是多对多 A表中的一条数据可以与B表中任意多条数据相关联 B表中的一条数据可以与A表中任意多条数据相关联 2 实现 在数据库中使用第三张表(关联表) 在编程语言中,可 ...
随机推荐
- SoundHound Inc. Programming Contest 2018
A - F Time Limit: 2 sec / Memory Limit: 1024 MB Score : 100100 points Problem Statement You are give ...
- ACM学习历程—HDU 2112 HDU Today(map && spfa && 优先队列)
Description 经过锦囊相助,海东集团终于度过了危机,从此,HDU的发展就一直顺风顺水,到了2050年,集团已经相当规模了,据说进入了钱江肉丝经济开发区500强.这时候,XHD夫妇也退居了二线 ...
- [CTSC 2012] Cheat
[题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=2806 [算法] 首先建立广义后缀自动机 注意到问题具有单调性 , 不妨对于每组询问二 ...
- dubbo的扩展点重构
可扩展设计是框架要重点考虑的设计,因为它直接影响到框架的稳定性和功能的扩展,Dubbo扩展点重构.它在扩展性设计上踩过的坑,值得框架设计者借鉴学习. 第一步,微核心,插件式,平等对待第三方 即然要扩展 ...
- RMAN兼容性、控制文件自动备份、保存时间、备份策略、备份脚本(二)
RMAN 程序的兼容性 RMAN 环境由以下5部分组成:(1) RMAN executable(2) Recovery catalog database(3) Recovery catalog sch ...
- Rsync的配置与使用
一.介绍 (不想看直接可以跳过) Rsync是一个远程数据同步工具,可通过LAN/WAN快速同步多台主机间的文件.Rsync本来是用以取代rcp的一个工具,它当前由 rsync.samba.org维护 ...
- OSP 与 Session
大家都知道,OSP是不支持session的,换句话说,登录有效期是永久的.一般网站,如果你不操作一段时间以后,必须重新登录.osp不是这样的,你一旦登录后,即便服务器重启了,你依然能访问服务器并不需要 ...
- Unity T4M 中文讲解
http://blog.csdn.net/tianmao111/article/details/46482963 现在在u3d圈里流行了一种地形转换器(或者叫编辑器吧),但是经查阅之后,似乎还没有中文 ...
- 2014-11-1 NOIP模拟赛1
冲刺NOIP2014复赛模拟题第六套第二试 题目名称 日历游戏 最大公约数 密码 英文代号 calendar gcd pasuwado 输入文件名 calendar.in gcd.in pasuw ...
- 洛谷P3431 [POI2005]AUT-The Bus
P3431 [POI2005]AUT-The Bus 题目描述 The streets of Byte City form a regular, chessboardlike network - th ...