通过前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

最新文章

  1. button 样式
  2. HBase简介
  3. 2016年10月15日 星期六 --出埃及记 Exodus 18:26
  4. hdu 1180 诡异的楼梯(广搜,简单)
  5. CentOS 7.0 重置root密码
  6. iOS 获取当前时间以及计算年龄(时间差)
  7. iOS多线程--深度解析
  8. 点评cat系列-应用集成
  9. 【Java编程思想笔记】注解1-简单了解注解
  10. 华为交换机配置stelnet登陆的实例
  11. [luogu P2521] [HAOI2011]防线修建
  12. flask 数据库迁移的简单操作
  13. 联想功能 Jquery autocomplete.js输入框联想补全功能
  14. Nmap扫描常用参数
  15. java自动装箱的一个例子
  16. 视角同步NewViewTarget
  17. Scala进阶之路-Scala的基本语法
  18. mysql 连接查询 join
  19. split函数和merge函数
  20. Redis搭建(七):Redis的Cluster集群动态增删节点

热门文章

  1. hex2bin
  2. TCPDUMP 使用详情
  3. [SHOI 2017] 寿司餐厅
  4. vue.js created函数注意事项
  5. eclipse 怎么关闭 show children
  6. 如何正确遍历删除List中的元素,你会吗?
  7. error:: undefined reference to symbol &#39;__glewBufferSubData&#39; 未定义的引用 以及 error: main.o: undefined reference to symbol &#39;glTexImage2D&#39;
  8. 微信小程序开发之真机预览
  9. springMVC拦截配置
  10. iOS 8 之后的动态沙盒路径