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

上一張優化了ORM的INSERT、UPDATE、DELETE,但將數據庫裏的值填充到實體類這塊還沒優化。另外有博友在網上諮詢說你這個都是查詢全部字段的,而他的需求是按需查詢字段,不是一次性取出來全部字段的,在這裏我請這位朋友耐心等待,這個會在後面章節提到的。此次咱們先優化datareader->entity,將數據庫裏的值Mapping到實體類裏,咱們經常使用的有兩種辦法第一種是採用EMIT方式,第二種是採用Expression tree表達式,這兩種方案在性能上差別不大,但寫法上第二種較第一種比較容易,我在這裏採用Expression tree表達式來完成實體類轉換操做。數據庫

讓咱們先來看下Expression 表達式如何將DataReader轉換成Object:app

            IDataReader reader = null;
            Expression<Func<IDataReader, User>> expr = (r) => new User()
            {
                UserId = r.GetInt32(0),
                CreatedTime = r.GetDateTime(1),
                Email = r.GetString(2),
            };

            var func = expr.Compile();
            func(reader);

在new User的時候採用對象初始化方式給屬性賦值,而在實際項目中咱們會遇到更復雜的,好比還要判斷是否爲DbNull,要否則轉換會出錯,在這裏我定義一個GetValue<T>(int i)方法,專門作取值操做。函數

    class DbFieldReader
    {
        private IDataReader reader;
        public DbFieldReader(IDataReader reader)
        {
            this.reader = reader;
        }

        public T GetValue<T>(int index)
        {
            object value = reader.GetValue(index);

            if (value == DBNull.Value)
                return default(T);

            return (T)reader.GetValue(index);
        }
    }

如今將代碼修改下,性能

            IDataReader reader = null;
            DbFieldReader fr = new DbFieldReader(reader);
            Expression<Func<DbFieldReader, User>> expr = (r) => new User()
            {
                UserId = r.GetValue<int>(0),
                CreatedTime = r.GetValue<DateTime>(1),
                Email = r.GetValue<string>(2),
            };

            var func = expr.Compile();
            func(fr);

是否是簡潔了不少?把判斷代碼轉移到DbFieldReader類中處理,如今咱們要將這個方法作成通用實體類轉換,先分解下Expression表達式(如吐有錯誤,懇請大神指教)。優化

第一步定義參數,類型爲DbFieldReader,參數名:rthis

ParameterExpression parExpr = Expression.Parameter(typeof(DbFieldReader), "r");

第二步,調用User類的構造函數,代碼以下:spa

var newExpr = Expression.New(type.GetConstructors().First());

合併這2個表達式和運行結果:code

Expression<Func<DbFieldReader, User>> expr = (Expression<Func<DbFieldReader, User>>)Expression.Lambda(newExpr, parExpr);

基本的雛形出來了,如今要作的就是給屬性賦值,怎麼來呢?先來個簡單的,就是給固定值。對象

var userid = Expression.Bind(type.GetProperty("UserId"), Expression.Constant(1, typeof(int)));

這就至關於UserId = 1的賦值操做,繼續合併表達式:blog

          var type = typeof(User);
            ParameterExpression parExpr = Expression.Parameter(typeof(DbFieldReader), "r");

            var newExpr = Expression.New(type.GetConstructors().First());

            var userid = Expression.Bind(type.GetProperty("UserId"), Expression.Constant(1, typeof(int)));
            var CreatedTime = Expression.Bind(type.GetProperty("CreatedTime"), Expression.Constant(DateTime.Now, typeof(DateTime)));

            var init = Expression.MemberInit(newExpr, userid, CreatedTime);
            Expression<Func<DbFieldReader, User>> expr = (Expression<Func<DbFieldReader, User>>)Expression.Lambda(init, parExpr);

常量賦值沒問題後,下面就要來調用方法了。

            var method = typeof(DbFieldReader).GetMethods().Where(c => c.Name == "GetValue" && c.IsGenericMethod).First();
            var callExpr = Expression.Call(parExpr, method.MakeGenericMethod(typeof(int)), Expression.Constant(0));
       var userid = Expression.Bind(type.GetProperty("UserId"), callExpr);

這句話至關於 r.GetValue<int>(0);

先獲取GetValue方法,而後調用MakeGenericMethod生成泛型方法,Expression.Constant(0)就是參數值。

完整版代碼:

            IDataReader reader = null;
            DbFieldReader fr = new DbFieldReader(reader);

            var entityMapping = AttributeMapping.Get<User>();
            ParameterExpression parExpr = Expression.Parameter(typeof(DbFieldReader), "r");


            var newExpr = Expression.New(entityMapping.EntityType.GetConstructors().First());

            var method = typeof(DbFieldReader).GetMethods().Where(c => c.Name == "GetValue" && c.IsGenericMethod).First();

            List<MemberBinding> memberBindings = new List<MemberBinding>();
            int index = 0;
            foreach (var item in entityMapping.Members)
            {
                var callExpr = Expression.Call(parExpr, method.MakeGenericMethod(item.Member.PropertyType), Expression.Constant(index));
                var memberAssignment = Expression.Bind(item.Member, callExpr);
                memberBindings.Add(memberAssignment);
                index++;
            }

            var init = Expression.MemberInit(newExpr, memberBindings);
            Expression<Func<DbFieldReader, User>> expr = (Expression<Func<DbFieldReader, User>>)Expression.Lambda(init, parExpr);

固然咱們還須要修改SQL語句,不能用SELECT * 語法,而是要用SELECT userid,email...from這樣的語法

最終的代碼代碼下載:

http://files.cnblogs.com/files/sobaby/ORM05.zip

相關文章
相關標籤/搜索