在第一篇《一步步实现自己的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. 单机 Oracle 11g(11.2.0.4)手动打补丁PSU(11.2.0.4.8)

    环境说明:database : 11.2.0.4 x64os: centos6.7 x64 准备内容:OPatch : p6880880_112000_Linux-x86-64.zipDB PSU : ...

  2. 查看SELinux状态&关闭SELinux

    1. 查看SELinux状态 1.1 getenforce getenforce 命令是单词get(获取)和enforce(执行)连写,可查看selinux状态,与setenforce命令相反. se ...

  3. asp.net mvc Model验证总结及常用正则表达式【转载】

    关于Model验证官方资料: http://msdn.microsoft.com/zh-cn/library/system.componentmodel.dataannotations.aspx AS ...

  4. deleteMany is not a function

    问题: 同事使用了deleteMany方法用于删除数据,但是全公司只有我一个人报错deleteMany is not a function. 很自然,输出了model.deleteMany,得到的结果 ...

  5. CodeForces 1097G. Vladislav and a Great Legend

    题目简述:给定$n \leq 10^5$个节点的树$T = (V, E)$,令$X \subseteq V$表示一个非空节点集合,定义$f(X)$为包含$X$的最小子树的边数.求 $$ \sum_{\ ...

  6. CF-839B

    B. Game of the Rows time limit per test 1 second memory limit per test 256 megabytes input standard ...

  7. 读取web应用下的资源文件(例如properties)

    package gz.itcast.b_resource; import java.io.IOException; import java.io.InputStream; import java.ut ...

  8. 文件解析库doctotext安装和使用

    安装doctotext 1 安装GCC到4.6以上 tar jxf gcc-4.7.0.tar.bz2 cd gcc-4.7.0 编译 ./contrib/download_prerequisites ...

  9. 在windwo server2008服务器上配置ftp服务器、及配置phpstrom工具、实现项目同步。

    在windwo server2008服务器上配置ftp服务器.及配置phpstrom工具.实现项目同步. 在windwo server2008服务器上配置ftp服务器 参考该篇文章:http://bl ...

  10. 数组,for语句(补10.11)

    1.数组定义:一系列通数据类型的数据集合. 2.数组赋值的两种方法: 先定义后赋值:(赋值从0开始) var aa = new Arrey(); aa[0] = 1; aa[1] = 2; 定义并赋值 ...