一步步实现自己的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 实现 在数据库中使用第三张表(关联表) 在编程语言中,可 ...
随机推荐
- 【转】Pro Android学习笔记(三):了解Android资源(上)
在Android开发中,资源包括文件或者值,它们和执行应用捆绑,无需在源代码中写死,因此我们可以改变或替换他们,而无需对应用重新编译. 了解资源构成 参考阅读Android学习笔记(三八):资源res ...
- shell 脚本学习之一 ------let,echo,变量
转自:http://blog.chinaunix.net/uid-26827001-id-3154024.html 首先看到好多以这样开头的 #!/bin/bash 这个是告诉系统 解释此脚 ...
- ACM之Java技巧
一.Java之ACM注意点 关于四舍五入 小数保留几位: DecimalFormat df = new DecimalFormat("0.00"); String num = d ...
- openstack开发环境搭建
1 目的 让linux下的openstack代码能在windows上面实现同步开发. 2 目标 使用samba实现window与Linux的文件共享. 3 实验环境 ...
- python接口测试,第三方包xlrd和xlutils,怎么安装
第1个:xlrd的下载地址:https://pypi.python.org/pypi/xlrd/0.9.2 安装过程:下载后解压文件夹,使用cd命令行进入该文件夹后,用命令:python setup. ...
- 用于获取或设置Web.config/*.exe.config中节点数据的辅助类
1. 用于获取或设置Web.config/*.exe.config中节点数据的辅助类 /**//// <summary> /// 用于获取或设置Web.config/*.exe.confi ...
- [hdu2544]最短路spfa
解题关键:最短路模板一直没整理过,这里整理了一下spfa+链式前向星建图的最短路模板,以后网络赛省的打了. spfa算法的关键就是松弛操作,只有进行松弛操作,其后的点距离才可能被更新. #includ ...
- day1 java基础
常见的dos命令 盘符: 进入指定的盘符下. dir : 列出当前目录下的文件以及文件夹 md : 创建目录 rd : 删除目录 注意:rd不能删除非空的文件夹,而且只能用于删除文件夹. cd ...
- UVa 11795 Mega Man's Mission (状压DP)
题意:你最初只有一个武器,你需要按照一定的顺序消灭n个机器人(n<=16).每消灭一个机器人将会得到他的武器. 每个武器只能杀死特定的机器人.问可以消灭所有机器人的顺序方案总数. 析:dp[s] ...
- HDU - 6113 2017百度之星初赛A 度度熊的01世界
度度熊的01世界 Accepts: 967 Submissions: 3064 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 327 ...