一步步实现自己的ORM(四)
通过前3章文章,大致对ORM有一定的了解,但也存在效率低下(大量用了反射)和重复代码,今天我们要对ORM进行优化。
具体流程如下:

我们优化的第一个就是减少反射调用,我的思路是定义一个Mapping,把表名、字段名信息缓存起来,EntityMapping 表示实体类信息对应数据库中的table,MemberMapping表示实体类的属性对应数据库中的Column。
EntityMapping代码:
class EntityMapping
{
/// <summary>
/// 对应实体类类型
/// </summary>
public Type EntityType { get; internal set; } /// <summary>
/// 表名
/// </summary>
public string TableName { get; set; } /// <summary>
/// 实体属性
/// </summary>
public List<MemberMapping> Members; /// <summary>
/// 主键
/// </summary>
public List<MemberMapping> PrimaryKey { get; set; } }
MemberMapping代码:
class MemberMapping
{
/// <summary>
/// 列名
/// </summary>
public string ColumnName { get; internal set; } /// <summary>
/// 属性名
/// </summary>
public PropertyInfo Member { get; set; } /// <summary>
/// 是否为主键
/// </summary>
public bool IsPrimaryKey { get; set; } /// <summary>
/// 是否为数据库自动生成
/// </summary>
public bool IsDbGenerated { get; set; }
}
有Mapping后,我们现在要做的是把实体类里的信息存到Mapping类中,我在这里定义了一个AttributeMapping,里面方法如下:
class AttributeMapping
{
private static Dictionary<Type, EntityMapping> entityMappings = new Dictionary<Type, EntityMapping>(); public static EntityMapping Get<T>() public static EntityMapping Get(Type type) private static EntityMapping CreateMapping(Type type)
}
其中Get方法就是根据实体类返回Mapping信息,而CreateMapping则是创建Mapping,具体代码见附件下载。
Mapping信息有了,我们需要做的是把重构之前的EntityHelper类,而这个类里有大量的链接数据库、操作数据库代码,我们把这些信息抽离到一个单独类,就好比我们常用的DbHelper类,而这个类我命名为DbProvider。
class DbProvider
{
private IDbConnection conntion;
public DbProvider(IDbConnection conntion)
public virtual int ExecuteNonQuery(string sql, Dictionary<string, object> parameters)
public virtual IDataReader ExecuteReader(string sql, Dictionary<string, object> parameters)
}
EntityHelper类还有大量的SQL生成语句,我们为了简化EntityHelper类把SQL生成的代码放到单独的DbSqlBuilder类,为啥要这么做呢?除了简化代码外,还有就是为了适应不同的Db。
class DbSqlBuilder
{
public string BuildInsertSql(EntityMapping entityMapping, List<string> columnNames) public string BuildUpdateSql(EntityMapping entityMapping, List<string> updateColumns,List<string> whereColumns) public string BuildDeleteSql(EntityMapping entityMapping, List<string> whereColumns) public string BuildSelectSql(EntityMapping entityMapping,string strWhere, string orderBy)
}
为什么参数里要传递List<string>呢?因为我们拼SQL语句要用到列名的,修改后的EntityHelper(我在这里将名字改成DbContext)。
将数据库操作和SQL生成代码放到单独类里,回头再看下DbContext代码,比原来的简洁了很多。
class DbContext
{
private DbProvider dbProvider;
private DbSqlBuilder sqlBuilder ; public DbContext(string connectionString)
{
SqlConnection conn = new SqlConnection(connectionString);
dbProvider = new DbProvider(conn);
this.sqlBuilder = new DbSqlBuilder();
} public int Insert<T>(object entity)
{
var entityMapping = AttributeMapping.Get<T>(); //将Entity转换成Dictionary
var parameters = DynamicMethodBuilder.ConvertFromObject(entity);
var sql = sqlBuilder.BuildInsertSql(entityMapping, parameters.Keys.ToList());
return dbProvider.ExecuteNonQuery(sql, parameters);
} public int Update<T>(T entity)
{
var entityMapping = AttributeMapping.Get<T>();
//将Entity转换成Dictionary
var parameters = DynamicMethodBuilder.ConvertFromObject(entity);
var columns = entityMapping.Members.Where(m => m.IsDbGenerated == false && m.IsPrimaryKey == false).Select(c => c.ColumnName).ToArray();
var updateColumns = new Dictionary<string, object>();
var whereColumns = new Dictionary<string, object>(); foreach (var item in parameters)
{
if (columns.Contains(item.Key))
updateColumns.Add(item.Key, item.Value);
if (entityMapping.PrimaryKey.All(m => m.ColumnName == item.Key))
whereColumns.Add(item.Key, item.Value);
} var sql = sqlBuilder.BuildUpdateSql(entityMapping, updateColumns.Keys.ToList(), whereColumns.Keys.ToList()); return dbProvider.ExecuteNonQuery(sql, parameters);
} public int DeleteByKey<T>(params object[] values)
{
var entityMapping = AttributeMapping.Get<T>(); if (values.Length != entityMapping.PrimaryKey.Count)
throw new ArgumentException("参数个数和主键数不一致"); var parameters = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
for (int i = ; i < entityMapping.PrimaryKey.Count; i++)
{
parameters.Add(entityMapping.PrimaryKey[i].ColumnName, values[i]);
} var sql = sqlBuilder.BuildDeleteSql(entityMapping, parameters.Keys.ToList()); return dbProvider.ExecuteNonQuery(sql, parameters);
} public T Get<T>(object[] values)
{
var entityMapping = AttributeMapping.Get<T>(); if (values.Length != entityMapping.PrimaryKey.Count)
throw new ArgumentException("参数个数和主键数不一致"); var parameters = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); StringBuilder where = new StringBuilder(); for (int i = ; i < values.Length; i++)
{
if (i > ) //考虑到有多个主键
where.Append(" AND "); where.Append(entityMapping.PrimaryKey[i].ColumnName).Append("=").Append("@p").Append(i); /*参数*/
parameters.Add("@p" + i, values[i]);
} var sql = this.sqlBuilder.BuildSelectSql(entityMapping, where.ToString(), string.Empty); return GetEntityList<T>(sql, parameters).FirstOrDefault();
} public List<T> GetList<T>(string where, string orderBy)
{
var entityMapping = AttributeMapping.Get<T>(); var sql = this.sqlBuilder.BuildSelectSql(entityMapping, where.ToString(), orderBy); return GetEntityList<T>(sql, null);
} public List<T> GetEntityList<T>(string sql, Dictionary<string, object> parameters)
{ var reader = dbProvider.ExecuteReader(sql, parameters);
List<T> list = new List<T>();
var type = typeof(T);
var properties = type.GetProperties();
while (reader.Read())
{
var user = Activator.CreateInstance(type);
for (int i = ; i < properties.Length; i++)
{
var pi = properties[i];
if (reader[pi.Name] != null) //等同于 if (reader["UserId"] != null)这样的语句
pi.SetValue(user, reader[pi.Name], null); //等同于 user.UserId = (int)reader["UserId"];
} list.Add((T)user);
}
return list;
}
}
本章结束,下一章要将DataReader转换成实体类的代码优化。
下载地址:http://files.cnblogs.com/files/sobaby/ORM04.zip
一步步实现自己的ORM(四)的更多相关文章
- 一步步实现自己的ORM(二)
在第一篇<一步步实现自己的ORM(一)>里,我们用反射获取类名.属性和值,我们用这些信息开发了简单的INSERT方法,在上一篇文章里我们提到主键为什么没有设置成自增长类型,单单从属性里我们 ...
- 一步步实现自己的ORM(一)
最近在研究ORM,尝试着自己开发了一个简单的ORM.我个人不喜欢EF因为跟不上EF升级太快了,再说公司里还停留在c# 3.5时代,对于NHibernate配置太复杂看到就头晕,就心生自己做一个ORM的 ...
- 一步步实现自己的ORM(三)
章节列表: <一步步实现自己的ORM(一)> <一步步实现自己的ORM(二)> 通过前面两篇文章,我们大致了解了ORM的基本原理,是通过Attribute+反射获取表的基本信息 ...
- 一步步写STM32 OS【四】OS基本框架
一.上篇回顾 上一篇文章中,我们完成了两个任务使用PendSV实现了互相切换的功能,下面我们接着其思路往下做.这次我们完成OS基本框架,即实现一个非抢占式(已经调度的进程执行完成,然后根据优先级调度等 ...
- 一步步实现自己的ORM(五)
上一张优化了ORM的INSERT.UPDATE.DELETE,但将数据库里的值填充到实体类这块还没优化.另外有博友在网上咨询说你这个都是查询所有字段的,而他的需求是按需查询字段,不是一次性取出来所有字 ...
- Django ORM (四) annotate,F,Q 查询
annotate 可以通过计算查询结果中每一个对象所关联的对象集合,从而得出总计值(也可以是平均值或总和),即为查询集的每一项生成聚合. from django.shortcuts import re ...
- 第四次团队作业——项目Alpha版本发布
这个作业属于哪个课程 <课程的链接> 这个作业要求在哪里 <作业要求的链接> 团队名称 Three cobblers 这个作业的目标 发布项目α版本,对项目进 ...
- orm(Manager isn't accessible via %s instances" % cls.__name)报错
报错信息 Manager isn't accessible via %s instances" % cls.__name 解决方法 https://www.jianshu.com/p/5e0 ...
- confluence5.6安装
转自:http://ju.outofmemory.cn/entry/157013 说明:此文在confluence-wiki-5.6.5版本亲测通过附件:http://pan.baidu.com/s/ ...
随机推荐
- Linux-Nginx和NFS
1 虚拟化 查看系统信息 cat /proc/meninfo cat /proc/cpuinfo 其中 flags里面的信息可以查看该cpu是否支持虚拟化 flags上有vmx svm等表示可以虚拟化 ...
- BZOJ_1004_[HNOI2008]Cards_burnside+DP
BZOJ_1004_[HNOI2008]Cards_burnside+DP Description 小春现在很清闲,面对书桌上的N张牌,他决定给每张染色,目前小春只有3种颜色:红色,蓝色,绿色.他询问 ...
- Android 4.2开发环境搭建
一.工具 jdk1.7; eclipse 4.3(for java ee); Android SDK; 二.安装JDK并配置 安装略,配置如下: 右击 “我的电脑”->属性->高级系统设置 ...
- MySQL(12)---纪录一次left join一对多关系而引起的BUG
MySQL(11)---纪录一次left join一对多关系而引起的bug BUG背景 我们有一个订单表 和 一个 物流表 它们通过 订单ID 进行一对一的关系绑定.但是由于物流表在保存订单信息的时候 ...
- STL——pair
功能:pair将一对值组合成一个值,这一对值可以具有不同的数据类型(T1和T2),两个值可以分别用pair的两个公有函数first和second访问. #include <bits/stdc++ ...
- 609. Find Duplicate File in System
Given a list of directory info including directory path, and all the files with contents in this dir ...
- HDU2604【矩阵快速幂】
思路: 把fm看成01,f-1,m-0: 不能存在101,111; dp[i]代表第i结尾的方案数: ①:结尾是0一定行:只要i-1序列里添个0就好了,dp[i]+=dp[i-1]: ②:结尾是1 ...
- Mol Cell Proteomics. |胡丹丹| 雷公藤红素通过SIRT1-FXR 信号通路保护胆汁淤积性肝损伤
期刊:Mol Cell Proteomics 题目:Celastrol protects from cholestatic liver injury though modulation of SIRT ...
- 第二篇 Nosql讲解之windows下memcache的安装(一)
memcached基本概念 1.Memcached是danga的一个项目,最早是LiveJournal服务的,最初为了加速LiveJournal访问速度而开发的,后来被很多大型的网站采用. 官方网站: ...
- samba服务器实验指导
第一节.samba是干什么的?它有什么用? Samba(SMB是其缩写) 是一个网络服务器,它是Linux作为本地服务器最重要的一个服务,用于Linux和Windows共享文件之用:Samba可以用于 ...