一步步实现自己的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 实现 在数据库中使用第三张表(关联表) 在编程语言中,可 ...
随机推荐
- 【LeetCode】011 Container With Most Water
题目: Given n non-negative integers a1, a2, ..., an, where each represents a point at coordinate (i, a ...
- javacpp-FFmpeg系列补充:FFmpeg拉流截图实现在线演示demo(视频截图并返回base64图像,支持jpg/png/gif/bmp等多种格式)
javacpp-ffmpeg系列: javacpp-FFmpeg系列之1:视频拉流解码成YUVJ420P,并保存为jpg图片 javacpp-FFmpeg系列之2:通用拉流解码器,支持视频拉流解码并转 ...
- Linq 支持动态字查询集合, 也就是说根据传入的值进行查询。
Linq 支持动态字查询集合, 也就是说根据传入的值进行查询. 比如我们有个类Patient, 其中有个字段PatientName, 现在有Patient集合, 想要查询PatientName为&qu ...
- MSTAR 平台
MApp_Menu.c ZUI_exefunc.h //菜单属性 MApp_ZUI_APItables.h #define GETWNDINFO(hwnd) (&g_GUI_WindowLis ...
- 用idea工具对java打包:命令 mvn clear package,报错
用idea工具对java打包:命令 mvn clear package,报错 网上都是eclipse的,要么是project structure和setting的(当然这俩也要用) 我都试了,每一个能 ...
- Python中的数据结构和算法
一.算法 1.算法的时间复杂度 大 O 记法,是描述算法复杂度的符号O(1) 常数复杂度,最快速的算法. 取数组第 1000000 个元素 字典和集合的存取都是 O(1) 数组的存取是 O(1) O( ...
- 【机器学习】决策树C4.5、ID3
一.算法流程 step1:计算信息熵 step2: 划分数据集 step3: 创建决策树 step4: 利用决策树分类 二.信息熵Entropy.信息增益Gain 重点:选择一个属性进行分支.注意信息 ...
- 性能压测,SQL查询异常
早上测试对性能压测,发现取sequence服务大量超时报错,查询线上的监控SQL: 大量这个查询,我在DeviceID和Isdelete上建有复合索引,应该很快,而且我测试了一下,取值,执行效率很高, ...
- CodeForces - 820
Mister B and Book ReadingCodeForces - 820A 题意:C,V0,V1,A,L..总共有C页书,第一天以V0速度读,每天加A,但是不能超过V1,并且要从前一天的看到 ...
- C语言实现折半插入算法
#include <stdio.h> int BInsertSort(int array[],int left,int right){ //接收主函数调用语句中的实参传到这里的形参里 in ...