NewLife/X

v9.8.2018.0605   由DataReader直接映射实体列表,以支持netstandard的MySql和SQLite,且提升性能
大石头 authored at 2018-06-05 00:45:23
64fa08b
Tree
1 Parent(s) 1b3755d
Summary: 13 changed files with 189 additions and 26 deletions.
Modified +8 -4
Modified +3 -0
Modified +43 -0
Modified +8 -0
Modified +17 -0
Modified +7 -3
Modified +2 -0
Modified +48 -6
Modified +2 -2
Modified +27 -6
Modified +18 -3
Modified +4 -2
Modified +2 -0
Modified +8 -4
diff --git a/TestST/Program.cs b/TestST/Program.cs
index d91b22e..68f609e 100644
--- a/TestST/Program.cs
+++ b/TestST/Program.cs
@@ -6,6 +6,7 @@ using Microsoft.Extensions.Configuration;
 using NewLife.Log;
 using NewLife.Net;
 using XCode.DataAccessLayer;
+using XCode.Membership;
 
 namespace TestST
 {
@@ -67,13 +68,16 @@ namespace TestST
             //var fact = MySqlClientFactory.Instance;
             var fact = SqliteFactory.Instance;
 
-            var dal = DAL.Create("Sqlite");
+            //var dal = DAL.Create("Sqlite");
             //DAL.AddConnStr("Membership", "Server=.;Port=3306;Database=world;Uid=root;Pwd=root", null, "MySql");
-            //var dal = DAL.Create("Membership");
+            var dal = DAL.Create("Membership");
             Console.WriteLine(dal.Db.ConnectionString);
 
-            var ds = dal.Select("select * from city");
-            Console.WriteLine(ds.Tables[0].Rows.Count);
+            //var ds = dal.Select("select * from city");
+            //Console.WriteLine(ds.Tables[0].Rows.Count);
+
+            var user = UserX.FindByName("admin");
+            Console.WriteLine(user);
 
             //var n = UserX.Meta.Count;
             //Console.WriteLine(n);
Modified +3 -0
diff --git a/XCode/DataAccessLayer/Common/DbBase.cs b/XCode/DataAccessLayer/Common/DbBase.cs
index 2c8740d..69609ee 100644
--- a/XCode/DataAccessLayer/Common/DbBase.cs
+++ b/XCode/DataAccessLayer/Common/DbBase.cs
@@ -818,6 +818,9 @@ namespace XCode.DataAccessLayer
 
         /// <summary>获取 或 设置 自动关闭。每次使用完数据库连接后,是否自动关闭连接,高频操作时设为false可提升性能。默认true</summary>
         public Boolean AutoClose { get; set; } = true;
+
+        /// <summary>是否支持Schema。默认true</summary>
+        public Boolean SupportSchema { get; set; } = true;
         #endregion
 
         #region 辅助函数
Modified +43 -0
diff --git a/XCode/DataAccessLayer/Common/DbSession.cs b/XCode/DataAccessLayer/Common/DbSession.cs
index 04474da..17dfcfc 100644
--- a/XCode/DataAccessLayer/Common/DbSession.cs
+++ b/XCode/DataAccessLayer/Common/DbSession.cs
@@ -324,6 +324,49 @@ namespace XCode.DataAccessLayer
             }
         }
 
+        /// <summary>执行SQL查询,返回记录集</summary>
+        /// <param name="sql">SQL语句</param>
+        /// <param name="type">命令类型,默认SQL文本</param>
+        /// <param name="ps">命令参数</param>
+        /// <param name="convert">转换器</param>
+        /// <returns>记录集</returns>
+        public virtual T Query<T>(String sql, CommandType type, IDataParameter[] ps, Func<IDataReader, T> convert)
+        {
+            using (var cmd = OnCreateCommand(sql, type, ps))
+            {
+                Transaction?.Check(cmd, false);
+
+                QueryTimes++;
+                WriteSQL(cmd);
+
+                using (var pi = Database.Pool.AcquireItem())
+                {
+                    try
+                    {
+                        //if (!Opened) Open();
+                        if (cmd.Connection == null) cmd.Connection = pi.Value;
+
+                        BeginTrace();
+                        using (var dr = cmd.ExecuteReader())
+                        {
+                            return convert(dr);
+                        }
+                    }
+                    catch (DbException ex)
+                    {
+                        // 数据库异常最好销毁连接
+                        cmd.Connection.TryDispose();
+
+                        throw OnException(ex, cmd);
+                    }
+                    finally
+                    {
+                        EndTrace(cmd);
+                    }
+                }
+            }
+        }
+
         private static Regex reg_QueryCount = new Regex(@"^\s*select\s+\*\s+from\s+([\w\W]+)\s*$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
         /// <summary>执行SQL查询,返回总记录数</summary>
         /// <param name="sql">SQL语句</param>
Modified +8 -0
diff --git a/XCode/DataAccessLayer/Common/IDbSession.cs b/XCode/DataAccessLayer/Common/IDbSession.cs
index d41e2f5..5f12a12 100644
--- a/XCode/DataAccessLayer/Common/IDbSession.cs
+++ b/XCode/DataAccessLayer/Common/IDbSession.cs
@@ -94,6 +94,14 @@ namespace XCode.DataAccessLayer
         /// <returns>记录集</returns>
         DataSet Query(DbCommand cmd);
 
+        /// <summary>执行SQL查询,返回记录集</summary>
+        /// <param name="sql">SQL语句</param>
+        /// <param name="type">命令类型,默认SQL文本</param>
+        /// <param name="ps">命令参数</param>
+        /// <param name="convert">转换器</param>
+        /// <returns>记录集</returns>
+        T Query<T>(String sql, CommandType type, IDataParameter[] ps, Func<IDataReader, T> convert);
+
         /// <summary>执行SQL查询,返回总记录数</summary>
         /// <param name="sql">SQL语句</param>
         /// <param name="type">命令类型,默认SQL文本</param>
Modified +17 -0
diff --git a/XCode/DataAccessLayer/DAL_DbOperate.cs b/XCode/DataAccessLayer/DAL_DbOperate.cs
index 3921097..f951671 100644
--- a/XCode/DataAccessLayer/DAL_DbOperate.cs
+++ b/XCode/DataAccessLayer/DAL_DbOperate.cs
@@ -58,6 +58,23 @@ namespace XCode.DataAccessLayer
             return Session.Query(builder.ToString(), CommandType.Text, builder.Parameters.ToArray());
         }
 
+        /// <summary>执行SQL查询,返回记录集</summary>
+        /// <param name="builder">SQL语句</param>
+        /// <param name="startRowIndex">开始行,0表示第一行</param>
+        /// <param name="maximumRows">最大返回行数,0表示所有行</param>
+        /// <param name="convert">转换器</param>
+        /// <returns></returns>
+        public T Query<T>(SelectBuilder builder, Int64 startRowIndex, Int64 maximumRows, Func<IDataReader, T> convert)
+        {
+            builder = PageSplit(builder, startRowIndex, maximumRows);
+            if (builder == null) return default(T);
+
+            CheckBeforeUseDatabase();
+
+            Interlocked.Increment(ref _QueryTimes);
+            return Session.Query(builder.ToString(), CommandType.Text, builder.Parameters.ToArray(), convert);
+        }
+
         /// <summary>执行SQL查询,返回总记录数</summary>
         /// <param name="sb">查询生成器</param>
         /// <returns></returns>
Modified +7 -3
diff --git a/XCode/DataAccessLayer/DAL.cs b/XCode/DataAccessLayer/DAL.cs
index b67b209..f6f93a1 100644
--- a/XCode/DataAccessLayer/DAL.cs
+++ b/XCode/DataAccessLayer/DAL.cs
@@ -168,9 +168,9 @@ namespace XCode.DataAccessLayer
                         // 读取配置文件
 
                         var css2 = new ConfigurationBuilder().AddJsonFile(settings).Build().GetSection("connectionStrings");
-//                        var css2 = new ConfigurationBuilder()
-//.Add(new JsonConfigurationSource { Path = "appsettings.json", ReloadOnChange = true })
-//.Build().GetSection("connectionStrings");
+                        //                        var css2 = new ConfigurationBuilder()
+                        //.Add(new JsonConfigurationSource { Path = "appsettings.json", ReloadOnChange = true })
+                        //.Build().GetSection("connectionStrings");
                         if (css2 != null)
                         {
                             foreach (var item in css2.GetChildren())
@@ -390,6 +390,8 @@ namespace XCode.DataAccessLayer
 
         private List<IDataTable> GetTables()
         {
+            if (Db is DbBase db2 && !db2.SupportSchema) return new List<IDataTable>();
+
             CheckBeforeUseDatabase();
             return Db.CreateMetaData().GetTables();
         }
@@ -525,6 +527,8 @@ namespace XCode.DataAccessLayer
         /// <param name="tables"></param>
         public void SetTables(params IDataTable[] tables)
         {
+            if (Db is DbBase db2 && !db2.SupportSchema) return;
+
             // 构建DataTable时也要注意表前缀,避免反向工程用错
             var pf = Db.TablePrefix;
             if (!pf.IsNullOrEmpty())
Modified +2 -0
diff --git a/XCode/DataAccessLayer/Database/SQLite.cs b/XCode/DataAccessLayer/Database/SQLite.cs
index fd2a7b4..d989ea9 100644
--- a/XCode/DataAccessLayer/Database/SQLite.cs
+++ b/XCode/DataAccessLayer/Database/SQLite.cs
@@ -98,6 +98,8 @@ namespace XCode.DataAccessLayer
                 // 自动清理数据
                 if (builder.TryGetAndRemove("autoVacuum", out var vac)) AutoVacuum = vac.ToBoolean();
             }
+            else
+                SupportSchema = false;
 
             // 默认超时时间
             //if (!builder.ContainsKey("Default Timeout")) builder["Default Timeout"] = 5 + "";
Modified +48 -6
diff --git a/XCode/Entity/DataRowEntityAccessor.cs b/XCode/Entity/DataRowEntityAccessor.cs
index 695e1f2..14bf27f 100644
--- a/XCode/Entity/DataRowEntityAccessor.cs
+++ b/XCode/Entity/DataRowEntityAccessor.cs
@@ -1,8 +1,7 @@
 using System;
 using System.Collections.Generic;
 using System.Data;
-using System.Linq;
-using NewLife.Reflection;
+using System.Data.Common;
 using XCode.Configuration;
 using XCode.DataAccessLayer;
 
@@ -15,6 +14,11 @@ namespace XCode
         /// <param name="dt">数据表</param>
         /// <returns>实体数组</returns>
         IList<T> LoadData<T>(DataTable dt) where T : Entity<T>, new();
+
+        /// <summary>加载数据表。无数据时返回空集合而不是null。</summary>
+        /// <param name="dr">数据读取器</param>
+        /// <returns>实体数组</returns>
+        IList<T> LoadData<T>(IDataReader dr) where T : Entity<T>, new();
     }
 
     /// <summary>在数据行和实体类之间映射数据接口的提供者</summary>
@@ -31,10 +35,7 @@ namespace XCode
         /// <summary>创建实体类的数据行访问器</summary>
         /// <param name="entityType"></param>
         /// <returns></returns>
-        public IDataRowEntityAccessor CreateDataRowEntityAccessor(Type entityType)
-        {
-            return new DataRowEntityAccessor();
-        }
+        public IDataRowEntityAccessor CreateDataRowEntityAccessor(Type entityType) => new DataRowEntityAccessor();
     }
 
     class DataRowEntityAccessor : IDataRowEntityAccessor
@@ -78,6 +79,47 @@ namespace XCode
             }
             return list;
         }
+
+        /// <summary>加载数据表。无数据时返回空集合而不是null。</summary>
+        /// <param name="dr">数据读取器</param>
+        /// <returns>实体数组</returns>
+        public IList<T> LoadData<T>(IDataReader dr) where T : Entity<T>, new()
+        {
+            // 准备好实体列表
+            var list = new List<T>();
+            if (dr == null) return list;
+
+            var dr2 = dr as DbDataReader;
+
+            // 对应数据表中字段的实体字段
+            var ps = new Dictionary<Int32, FieldItem>();
+            // 数据表中找不到对应的实体字段的数据字段
+            var exts = new Dictionary<Int32, String>();
+            var ti = Entity<T>.Meta.Table;
+            for (var i = 0; i < dr2.FieldCount; i++)
+            {
+                var name = dr2.GetName(i);
+                if (ti.FindByName(name) is FieldItem fi)
+                    ps.Add(i, fi);
+                else
+                    exts.Add(i, name);
+            }
+
+            // 遍历每一行数据,填充成为实体
+            while(dr.Read())
+            {
+                // 由实体操作者创建实体对象,因为实体操作者可能更换
+                var entity = Entity<T>.Meta.Factory.Create() as T;
+                foreach (var item in ps)
+                    SetValue(entity, item.Value.Name, item.Value.Type, dr[item.Key]);
+
+                foreach (var item in exts)
+                    SetValue(entity, item.Value, null, dr[item.Key]);
+
+                list.Add(entity);
+            }
+            return list;
+        }
         #endregion
 
         #region 方法
Modified +2 -2
diff --git a/XCode/Entity/Entity_Operate.cs b/XCode/Entity/Entity_Operate.cs
index 60001dd..5fed0c8 100644
--- a/XCode/Entity/Entity_Operate.cs
+++ b/XCode/Entity/Entity_Operate.cs
@@ -75,12 +75,12 @@ namespace XCode
             /// <summary>创建一个实体对象</summary>
             /// <param name="forEdit">是否为了编辑而创建,如果是,可以再次做一些相关的初始化工作</param>
             /// <returns></returns>
-            public virtual IEntity Create(Boolean forEdit = false) { return (Default as TEntity).CreateInstance(forEdit) as TEntity; }
+            public virtual IEntity Create(Boolean forEdit = false) => (Default as TEntity).CreateInstance(forEdit) as TEntity;
 
             /// <summary>加载记录集</summary>
             /// <param name="ds">记录集</param>
             /// <returns>实体数组</returns>
-            public virtual IList<IEntity> LoadData(DataSet ds) { return Entity<TEntity>.LoadData(ds).Cast<IEntity>().ToList(); }
+            public virtual IList<IEntity> LoadData(DataSet ds) => Entity<TEntity>.LoadData(ds).Cast<IEntity>().ToList();
             #endregion
 
             #region 查找单个实体
Modified +27 -6
diff --git a/XCode/Entity/Entity.cs b/XCode/Entity/Entity.cs
index 6745fa2..957757d 100644
--- a/XCode/Entity/Entity.cs
+++ b/XCode/Entity/Entity.cs
@@ -90,6 +90,26 @@ namespace XCode
             if (dt == null) return new List<TEntity>();
 
             var list = DreAccessor.LoadData<TEntity>(dt);
+            OnLoadData(list);
+
+            return list;
+        }
+
+        /// <summary>加载数据表。无数据时返回空集合而不是null。</summary>
+        /// <param name="dr">数据读取器</param>
+        /// <returns>实体数组</returns>
+        public static IList<TEntity> LoadData(IDataReader dr)
+        {
+            if (dr == null) return new List<TEntity>();
+
+            var list = DreAccessor.LoadData<TEntity>(dr);
+            OnLoadData(list);
+
+            return list;
+        }
+
+        private static void OnLoadData(IList<TEntity> list)
+        {
             // 设置默认累加字段
             EntityAddition.SetField(list.Cast<IEntity>().ToList());
             foreach (var entity in list)
@@ -110,8 +130,6 @@ namespace XCode
                     }
                 });
             }
-
-            return list;
         }
 
         private static IDataRowEntityAccessor DreAccessor => XCodeService.CreateDataRowEntityAccessor(typeof(TEntity));
@@ -456,7 +474,8 @@ namespace XCode
             // 提取参数
             builder = FixParam(builder, ps);
 
-            var list = LoadData(session.Query(builder, 0, 0));
+            //var list = LoadData(session.Query(builder, 0, 0));
+            var list = session.Query(builder, 0, 0, LoadData);
             if (list == null || list.Count < 1) return null;
 
             if (list.Count > 1 && DAL.Debug)
@@ -611,7 +630,8 @@ namespace XCode
             var session = Meta.Session;
 
             var builder = CreateBuilder(where, order, selects, startRowIndex, maximumRows);
-            return LoadData(session.Query(builder, startRowIndex, maximumRows));
+            //return LoadData(session.Query(builder, startRowIndex, maximumRows));
+            return session.Query(builder, startRowIndex, maximumRows, LoadData);
         }
 
         /// <summary>最标准的查询数据。没有数据时返回空集合而不是null</summary>
@@ -707,7 +727,8 @@ namespace XCode
                         var start = (Int32)(count - (startRowIndex + maximumRows));
 
                         var builder2 = CreateBuilder(where, order2, selects, start, max);
-                        var list = LoadData(session.Query(builder2, start, max));
+                        //var list = LoadData(session.Query(builder2, start, max));
+                        var list = session.Query(builder2, start, max, LoadData);
                         if (list == null || list.Count < 1) return list;
                         // 因为这样取得的数据是倒过来的,所以这里需要再倒一次
                         list.Reverse();
@@ -718,7 +739,7 @@ namespace XCode
             #endregion
 
             var builder = CreateBuilder(where, order, selects, startRowIndex, maximumRows);
-            return LoadData(session.Query(builder, startRowIndex, maximumRows));
+            return session.Query(builder, startRowIndex, maximumRows, LoadData);
         }
 
         /// <summary>同时查询满足条件的记录集和记录总数。没有数据时返回空集合而不是null</summary>
Modified +18 -3
diff --git a/XCode/Entity/EntitySession.cs b/XCode/Entity/EntitySession.cs
index be631bb..970e713 100644
--- a/XCode/Entity/EntitySession.cs
+++ b/XCode/Entity/EntitySession.cs
@@ -543,9 +543,11 @@ namespace XCode
                     Table = FormatedTableName,
                     OrderBy = Table.Identity.Desc()
                 };
-                var ds = Dal.Select(builder, 0, 1);
-                if (ds.Tables[0].Rows.Count > 0)
-                    count = Convert.ToInt64(ds.Tables[0].Rows[0][Table.Identity.ColumnName]);
+                //var ds = Dal.Select(builder, 0, 1);
+                //if (ds.Tables[0].Rows.Count > 0)
+                //    count = Convert.ToInt64(ds.Tables[0].Rows[0][Table.Identity.ColumnName]);
+                var rs = Dal.Query(builder, 0, 0, dr => dr.Read() ? dr[0].ToInt() : -1);
+                if (rs > 0) count = rs;
             }
 
             // 100w数据时,没有预热Select Count需要3000ms,预热后需要500ms
@@ -594,6 +596,19 @@ namespace XCode
         /// <param name="builder">SQL语句</param>
         /// <param name="startRowIndex">开始行,0表示第一行</param>
         /// <param name="maximumRows">最大返回行数,0表示所有行</param>
+        /// <param name="convert">转换器</param>
+        /// <returns></returns>
+        public virtual T Query<T>(SelectBuilder builder, Int64 startRowIndex, Int64 maximumRows, Func<IDataReader, T> convert)
+        {
+            InitData();
+
+            return Dal.Query(builder, startRowIndex, maximumRows, convert);
+        }
+
+        /// <summary>执行SQL查询,返回记录集</summary>
+        /// <param name="builder">SQL语句</param>
+        /// <param name="startRowIndex">开始行,0表示第一行</param>
+        /// <param name="maximumRows">最大返回行数,0表示所有行</param>
         /// <returns></returns>
         public virtual DataSet Query(SelectBuilder builder, Int64 startRowIndex, Int64 maximumRows)
         {
Modified +4 -2
diff --git a/XCode/Properties/AssemblyInfo.cs b/XCode/Properties/AssemblyInfo.cs
index 0193620..6a12437 100644
--- a/XCode/Properties/AssemblyInfo.cs
+++ b/XCode/Properties/AssemblyInfo.cs
@@ -39,8 +39,8 @@ using XCode;
 //
 // 可以指定所有这些值,也可以使用“修订号”和“内部版本号”的默认值,
 // 方法是按如下所示使用“*”:
-[assembly: AssemblyVersion("9.7.*")]
-[assembly: AssemblyFileVersion("9.7.2018.0421")]
+[assembly: AssemblyVersion("9.8.*")]
+[assembly: AssemblyFileVersion("9.8.2018.0605")]
 
 /*
  * XCode的重大改进
@@ -58,6 +58,8 @@ using XCode;
  * /
 
 /*
+ * v9.8.2018.0605   由DataReader直接映射实体列表,以支持netstandard的MySql和SQLite,且提升性能
+ * 
  * v9.7.2018.0421   支持运行时修改DAL连接字符串
  * 
  * v9.6.2018.0326   重构权限体系,支持多角色
Modified +2 -0
diff --git a/XCode/UpdateInfo.txt b/XCode/UpdateInfo.txt
index 91d6d50..a366596 100644
--- a/XCode/UpdateInfo.txt
+++ b/XCode/UpdateInfo.txt
@@ -10,6 +10,8 @@ v2.0 数据架构功能,实体和数据结构双向映射
 v1.2 使用泛型基类
 v1.0 创建XCode
 
+v9.8.2018.0605   由DataReader直接映射实体列表,以支持netstandard的MySql和SQLite,且提升性能
+
 v9.7.2018.0421   支持运行时修改DAL连接字符串
 
 v9.6.2018.0326   重构权限体系,支持多角色