一步步實現本身的ORM(四)

經過前3章文章,大體對ORM有必定的瞭解,但也存在效率低下(大量用了反射)和重複代碼,今天咱們要對ORM進行優化。sql

具體流程以下:數據庫

咱們優化的第一個就是減小反射調用,個人思路是定義一個Mapping,把表名、字段名信息緩存起來,EntityMapping 表示實體類信息對應數據庫中的table,MemberMapping表示實體類的屬性對應數據庫中的Column。緩存

EntityMapping代碼:app

    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代碼:ide

 

    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,具體代碼見附件下載。ui

Mapping信息有了,咱們須要作的是把重構以前的EntityHelper類,而這個類裏有大量的連接數據庫、操做數據庫代碼,咱們把這些信息抽離到一個單獨類,就比如咱們經常使用的DbHelper類,而這個類我命名爲DbProvider。this

    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。spa

    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)。code

將數據庫操做和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

相關文章
相關標籤/搜索