• 一步步实现自己的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; }
           
        }
    View Code

    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; }
        }
    View Code

    有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 = 0; 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 = 0; i < values.Length; i++)
                {
                    if (i > 0) //考虑到有多个主键
                        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 = 0; 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;
            }
        }
    View Code

    本章结束,下一章要将DataReader转换成实体类的代码优化。

    下载地址:http://files.cnblogs.com/files/sobaby/ORM04.zip

  • 相关阅读:
    扫盲-关系数据库的第一第二第三范式
    Arcgis SOE学习
    JavaScript学习笔记(持续更新)
    JavaScript学习心得
    Vue学习
    扫盲-ES6
    JavaScript原理学习
    (转)扫盲--JavaScript的立即执行函数
    (*)(转)要快速学习SSM框架,你需要一套学习曲线平滑的教程
    网络爬虫研发重点介绍
  • 原文地址:https://www.cnblogs.com/sobaby/p/4378377.html
Copyright © 2020-2023  润新知