Explorar o código

Merge pull request #20 from LithWang/master

2.4.0
Lith hai 4 meses
pai
achega
4c2dc0f022
Modificáronse 75 ficheiros con 1587 adicións e 542 borrados
  1. 6 1
      Publish/DevOps3/README.md
  2. 1 1
      Publish/DevOps3/build-bash/10.Test.bash
  3. 1 1
      Publish/DevOps3/build-bash/30.nuget-pack.sh
  4. 1 1
      Publish/DevOps3/build-bash/40.Station-publish.sh
  5. 1 1
      Publish/DevOps3/release-bash/72.nuget-push.sh
  6. 12 0
      doc/ReleaseNotes.md
  7. 1 1
      src/Versions.props
  8. 1 1
      src/Vitorm.MySql/DataProvider.cs
  9. 5 4
      src/Vitorm.MySql/DbContext_Extensions_UseMySql.cs
  10. 12 11
      src/Vitorm.MySql/SqlTransactionManager.cs
  11. 19 22
      src/Vitorm.MySql/SqlTransactionManager_Command.cs
  12. 1 1
      src/Vitorm.MySql/Vitorm.MySql.csproj
  13. 1 1
      src/Vitorm.SqlServer/DataProvider.cs
  14. 3 3
      src/Vitorm.SqlServer/DbContext_Extensions_UseSqlServer.cs
  15. 14 13
      src/Vitorm.SqlServer/SqlTransactionManager.cs
  16. 1 1
      src/Vitorm.Sqlite/DataProvider.cs
  17. 3 3
      src/Vitorm.Sqlite/DbContext_Extensions_UseSqlite.cs
  18. 14 13
      src/Vitorm.Sqlite/SqlTransactionManager.cs
  19. 1 1
      src/Vitorm.Sqlite/Vitorm.Sqlite.csproj
  20. 20 0
      src/Vitorm/DbContext.Transaction.cs
  21. 5 0
      src/Vitorm/DbContext.cs
  22. 1 0
      src/Vitorm/IDbContext.cs
  23. 2 2
      src/Vitorm/Sql/DataProvider/SqlDataProvider.cs
  24. 1 1
      src/Vitorm/Sql/DataReader/IDbDataReader.cs
  25. 1 1
      src/Vitorm/Sql/Extensions/IDataProvider_Extensions.cs
  26. 64 0
      src/Vitorm/Sql/Extensions/IDataReader_Extensions.EntityReader.cs
  27. 67 0
      src/Vitorm/Sql/Extensions/IDataReader_Extensions.ValueReader.cs
  28. 238 0
      src/Vitorm/Sql/Extensions/IDataReader_Extensions.cs
  29. 1 1
      src/Vitorm/Sql/QueryExecutor/Async/CountAsync.cs
  30. 1 1
      src/Vitorm/Sql/QueryExecutor/Async/ExecuteDeleteAsync.cs
  31. 1 1
      src/Vitorm/Sql/QueryExecutor/Async/ExecuteUpdateAsync.cs
  32. 1 1
      src/Vitorm/Sql/QueryExecutor/Async/FirstOrDefaultAsync.cs
  33. 2 1
      src/Vitorm/Sql/QueryExecutor/Async/ToListAndTotalCountAsync.cs
  34. 1 1
      src/Vitorm/Sql/QueryExecutor/Async/ToListAsync.cs
  35. 1 1
      src/Vitorm/Sql/QueryExecutor/Sync/Count.cs
  36. 1 1
      src/Vitorm/Sql/QueryExecutor/Sync/ExecuteDelete.cs
  37. 1 1
      src/Vitorm/Sql/QueryExecutor/Sync/ExecuteUpdate.cs
  38. 1 1
      src/Vitorm/Sql/QueryExecutor/Sync/FirstOrDefault.cs
  39. 1 1
      src/Vitorm/Sql/QueryExecutor/Sync/ToList.cs
  40. 2 1
      src/Vitorm/Sql/QueryExecutor/Sync/ToListAndTotalCount.cs
  41. 92 77
      src/Vitorm/Sql/SqlDbContext.Execute.cs
  42. 1 0
      src/Vitorm/Sql/SqlDbContext.cs
  43. 9 9
      src/Vitorm/Sql/SqlDbSet.Async.cs
  44. 9 9
      src/Vitorm/Sql/SqlDbSet.cs
  45. 27 0
      src/Vitorm/Sql/SqlExecute/ExecuteArgument.cs
  46. 137 0
      src/Vitorm/Sql/SqlExecute/SqlExecutor.Async.cs
  47. 134 0
      src/Vitorm/Sql/SqlExecute/SqlExecutor.cs
  48. 0 119
      src/Vitorm/Sql/SqlExecutor.Async.cs
  49. 0 110
      src/Vitorm/Sql/SqlExecutor.cs
  50. 2 0
      src/Vitorm/Sql/SqlTranslate/QueryTranslateArgument.cs
  51. 3 6
      src/Vitorm/Sql/SqlTranslate/QueryTranslateService.cs
  52. 8 0
      src/Vitorm/Sql/Transaction/ETransactionState.cs
  53. 0 11
      src/Vitorm/Sql/Transaction/ITransactionScope.cs
  54. 6 8
      src/Vitorm/Sql/Transaction/SqlTransaction.cs
  55. 12 10
      src/Vitorm/Sql/Transaction/SqlTransactionManager.cs
  56. 10 0
      src/Vitorm/Transaction/ITransaction.cs
  57. 9 0
      src/Vitorm/Transaction/ITransactionManager.cs
  58. 1 1
      src/Vitorm/TypeUtil.cs
  59. 2 2
      test/IssuesTest/Vitorm.MsTest.Issue000_099/Issues/Issue_004_Test.cs
  60. 4 4
      test/IssuesTest/Vitorm.MsTest.Issue000_099/Vitorm.MsTest.Issue000_099.csproj
  61. 6 6
      test/Vitorm.Data.Benchmark/Vitorm.Data.Benchmark.csproj
  62. 1 1
      test/Vitorm.Data.Console/Vitorm.Data.Console.csproj
  63. 4 4
      test/Vitorm.Data.MsTest/Vitorm.Data.MsTest.csproj
  64. 4 4
      test/Vitorm.EntityGenerate.MsTest/Vitorm.EntityGenerate.MsTest.csproj
  65. 4 4
      test/Vitorm.MsTest/Vitorm.MsTest.csproj
  66. 141 0
      test/Vitorm.MySql.MsTest/CustomTest/Procedure_Test.cs
  67. 4 4
      test/Vitorm.MySql.MsTest/Vitorm.MySql.MsTest.csproj
  68. 4 4
      test/Vitorm.SqlServer.MsTest/Vitorm.SqlServer.MsTest.csproj
  69. 1 1
      test/Vitorm.Sqlite.Console/Vitorm.Sqlite.Console.csproj
  70. 56 0
      test/Vitorm.Sqlite.MsTest/CommonTest/Query_Group_FirstOrDefault_Test.cs
  71. 54 0
      test/Vitorm.Sqlite.MsTest/CommonTest/Transaction_Nested_Test.cs
  72. 4 46
      test/Vitorm.Sqlite.MsTest/CommonTest/Transaction_Test.cs
  73. 322 0
      test/Vitorm.Sqlite.MsTest/CustomTest/ReadEntity_Test.cs
  74. 1 2
      test/Vitorm.Sqlite.MsTest/DataSource.cs
  75. 4 4
      test/Vitorm.Sqlite.MsTest/Vitorm.Sqlite.MsTest.csproj

+ 6 - 1
Publish/DevOps3/README.md

@@ -1,5 +1,5 @@
 
-# DevOps 3.7
+# DevOps 3.8
 
 
 # build-bash
@@ -24,6 +24,11 @@ if this file exists, will not need approval for jenkins build.
 ----------------------------------------------
 # ReleaseLog
 
+-----------------------
+# 3.8
+> 2024-12-19
+- upgrade net to 8.0
+
 -----------------------
 # 3.7
 > 2024-09-21

+ 1 - 1
Publish/DevOps3/build-bash/10.Test.bash

@@ -37,7 +37,7 @@ docker run -i --rm \
 -v $NUGET_PATH:/root/.nuget \
 -v "$basePath":/root/code \
 -v "$basePath":"$basePath" \
-serset/dotnet:sdk-6.0 \
+serset/dotnet:sdk-8.0 \
 bash -c "
 set -e
 

+ 1 - 1
Publish/DevOps3/build-bash/30.nuget-pack.sh

@@ -22,7 +22,7 @@ docker run -i --rm \
 --env LANG=C.UTF-8 \
 -v $NUGET_PATH:/root/.nuget \
 -v $basePath:/root/code \
-serset/dotnet:sdk-6.0 \
+serset/dotnet:sdk-8.0 \
 bash -c "
 
 publishPath=/root/code/Publish/release/release/nuget

+ 1 - 1
Publish/DevOps3/build-bash/40.Station-publish.sh

@@ -23,7 +23,7 @@ docker run -i --rm \
 -v $NUGET_PATH:/root/.nuget \
 -v "$basePath":/root/code \
 -v "$basePath":"$basePath" \
-serset/dotnet:sdk-6.0 \
+serset/dotnet:sdk-8.0 \
 bash -c "
 set -e
 

+ 1 - 1
Publish/DevOps3/release-bash/72.nuget-push.sh

@@ -25,7 +25,7 @@ fi
 docker run -i --rm \
 --env LANG=C.UTF-8 \
 -v $basePath:/root/code \
-serset/dotnet:sdk-6.0 \
+serset/dotnet:sdk-8.0 \
 bash -c "
 for file in /root/code/Publish/release/release/nuget/*.nupkg
 do

+ 12 - 0
doc/ReleaseNotes.md

@@ -1,5 +1,17 @@
 # Vitorm ReleaseNotes
 
+-----------------------
+# 2.4.0
+- [Vitorm] DbContext, new method GetEntityDescriptorFromCache
+- [Vitorm] move BeginTransaction to interface and refactor Transaction
+- [Vitorm] rename GetCurrentTransaction to GetDbTransaction
+- upgrade net to 8.0
+- [Vitorm] move SqlDataProvider to namespace Vitorm.Sql.DataProvider
+- [Vitorm] move IDbDataReader to namespace Vitorm.Sql.DataReader
+- [Vitorm] support Execute Procedure in SqlExecutor and SqlDbContext
+- [Vitorm] IDataReader extension methods: ReadValue ReadTuple ReadEntity
+
+
 -----------------------
 # 2.3.0
 

+ 1 - 1
src/Versions.props

@@ -1,6 +1,6 @@
 <Project>
     <PropertyGroup>
-        <Version>2.3.0</Version>
+        <Version>2.4.0</Version>
         <VitLinq_Version>[3.1.5, 3.2.0)</VitLinq_Version>
     </PropertyGroup>
 

+ 1 - 1
src/Vitorm.MySql/DataProvider.cs

@@ -1,7 +1,7 @@
 using System.Collections.Generic;
 
-using Vitorm.DataProvider;
 using Vitorm.Sql;
+using Vitorm.Sql.DataProvider;
 
 namespace Vitorm.MySql
 {

+ 5 - 4
src/Vitorm.MySql/DbContext_Extensions_UseMySql.cs

@@ -2,7 +2,8 @@
 
 using Vitorm.MySql;
 using Vitorm.Sql;
-using Vitorm.Sql.Transaction;
+using Vitorm.Sql.SqlExecute;
+using Vitorm.Transaction;
 
 using DbConnection = MySqlConnector.MySqlConnection;
 
@@ -21,7 +22,7 @@ namespace Vitorm
                 sqlExecutor: sqlExecutor
                 );
 
-            dbContext.createTransactionScope = createTransactionScope;
+            dbContext.createTransactionManager = createTransactionManager;
 
 
             if (config.commandTimeout.HasValue) dbContext.commandTimeout = config.commandTimeout.Value;
@@ -44,8 +45,8 @@ namespace Vitorm
         #endregion
 
 
-        static ITransactionScope createTransactionScope(SqlDbContext dbContext) => new Vitorm.MySql.SqlTransactionScope(dbContext);
-        //static ITransactionScope createTransactionScope(SqlDbContext dbContext) => new Vitorm.MySql.SqlTransactionScope_Command(dbContext);
+        static ITransactionManager createTransactionManager(SqlDbContext dbContext) => new Vitorm.MySql.SqlTransactionManager(dbContext);
+        static ITransactionManager createTransactionManager2(SqlDbContext dbContext) => new Vitorm.MySql.SqlTransactionManager_Command(dbContext);
 
 
     }

+ 12 - 11
src/Vitorm.MySql/SqlTransactionScope.cs → src/Vitorm.MySql/SqlTransactionManager.cs

@@ -2,27 +2,28 @@
 
 using Vitorm.Sql;
 using Vitorm.Sql.Transaction;
+using Vitorm.Transaction;
 
 using SqlTransaction = MySqlConnector.MySqlTransaction;
 
 namespace Vitorm.MySql
 {
-    public class SqlTransactionScope : Vitorm.Sql.Transaction.SqlTransactionScope
+    public class SqlTransactionManager : Vitorm.Sql.Transaction.SqlTransactionManager
     {
         int savePointCount = 0;
-        public DbTransactionWrap CreateTransactionSavePoint(IDbTransaction originalTransaction)
+        public Sql.Transaction.SqlTransaction CreateTransactionSavePoint(IDbTransaction originalTransaction)
         {
             var savePointName = "tran" + savePointCount++;
             return new DbTransactionWrapSavePoint(originalTransaction, savePointName);
         }
-        public SqlTransactionScope(SqlDbContext dbContext) : base(dbContext)
+        public SqlTransactionManager(SqlDbContext dbContext) : base(dbContext)
         {
         }
 
-        public override IDbTransaction BeginTransaction()
+        public override ITransaction BeginTransaction()
         {
-            DbTransactionWrap transactionWrap;
-            IDbTransaction originalTransaction = GetCurrentTransaction();
+            Sql.Transaction.SqlTransaction transaction;
+            IDbTransaction originalTransaction = GetDbTransaction();
             if (originalTransaction == null)
             {
                 var dbConnection = dbContext.dbConnection as MySqlConnector.MySqlConnection;
@@ -30,20 +31,20 @@ namespace Vitorm.MySql
 
                 originalTransaction = dbConnection.BeginTransaction();
                 dbContext.ExecuteWithTransaction("SET autocommit=0;", transaction: originalTransaction);
-                transactionWrap = new DbTransactionWrap(originalTransaction);
+                transaction = new Sql.Transaction.SqlTransaction(originalTransaction);
             }
             else
             {
-                transactionWrap = CreateTransactionSavePoint(originalTransaction);
+                transaction = CreateTransactionSavePoint(originalTransaction);
             }
 
-            transactions.Push(transactionWrap);
-            return transactionWrap;
+            transactions.Push(transaction);
+            return transaction;
         }
 
 
 
-        public class DbTransactionWrapSavePoint : DbTransactionWrap
+        public class DbTransactionWrapSavePoint : Sql.Transaction.SqlTransaction
         {
             public SqlTransaction sqlTran => (SqlTransaction)originalTransaction;
             readonly string savePointName;

+ 19 - 22
src/Vitorm.MySql/SqlTransactionScope_Command.cs → src/Vitorm.MySql/SqlTransactionManager_Command.cs

@@ -3,8 +3,8 @@ using System.Data;
 
 using Vitorm.Sql;
 using Vitorm.Sql.Transaction;
+using Vitorm.Transaction;
 
-using static Vitorm.Sql.Transaction.DbTransactionWrap;
 
 namespace Vitorm.MySql
 {
@@ -21,63 +21,60 @@ namespace Vitorm.MySql
           COMMIT;
           -- ROLLBACK;
     */
-    public class SqlTransactionScope_Command : ITransactionScope
+    public class SqlTransactionManager_Command : ITransactionManager
     {
         protected SqlDbContext dbContext;
-        protected Stack<DbTransactionWrapSavePoint> savePoints = new();
+        protected Stack<TransactionSavePoint> savePoints = new();
         int savePointCount = 0;
-        public SqlTransactionScope_Command(SqlDbContext dbContext)
+        public SqlTransactionManager_Command(SqlDbContext dbContext)
         {
             this.dbContext = dbContext;
         }
 
-        DbTransactionWrap_Command dbTransactionWrap;
-        public virtual IDbTransaction BeginTransaction()
+        Transaction_Command transaction;
+        public virtual ITransaction BeginTransaction()
         {
-            if (dbTransactionWrap == null)
+            if (transaction == null)
             {
                 var dbConnection = dbContext.dbConnection;
                 if (dbConnection.State != ConnectionState.Open) dbConnection.Open();
 
-                dbTransactionWrap = new DbTransactionWrap_Command(dbContext);
-                return dbTransactionWrap;
+                transaction = new Transaction_Command(dbContext);
+                return transaction;
 
             }
             var savePointName = "tran" + savePointCount++;
-            var savePoint = dbTransactionWrap.BeginSavePoint(savePointName);
+            var savePoint = transaction.BeginSavePoint(savePointName);
 
             savePoints.Push(savePoint);
             return savePoint;
         }
-
-        public virtual IDbTransaction GetCurrentTransaction() => null;
-
         public virtual void Dispose()
         {
             while (savePoints?.Count > 0)
             {
                 var transaction = savePoints.Pop();
-                if (transaction?.TransactionState != DbTransactionWrap.ETransactionState.Disposed)
+                if (transaction?.TransactionState != ETransactionState.Disposed)
                 {
                     transaction?.Dispose();
                 }
             }
             savePoints = null;
 
-            dbTransactionWrap?.Dispose();
-            dbTransactionWrap = null;
+            transaction?.Dispose();
+            transaction = null;
         }
 
 
 
-        public class DbTransactionWrap_Command : IDbTransaction
+        public class Transaction_Command : ITransaction
         {
             public virtual System.Data.IsolationLevel IsolationLevel => default;
             public IDbConnection Connection => dbContext.dbConnection;
             readonly SqlDbContext dbContext;
             public virtual ETransactionState TransactionState { get; protected set; } = ETransactionState.Active;
 
-            public DbTransactionWrap_Command(SqlDbContext dbContext)
+            public Transaction_Command(SqlDbContext dbContext)
             {
                 this.dbContext = dbContext;
                 Execute($"START TRANSACTION; SET autocommit=0;");
@@ -103,9 +100,9 @@ namespace Vitorm.MySql
                 Execute($"ROLLBACK;");
                 TransactionState = ETransactionState.RolledBack;
             }
-            public DbTransactionWrapSavePoint BeginSavePoint(string savePoint)
+            public TransactionSavePoint BeginSavePoint(string savePoint)
             {
-                return new DbTransactionWrapSavePoint(dbContext, savePoint);
+                return new TransactionSavePoint(dbContext, savePoint);
             }
             protected virtual void Execute(string sql)
             {
@@ -113,7 +110,7 @@ namespace Vitorm.MySql
             }
         }
 
-        public class DbTransactionWrapSavePoint : IDbTransaction
+        public class TransactionSavePoint : ITransaction
         {
             public virtual System.Data.IsolationLevel IsolationLevel => default;
 
@@ -127,7 +124,7 @@ namespace Vitorm.MySql
             {
                 dbContext.ExecuteWithTransaction(sql);
             }
-            public DbTransactionWrapSavePoint(SqlDbContext dbContext, string savePointName)
+            public TransactionSavePoint(SqlDbContext dbContext, string savePointName)
             {
                 this.dbContext = dbContext;
                 this.savePointName = savePointName;

+ 1 - 1
src/Vitorm.MySql/Vitorm.MySql.csproj

@@ -32,7 +32,7 @@
     </ItemGroup>
 
     <ItemGroup>
-        <PackageReference Include="MySqlConnector" Version="2.3.7" />
+        <PackageReference Include="MySqlConnector" Version="2.4.0" />
     </ItemGroup>
 
     <ItemGroup>

+ 1 - 1
src/Vitorm.SqlServer/DataProvider.cs

@@ -1,7 +1,7 @@
 using System.Collections.Generic;
 
-using Vitorm.DataProvider;
 using Vitorm.Sql;
+using Vitorm.Sql.DataProvider;
 
 namespace Vitorm.SqlServer
 {

+ 3 - 3
src/Vitorm.SqlServer/DbContext_Extensions_UseSqlServer.cs

@@ -1,6 +1,6 @@
 using Vitorm.Sql;
-using Vitorm.Sql.Transaction;
 using Vitorm.SqlServer;
+using Vitorm.Transaction;
 
 namespace Vitorm
 {
@@ -16,14 +16,14 @@ namespace Vitorm
                 dbConnectionProvider: config.ToDbConnectionProvider()
                 );
 
-            dbContext.createTransactionScope = createTransactionScope;
+            dbContext.createTransactionManager = createTransactionManager;
 
             if (config.commandTimeout.HasValue) dbContext.commandTimeout = config.commandTimeout.Value;
 
             return dbContext;
         }
 
-        static ITransactionScope createTransactionScope(SqlDbContext dbContext) => new Vitorm.SqlServer.SqlTransactionScope(dbContext);
+        static ITransactionManager createTransactionManager(SqlDbContext dbContext) => new Vitorm.SqlServer.SqlTransactionManager(dbContext);
 
     }
 }

+ 14 - 13
src/Vitorm.SqlServer/SqlTransactionScope.cs → src/Vitorm.SqlServer/SqlTransactionManager.cs

@@ -2,51 +2,52 @@
 
 using Vitorm.Sql;
 using Vitorm.Sql.Transaction;
+using Vitorm.Transaction;
 
 using SqlTransaction = Microsoft.Data.SqlClient.SqlTransaction;
 
 namespace Vitorm.SqlServer
 {
-    public class SqlTransactionScope : Vitorm.Sql.Transaction.SqlTransactionScope
+    public class SqlTransactionManager : Vitorm.Sql.Transaction.SqlTransactionManager
     {
         int savePointCount = 0;
-        public DbTransactionWrap CreateTransactionSavePoint(IDbTransaction originalTransaction)
+        public Sql.Transaction.SqlTransaction CreateTransactionSavePoint(IDbTransaction originalTransaction)
         {
             var savePointName = "tran" + savePointCount++;
-            return new DbTransactionWrapSavePoint(originalTransaction, savePointName);
+            return new TransactionSavePoint(originalTransaction, savePointName);
         }
-        public SqlTransactionScope(SqlDbContext dbContext) : base(dbContext)
+        public SqlTransactionManager(SqlDbContext dbContext) : base(dbContext)
         {
         }
 
-        public override IDbTransaction BeginTransaction()
+        public override ITransaction BeginTransaction()
         {
-            DbTransactionWrap transactionWrap;
-            IDbTransaction originalTransaction = GetCurrentTransaction();
+            Sql.Transaction.SqlTransaction transaction;
+            IDbTransaction originalTransaction = GetDbTransaction();
             if (originalTransaction == null)
             {
                 var dbConnection = dbContext.dbConnection;
                 if (dbConnection.State != ConnectionState.Open) dbConnection.Open();
                 originalTransaction = dbConnection.BeginTransaction();
 
-                transactionWrap = new DbTransactionWrap(originalTransaction);
+                transaction = new Sql.Transaction.SqlTransaction(originalTransaction);
             }
             else
             {
-                transactionWrap = CreateTransactionSavePoint(originalTransaction);
+                transaction = CreateTransactionSavePoint(originalTransaction);
             }
 
-            transactions.Push(transactionWrap);
-            return transactionWrap;
+            transactions.Push(transaction);
+            return transaction;
         }
 
     }
 
-    public class DbTransactionWrapSavePoint : DbTransactionWrap
+    public class TransactionSavePoint : Sql.Transaction.SqlTransaction
     {
         public SqlTransaction sqlTran => (SqlTransaction)originalTransaction;
         readonly string savePointName;
-        public DbTransactionWrapSavePoint(IDbTransaction transaction, string savePointName) : base(transaction)
+        public TransactionSavePoint(IDbTransaction transaction, string savePointName) : base(transaction)
         {
             this.savePointName = savePointName;
             sqlTran.Save(savePointName);

+ 1 - 1
src/Vitorm.Sqlite/DataProvider.cs

@@ -1,7 +1,7 @@
 using System.Collections.Generic;
 
-using Vitorm.DataProvider;
 using Vitorm.Sql;
+using Vitorm.Sql.DataProvider;
 
 namespace Vitorm.Sqlite
 {

+ 3 - 3
src/Vitorm.Sqlite/DbContext_Extensions_UseSqlite.cs

@@ -1,6 +1,6 @@
 using Vitorm.Sql;
-using Vitorm.Sql.Transaction;
 using Vitorm.Sqlite;
+using Vitorm.Transaction;
 
 namespace Vitorm
 {
@@ -16,13 +16,13 @@ namespace Vitorm
                 dbConnectionProvider: config.ToDbConnectionProvider()
                 );
 
-            dbContext.createTransactionScope = createTransactionScope;
+            dbContext.createTransactionManager = createTransactionManager;
 
             if (config.commandTimeout.HasValue) dbContext.commandTimeout = config.commandTimeout.Value;
 
             return dbContext;
         }
 
-        static ITransactionScope createTransactionScope(SqlDbContext dbContext) => new Vitorm.Sqlite.SqlTransactionScope(dbContext);
+        static ITransactionManager createTransactionManager(SqlDbContext dbContext) => new Vitorm.Sqlite.SqlTransactionManager(dbContext);
     }
 }

+ 14 - 13
src/Vitorm.Sqlite/SqlTransactionScope.cs → src/Vitorm.Sqlite/SqlTransactionManager.cs

@@ -2,52 +2,53 @@
 
 using Vitorm.Sql;
 using Vitorm.Sql.Transaction;
+using Vitorm.Transaction;
 
 using SqlTransaction = Microsoft.Data.Sqlite.SqliteTransaction;
 
 namespace Vitorm.Sqlite
 {
     // sqlite/transactions  https://learn.microsoft.com/en-us/dotnet/standard/data/sqlite/transactions  
-    public class SqlTransactionScope : Vitorm.Sql.Transaction.SqlTransactionScope
+    public class SqlTransactionManager : Vitorm.Sql.Transaction.SqlTransactionManager
     {
         int savePointCount = 0;
-        public DbTransactionWrap CreateTransactionSavePoint(IDbTransaction originalTransaction)
+        public Sql.Transaction.SqlTransaction CreateTransactionSavePoint(IDbTransaction originalTransaction)
         {
             var savePointName = "tran" + savePointCount++;
-            return new DbTransactionWrapSavePoint(originalTransaction, savePointName);
+            return new TransactionSavePoint(originalTransaction, savePointName);
         }
-        public SqlTransactionScope(SqlDbContext dbContext) : base(dbContext)
+        public SqlTransactionManager(SqlDbContext dbContext) : base(dbContext)
         {
         }
 
-        public override IDbTransaction BeginTransaction()
+        public override ITransaction BeginTransaction()
         {
-            DbTransactionWrap transactionWrap;
-            IDbTransaction originalTransaction = GetCurrentTransaction();
+            Sql.Transaction.SqlTransaction transaction;
+            IDbTransaction originalTransaction = GetDbTransaction();
             if (originalTransaction == null)
             {
                 var dbConnection = dbContext.dbConnection;
                 if (dbConnection.State != ConnectionState.Open) dbConnection.Open();
                 originalTransaction = dbConnection.BeginTransaction();
 
-                transactionWrap = new DbTransactionWrap(originalTransaction);
+                transaction = new Sql.Transaction.SqlTransaction(originalTransaction);
             }
             else
             {
-                transactionWrap = CreateTransactionSavePoint(originalTransaction);
+                transaction = CreateTransactionSavePoint(originalTransaction);
             }
 
-            transactions.Push(transactionWrap);
-            return transactionWrap;
+            transactions.Push(transaction);
+            return transaction;
         }
 
     }
 
-    public class DbTransactionWrapSavePoint : DbTransactionWrap
+    public class TransactionSavePoint : Sql.Transaction.SqlTransaction
     {
         public SqlTransaction sqlTran => (SqlTransaction)originalTransaction;
         readonly string savePointName;
-        public DbTransactionWrapSavePoint(IDbTransaction transaction, string savePointName) : base(transaction)
+        public TransactionSavePoint(IDbTransaction transaction, string savePointName) : base(transaction)
         {
             this.savePointName = savePointName;
             sqlTran.Save(savePointName);

+ 1 - 1
src/Vitorm.Sqlite/Vitorm.Sqlite.csproj

@@ -32,7 +32,7 @@
     </ItemGroup>
 
     <ItemGroup>
-        <PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.10" />
+        <PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.0" />
     </ItemGroup>
 
     <ItemGroup>

+ 20 - 0
src/Vitorm/DbContext.Transaction.cs

@@ -0,0 +1,20 @@
+using System;
+
+using Vitorm.Transaction;
+
+namespace Vitorm
+{
+    public partial class DbContext : IDbContext, IDisposable
+    {
+        #region Transaction
+
+        public virtual ITransaction BeginTransaction()
+        {
+            throw new NotImplementedException();
+        }
+
+        #endregion
+    }
+
+
+}

+ 5 - 0
src/Vitorm/DbContext.cs

@@ -66,6 +66,11 @@ namespace Vitorm
             if (tryFromCache && dbSetMap?.TryGetValue(entityType, out var dbSet) == true) return dbSet.entityDescriptor;
             return entityLoader.LoadDescriptor(entityType).entityDescriptor;
         }
+        public virtual IEntityDescriptor GetEntityDescriptorFromCache(Type entityType)
+        {
+            if (dbSetMap?.TryGetValue(entityType, out var dbSet) == true) return dbSet.entityDescriptor;
+            return default;
+        }
         public virtual IEntityDescriptor GetEntityDescriptor<Entity>(bool tryFromCache = true)
             => GetEntityDescriptor(typeof(Entity), tryFromCache);
         #endregion

+ 1 - 0
src/Vitorm/IDbContext.cs

@@ -6,6 +6,7 @@ namespace Vitorm
 {
     public partial interface IDbContext
     {
+
         #region Sync Method
 
         // #0 Schema :  Create Drop

+ 2 - 2
src/Vitorm/DataProvider/SqlDataProvider.cs → src/Vitorm/Sql/DataProvider/SqlDataProvider.cs

@@ -3,9 +3,9 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Threading.Tasks;
 
-using Vitorm.Sql;
+using Vitorm.DataProvider;
 
-namespace Vitorm.DataProvider
+namespace Vitorm.Sql.DataProvider
 {
     public abstract partial class SqlDataProvider : IDataProvider
     {

+ 1 - 1
src/Vitorm/Sql/IDbDataReader.cs → src/Vitorm/Sql/DataReader/IDbDataReader.cs

@@ -1,6 +1,6 @@
 
 
-namespace Vitorm.Sql
+namespace Vitorm.Sql.DataReader
 {
     public interface IDbDataReader
     {

+ 1 - 1
src/Vitorm/DataProvider/IDataProvider_Extensions.cs → src/Vitorm/Sql/Extensions/IDataProvider_Extensions.cs

@@ -1,5 +1,6 @@
 using Vitorm.DataProvider;
 using Vitorm.Sql;
+using Vitorm.Sql.DataProvider;
 
 namespace Vitorm
 {
@@ -9,6 +10,5 @@ namespace Vitorm
         {
             return (dataProvider as SqlDataProvider)?.CreateDbContext();
         }
-
     }
 }

+ 64 - 0
src/Vitorm/Sql/Extensions/IDataReader_Extensions.EntityReader.cs

@@ -0,0 +1,64 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations.Schema;
+using System.Data;
+using System.Linq;
+using System.Reflection;
+
+namespace Vitorm
+{
+    public static partial class IDataReader_Extensions
+    {
+
+        class EntityReader
+        {
+            Type entityType;
+            (PropertyInfo property, int columnIndex, Type underlyingType)[] columns;
+
+            public EntityReader(Type entityType, List<string> columnNames) : this(entityType, columnNames.Select((m, i) => (m, i)).ToList())
+            { }
+
+            public EntityReader(Type entityType, List<(string columName, int index)> columnIndexes)
+            {
+                this.entityType = entityType;
+
+                var properties =
+                    entityType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
+                    .Select(property => (
+                        property: property,
+                        name: property.GetCustomAttribute<ColumnAttribute>(inherit: true)?.Name ?? property.Name
+                        )
+                    )
+                    .ToList();
+
+                columns = properties.Select(m =>
+                {
+                    var property = m.property;
+                    var columnIndex = columnIndexes.FirstOrDefault(col => m.name.Equals(col.columName, StringComparison.OrdinalIgnoreCase));
+                    if (columnIndex.columName == null) return default;
+
+                    var index = columnIndex.index;
+                    var underlyingType = TypeUtil.GetUnderlyingType(property.PropertyType);
+                    return (property, index, underlyingType);
+                })
+                .Where(m => m != default)
+                .ToArray();
+            }
+
+
+            public object Read(IDataReader reader)
+            {
+                var entity = Activator.CreateInstance(entityType);
+                foreach (var column in columns)
+                {
+                    var value = reader.GetValue(column.columnIndex);
+                    var convertedValue = TypeUtil.ConvertToUnderlyingType(value, column.underlyingType);
+                    column.property.SetValue(entity, convertedValue);
+                }
+                return entity;
+            }
+
+        }
+
+    }
+}

+ 67 - 0
src/Vitorm/Sql/Extensions/IDataReader_Extensions.ValueReader.cs

@@ -0,0 +1,67 @@
+using System;
+using System.Data;
+
+namespace Vitorm
+{
+    public static partial class IDataReader_Extensions
+    {
+
+        interface IValueReader
+        {
+            Type valueType { get; }
+            object Read(IDataReader reader, int index = 0);
+        }
+
+
+
+        class ValueReader_String : IValueReader
+        {
+            public Type valueType => typeof(string);
+            public object Read(IDataReader reader, int index = 0) => reader.GetString(index);
+
+
+            public static ValueReader_String Instance = new ValueReader_String();
+        }
+
+
+
+        class ValueReader_Nullable : IValueReader
+        {
+            public ValueReader_Nullable(Type valueType)
+            {
+                this.valueType = valueType;
+                underlyingType = TypeUtil.GetUnderlyingType(valueType);
+            }
+            public Type valueType { get; private set; }
+            Type underlyingType;
+            public object Read(IDataReader reader, int index = 0)
+            {
+                return TypeUtil.ConvertToUnderlyingType(reader[index], underlyingType);
+            }
+        }
+
+
+
+
+        class ValueReader_Struct : IValueReader
+        {
+            public ValueReader_Struct(Type valueType)
+            {
+                this.valueType = valueType;
+                defaultValue = TypeUtil.GetDefaultValue(valueType);
+            }
+            public Type valueType { get; private set; }
+            object defaultValue;
+            public object Read(IDataReader reader, int index = 0)
+            {
+                var value = reader[index];
+                if (value == null || value == DBNull.Value) return defaultValue;
+                return TypeUtil.ConvertToUnderlyingType(value, valueType);
+            }
+        }
+
+
+
+
+    }
+}

+ 238 - 0
src/Vitorm/Sql/Extensions/IDataReader_Extensions.cs

@@ -0,0 +1,238 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Linq;
+
+namespace Vitorm
+{
+    public static partial class IDataReader_Extensions
+    {
+
+        static IValueReader BuildValueReader(Type type)
+        {
+            if (type == typeof(string)) return ValueReader_String.Instance;
+            else if (TypeUtil.IsNullable(type)) return new ValueReader_Nullable(type);
+            return new ValueReader_Struct(type);
+        }
+
+
+        #region #1 ReadValue
+        public static IEnumerable<Value> ReadValue<Value>(this IDataReader reader, int index = 0)
+        {
+            var valueReader = BuildValueReader(typeof(Value));
+            while (reader.Read())
+                yield return (Value)valueReader.Read(reader, index);
+        }
+
+        public static IEnumerable<Value> ReadValue<Value>(this IDataReader reader, string columnName)
+        {
+            var names = Enumerable.Range(0, reader.FieldCount).Select(i => reader.GetName(i)).ToList();
+            int index = names.FindIndex(name => name.Equals(columnName, StringComparison.OrdinalIgnoreCase));
+
+            if (index < 0) throw new ArgumentException("can not find column " + columnName);
+
+            return ReadValue<Value>(reader, index);
+        }
+        #endregion
+
+
+
+
+        #region #2 ReadTuple
+
+        public static IEnumerable<(Column0, Column1)> ReadTuple<Column0, Column1>(this IDataReader reader, int[] indexes = null)
+        {
+            var valueReaders = new[] { typeof(Column0), typeof(Column1) }.Select(type => BuildValueReader(type)).ToArray();
+            indexes ??= new[] { 0, 1 };
+
+            while (reader.Read())
+                yield return ((Column0)valueReaders[0].Read(reader, indexes[0]), (Column1)valueReaders[1].Read(reader, indexes[1]));
+        }
+        public static IEnumerable<(Column0, Column1)> ReadTuple<Column0, Column1>(this IDataReader reader, string[] columnNames)
+        {
+            var names = Enumerable.Range(0, reader.FieldCount).Select(i => reader.GetName(i)).ToList();
+            var indexes = columnNames.Select(
+                columnName =>
+                {
+                    var index = names.FindIndex(name => name.Equals(columnName, StringComparison.OrdinalIgnoreCase));
+                    if (index < 0) throw new ArgumentException("can not find column " + columnName);
+                    return index;
+                }
+            ).ToArray();
+
+            return ReadTuple<Column0, Column1>(reader, indexes);
+        }
+
+
+        public static IEnumerable<(Column0, Column1, Column2)> ReadTuple<Column0, Column1, Column2>(this IDataReader reader, int[] indexes = null)
+        {
+            var valueReaders = new[] { typeof(Column0), typeof(Column1), typeof(Column2) }.Select(type => BuildValueReader(type)).ToArray();
+            indexes ??= new[] { 0, 1, 2 };
+
+            while (reader.Read())
+                yield return ((Column0)valueReaders[0].Read(reader, indexes[0]), (Column1)valueReaders[1].Read(reader, indexes[1]), (Column2)valueReaders[2].Read(reader, indexes[2]));
+        }
+        public static IEnumerable<(Column0, Column1, Column2)> ReadTuple<Column0, Column1, Column2>(this IDataReader reader, string[] columnNames)
+        {
+            var names = Enumerable.Range(0, reader.FieldCount).Select(i => reader.GetName(i)).ToList();
+            var indexes = columnNames.Select(
+                columnName =>
+                {
+                    var index = names.FindIndex(name => name.Equals(columnName, StringComparison.OrdinalIgnoreCase));
+                    if (index < 0) throw new ArgumentException("can not find column " + columnName);
+                    return index;
+                }
+            ).ToArray();
+
+            return ReadTuple<Column0, Column1, Column2>(reader, indexes);
+        }
+
+
+        public static IEnumerable<(Column0, Column1, Column2, Column3)> ReadTuple<Column0, Column1, Column2, Column3>(this IDataReader reader, int[] indexes = null)
+        {
+            var valueReaders = new[] { typeof(Column0), typeof(Column1), typeof(Column2), typeof(Column3) }.Select(type => BuildValueReader(type)).ToArray();
+            indexes ??= new[] { 0, 1, 2, 3 };
+
+            while (reader.Read())
+                yield return ((Column0)valueReaders[0].Read(reader, indexes[0]), (Column1)valueReaders[1].Read(reader, indexes[1]), (Column2)valueReaders[2].Read(reader, indexes[2]), (Column3)valueReaders[3].Read(reader, indexes[3]));
+        }
+        public static IEnumerable<(Column0, Column1, Column2, Column3)> ReadTuple<Column0, Column1, Column2, Column3>(this IDataReader reader, string[] columnNames)
+        {
+            var names = Enumerable.Range(0, reader.FieldCount).Select(i => reader.GetName(i)).ToList();
+            var indexes = columnNames.Select(
+                columnName =>
+                {
+                    var index = names.FindIndex(name => name.Equals(columnName, StringComparison.OrdinalIgnoreCase));
+                    if (index < 0) throw new ArgumentException("can not find column " + columnName);
+                    return index;
+                }
+            ).ToArray();
+
+            return ReadTuple<Column0, Column1, Column2, Column3>(reader, indexes);
+        }
+        #endregion
+
+
+
+
+
+
+
+
+        #region #3 ReadEntity
+        public static IEnumerable<Entity> ReadEntity<Entity>(this IDataReader reader) where Entity : class, new()
+        {
+            var fieldNames = Enumerable.Range(0, reader.FieldCount).Select(i => reader.GetName(i)).ToList();
+
+            var entityReader = new EntityReader(typeof(Entity), fieldNames);
+
+            while (reader.Read())
+            {
+                yield return (Entity)entityReader.Read(reader);
+            }
+        }
+
+
+        #region ReadEntity by Delegate
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="reader"></param>
+        /// <param name="func"></param>
+        /// <param name="splitIndexes">if splitIndex is valid, will try populate all column to all entity</param>
+        /// <param name="types"></param>
+        /// <param name="fieldNames"></param>
+        /// <returns></returns>
+        static IEnumerable<object> ReadEntity(this IDataReader reader, Delegate func, int[] splitIndexes, Type[] types = null, List<string> fieldNames = null)
+        {
+            types ??= func.Method.GetParameters().Select(p => p.ParameterType).ToArray();
+            fieldNames ??= Enumerable.Range(0, reader.FieldCount).Select(i => reader.GetName(i)).ToList();
+
+            // splitIndexes  :   1, 5 , -1, 10
+            splitIndexes ??= Enumerable.Repeat(-1, types.Length - 1).ToArray();
+
+            var splitRangeStart = new[] { 0 }.Concat(splitIndexes.Select(i => i >= 0 ? i : 0)).ToArray();
+            var splitRangeEnd = splitIndexes.Select(i => i >= 0 ? i - 1 : int.MaxValue).Concat(new[] { int.MaxValue }).ToArray();
+
+            var fieldIndexes = fieldNames.Select((fieldName, index) => (fieldName: fieldName, index: index)).ToList();
+            var entityReaders = types.Select((type, index) => new EntityReader(type, fieldIndexes.Where(field => splitRangeStart[index] <= field.index && field.index <= splitRangeEnd[index]).ToList())).ToList();
+
+            while (reader.Read())
+            {
+                yield return func.DynamicInvoke(entityReaders.Select(entityReader => entityReader.Read(reader)).ToArray());
+            }
+        }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="reader"></param>
+        /// <param name="func"></param>
+        /// <param name="splitOns">if can not find splitOn column, will try populate all column to all entity</param>
+        /// <returns></returns>
+        static IEnumerable<object> ReadEntity(this IDataReader reader, Delegate func, string[] splitOns)
+        {
+            var types = func.Method.GetParameters().Select(p => p.ParameterType).ToArray();
+            var fieldNames = Enumerable.Range(0, reader.FieldCount).Select(i => reader.GetName(i)).ToList();
+
+            splitOns ??= new string[types.Length - 1];
+
+            // splitIndexes  :   1, 5 , -1, 10
+            var splitIndexes = splitOns.Select(splitOn => fieldNames.FindIndex(name => name.Equals(splitOn, StringComparison.OrdinalIgnoreCase))).Concat(new[] { fieldNames.Count }).ToArray();
+
+            return ReadEntity(reader, func, splitIndexes, types, fieldNames);
+        }
+
+
+
+        static IEnumerable<TReturn> ReadEntity<TReturn>(this IDataReader reader, Delegate func, string[] splitOns)
+            => ReadEntity(reader, func, splitOns).Select(m => (TReturn)m);
+
+        static IEnumerable<TReturn> ReadEntity<TReturn>(this IDataReader reader, Delegate func, int[] splitIndexes = null)
+            => ReadEntity(reader, func, splitIndexes).Select(m => (TReturn)m);
+        #endregion
+
+
+        #region ReadEntity2
+        public static IEnumerable<TReturn> ReadEntity<TFirst, TSecond, TReturn>(this IDataReader reader, Func<TFirst, TSecond, TReturn> func)
+            => ReadEntity<TReturn>(reader, func);
+
+        public static IEnumerable<TReturn> ReadEntity<TFirst, TSecond, TReturn>(this IDataReader reader, Func<TFirst, TSecond, TReturn> func, string splitOn)
+            => ReadEntity<TReturn>(reader, func, new[] { splitOn });
+
+        public static IEnumerable<TReturn> ReadEntity<TFirst, TSecond, TReturn>(this IDataReader reader, Func<TFirst, TSecond, TReturn> func, string[] splitOns)
+            => ReadEntity<TReturn>(reader, func, splitOns);
+
+        public static IEnumerable<TReturn> ReadEntity<TFirst, TSecond, TReturn>(this IDataReader reader, Func<TFirst, TSecond, TReturn> func, int splitIndex)
+            => ReadEntity<TReturn>(reader, func, new[] { splitIndex });
+        public static IEnumerable<TReturn> ReadEntity<TFirst, TSecond, TReturn>(this IDataReader reader, Func<TFirst, TSecond, TReturn> func, int[] splitIndexes)
+            => ReadEntity<TReturn>(reader, func, splitIndexes);
+
+        #endregion
+
+        #region ReadEntity3
+        public static IEnumerable<TReturn> ReadEntity<TFirst, TSecond, TThird, TReturn>(this IDataReader reader, Func<TFirst, TSecond, TThird, TReturn> func)
+            => ReadEntity<TReturn>(reader, func);
+
+        public static IEnumerable<TReturn> ReadEntity<TFirst, TSecond, TThird, TReturn>(this IDataReader reader, Func<TFirst, TSecond, TThird, TReturn> func, string splitOn0, string splitOn1)
+            => ReadEntity<TReturn>(reader, func, new[] { splitOn0, splitOn1 });
+
+        public static IEnumerable<TReturn> ReadEntity<TFirst, TSecond, TThird, TReturn>(this IDataReader reader, Func<TFirst, TSecond, TThird, TReturn> func, string[] splitOns)
+            => ReadEntity<TReturn>(reader, func, splitOns);
+
+        public static IEnumerable<TReturn> ReadEntity<TFirst, TSecond, TThird, TReturn>(this IDataReader reader, Func<TFirst, TSecond, TThird, TReturn> func, int splitIndex0, int splitIndex1)
+            => ReadEntity<TReturn>(reader, func, new[] { splitIndex0, splitIndex1 });
+        public static IEnumerable<TReturn> ReadEntity<TFirst, TSecond, TThird, TReturn>(this IDataReader reader, Func<TFirst, TSecond, TThird, TReturn> func, int[] splitIndexes)
+            => ReadEntity<TReturn>(reader, func, splitIndexes);
+
+        #endregion
+
+
+        #endregion
+
+
+
+
+    }
+}

+ 1 - 1
src/Vitorm/Sql/QueryExecutor/Async/CountAsync.cs

@@ -38,7 +38,7 @@ namespace Vitorm.Sql.QueryExecutor
             combinedStream.method = method;
 
             // #3 Execute
-            var countValue = await dbContext.ExecuteScalarAsync(sql: sql, param: arg.sqlParam, useReadOnly: true);
+            var countValue = await dbContext.ExecuteScalarAsync(sql: sql, parameters: arg.sqlParam, useReadOnly: true);
             var count = Convert.ToInt32(countValue);
 
             // Count and TotalCount

+ 1 - 1
src/Vitorm/Sql/QueryExecutor/Async/ExecuteDeleteAsync.cs

@@ -30,7 +30,7 @@ namespace Vitorm.Sql.QueryExecutor
             combinedStream.method = method;
 
             // #3 Execute
-            return await dbContext.ExecuteAsync(sql: sql, param: arg.sqlParam);
+            return await dbContext.ExecuteAsync(sql: sql, parameters: arg.sqlParam);
         }
     }
 }

+ 1 - 1
src/Vitorm/Sql/QueryExecutor/Async/ExecuteUpdateAsync.cs

@@ -34,7 +34,7 @@ namespace Vitorm.Sql.QueryExecutor
             combinedStream.method = method;
 
             // #3 Execute
-            return await dbContext.ExecuteAsync(sql: sql, param: arg.sqlParam);
+            return await dbContext.ExecuteAsync(sql: sql, parameters: arg.sqlParam);
         }
     }
 }

+ 1 - 1
src/Vitorm/Sql/QueryExecutor/Async/FirstOrDefaultAsync.cs

@@ -38,7 +38,7 @@ namespace Vitorm.Sql.QueryExecutor
             combinedStream.method = method;
 
             // #3 Execute
-            using var reader = await dbContext.ExecuteReaderAsync(sql: sql, param: arg.sqlParam, useReadOnly: true);
+            using var reader = await dbContext.ExecuteReaderAsync(sql: sql, parameters: arg.sqlParam, useReadOnly: true);
 
             return (Result)arg.dataReader.ReadData(reader);
         }

+ 2 - 1
src/Vitorm/Sql/QueryExecutor/Async/ToListAndTotalCountAsync.cs

@@ -6,6 +6,7 @@ using System.Threading.Tasks;
 
 using Vit.Linq;
 
+using Vitorm.Sql.DataReader;
 using Vitorm.Sql.SqlTranslate;
 using Vitorm.StreamQuery;
 
@@ -66,7 +67,7 @@ namespace Vitorm.Sql.QueryExecutor
                 // #3 read data
                 {
                     var sql = sqlCount + " ;\r\n" + sqlToList;
-                    using var reader = await dbContext.ExecuteReaderAsync(sql: sql, param: arg.sqlParam, useReadOnly: true);
+                    using var reader = await dbContext.ExecuteReaderAsync(sql: sql, parameters: arg.sqlParam, useReadOnly: true);
                     reader.Read();
                     totalCount = Convert.ToInt32(reader[0]);
                     reader.NextResult();

+ 1 - 1
src/Vitorm/Sql/QueryExecutor/Async/ToListAsync.cs

@@ -38,7 +38,7 @@ namespace Vitorm.Sql.QueryExecutor
             var sql = sqlTranslateService.PrepareQuery(arg, combinedStream);
 
             // #3 Execute
-            using var reader = await dbContext.ExecuteReaderAsync(sql: sql, param: arg.sqlParam, useReadOnly: true);
+            using var reader = await dbContext.ExecuteReaderAsync(sql: sql, parameters: arg.sqlParam, useReadOnly: true);
             return (List<Result>)arg.dataReader.ReadData(reader);
         }
 

+ 1 - 1
src/Vitorm/Sql/QueryExecutor/Sync/Count.cs

@@ -38,7 +38,7 @@ namespace Vitorm.Sql.QueryExecutor
             var sql = sqlTranslateService.PrepareCountQuery(arg, combinedStream);
 
             // #3 Execute
-            var countValue = dbContext.ExecuteScalar(sql: sql, param: arg.sqlParam, useReadOnly: true);
+            var countValue = dbContext.ExecuteScalar(sql: sql, parameters: arg.sqlParam, useReadOnly: true);
             var count = Convert.ToInt32(countValue);
 
             // Count and TotalCount

+ 1 - 1
src/Vitorm/Sql/QueryExecutor/Sync/ExecuteDelete.cs

@@ -23,7 +23,7 @@ namespace Vitorm.Sql.QueryExecutor
             var sql = sqlTranslateService.PrepareExecuteDelete(arg, combinedStream);
 
             // #3 Execute
-            return dbContext.Execute(sql: sql, param: arg.sqlParam);
+            return dbContext.Execute(sql: sql, parameters: arg.sqlParam);
         }
 
     }

+ 1 - 1
src/Vitorm/Sql/QueryExecutor/Sync/ExecuteUpdate.cs

@@ -28,7 +28,7 @@ namespace Vitorm.Sql.QueryExecutor
             var sql = sqlTranslateService.PrepareExecuteUpdate(arg, streamToUpdate);
 
             // #3 Execute
-            return dbContext.Execute(sql: sql, param: arg.sqlParam);
+            return dbContext.Execute(sql: sql, parameters: arg.sqlParam);
         }
     }
 }

+ 1 - 1
src/Vitorm/Sql/QueryExecutor/Sync/FirstOrDefault.cs

@@ -25,7 +25,7 @@ namespace Vitorm.Sql.QueryExecutor
             var sql = sqlTranslateService.PrepareQuery(arg, combinedStream);
 
             // #3 Execute
-            using var reader = dbContext.ExecuteReader(sql: sql, param: arg.sqlParam, useReadOnly: true);
+            using var reader = dbContext.ExecuteReader(sql: sql, parameters: arg.sqlParam, useReadOnly: true);
             return arg.dataReader.ReadData(reader);
         }
 

+ 1 - 1
src/Vitorm/Sql/QueryExecutor/Sync/ToList.cs

@@ -32,7 +32,7 @@ namespace Vitorm.Sql.QueryExecutor
             var sql = sqlTranslateService.PrepareQuery(arg, combinedStream);
 
             // #3 Execute
-            using var reader = dbContext.ExecuteReader(sql: sql, param: arg.sqlParam, useReadOnly: true);
+            using var reader = dbContext.ExecuteReader(sql: sql, parameters: arg.sqlParam, useReadOnly: true);
             return arg.dataReader.ReadData(reader);
         }
 

+ 2 - 1
src/Vitorm/Sql/QueryExecutor/Sync/ToListAndTotalCount.cs

@@ -4,6 +4,7 @@ using System.Reflection;
 
 using Vit.Linq;
 
+using Vitorm.Sql.DataReader;
 using Vitorm.Sql.SqlTranslate;
 using Vitorm.StreamQuery;
 
@@ -56,7 +57,7 @@ namespace Vitorm.Sql.QueryExecutor
                 // #3 read data
                 {
                     var sql = sqlCount + " ;\r\n" + sqlToList;
-                    using var reader = dbContext.ExecuteReader(sql: sql, param: arg.sqlParam, useReadOnly: true);
+                    using var reader = dbContext.ExecuteReader(sql: sql, parameters: arg.sqlParam, useReadOnly: true);
                     reader.Read();
                     totalCount = Convert.ToInt32(reader[0]);
                     reader.NextResult();

+ 92 - 77
src/Vitorm/Sql/SqlDbContext.Execute.cs

@@ -3,7 +3,9 @@ using System.Collections.Generic;
 using System.Data;
 using System.Threading.Tasks;
 
+using Vitorm.Sql.SqlExecute;
 using Vitorm.Sql.Transaction;
+using Vitorm.Transaction;
 
 namespace Vitorm.Sql
 {
@@ -13,11 +15,11 @@ namespace Vitorm.Sql
         {
             try
             {
-                transactionScope?.Dispose();
+                transactionManager?.Dispose();
             }
             finally
             {
-                transactionScope = null;
+                transactionManager = null;
                 try
                 {
                     _dbConnection?.Dispose();
@@ -80,78 +82,89 @@ namespace Vitorm.Sql
 
 
         #region Transaction
-        public virtual Func<SqlDbContext, ITransactionScope> createTransactionScope { set; get; }
-                    = (dbContext) => new SqlTransactionScope(dbContext);
-        protected virtual ITransactionScope transactionScope { get; set; }
+        public virtual Func<SqlDbContext, ITransactionManager> createTransactionManager { set; get; }
+                    = (dbContext) => new SqlTransactionManager(dbContext);
+        protected virtual ITransactionManager transactionManager { get; set; }
 
-        public virtual IDbTransaction BeginTransaction()
+        public override ITransaction BeginTransaction()
         {
-            transactionScope ??= createTransactionScope(this);
-            return transactionScope.BeginTransaction();
+            transactionManager ??= createTransactionManager(this);
+            return transactionManager.BeginTransaction();
         }
-        public virtual IDbTransaction GetCurrentTransaction() => transactionScope?.GetCurrentTransaction();
+        public virtual IDbTransaction GetDbTransaction() => (transactionManager as SqlTransactionManager)?.GetDbTransaction();
 
         #endregion
 
 
 
         #region Sync Method
-        public virtual int ExecuteWithTransaction(string sql, IDictionary<string, object> param = null, IDbTransaction transaction = null)
+        public virtual int ExecuteWithTransaction(string sql, Dictionary<string, object> parameters = null, IDbTransaction transaction = null, bool isProcedure = false)
         {
+            transaction ??= GetDbTransaction();
             commandTimeout ??= this.commandTimeout ?? defaultCommandTimeout;
 
-            return sqlExecutor.Execute(dbConnection, sql, param: param, transaction: transaction, commandTimeout: commandTimeout);
+            return sqlExecutor.Execute(dbConnection, sql, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout, isProcedure: isProcedure);
         }
 
-        public virtual int Execute(string sql, IDictionary<string, object> param = null, int? commandTimeout = null, bool useReadOnly = false)
+        public virtual int Execute(ExecuteArgument arg, bool useReadOnly = false)
         {
-            this.Event_OnExecuting(sql, param);
+            arg.transaction ??= GetDbTransaction();
+            arg.connection ??= useReadOnly && arg.transaction == null ? readOnlyDbConnection : dbConnection;
+            arg.commandTimeout ??= this.commandTimeout ?? defaultCommandTimeout;
 
+            return sqlExecutor.Execute(arg);
+        }
+        public virtual int Execute(string sql, Dictionary<string, object> parameters = null, int? commandTimeout = null, bool useReadOnly = false, bool isProcedure = false)
+        {
+            this.Event_OnExecuting(sql, parameters);
+
+            var transaction = GetDbTransaction();
+            var connection = useReadOnly && transaction == null ? readOnlyDbConnection : dbConnection;
             commandTimeout ??= this.commandTimeout ?? defaultCommandTimeout;
-            var transaction = GetCurrentTransaction();
 
-            if (useReadOnly && transaction == null)
-            {
-                return sqlExecutor.Execute(readOnlyDbConnection, sql, param: param, commandTimeout: commandTimeout);
-            }
-            else
-            {
-                return sqlExecutor.Execute(dbConnection, sql, param: param, transaction: transaction, commandTimeout: commandTimeout);
-            }
+            return sqlExecutor.Execute(connection, sql, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout, isProcedure: isProcedure);
         }
 
-        public virtual IDataReader ExecuteReader(string sql, IDictionary<string, object> param = null, int? commandTimeout = null, bool useReadOnly = false)
+
+
+
+        public virtual IDataReader ExecuteReader(ExecuteArgument arg, bool useReadOnly = false)
         {
-            this.Event_OnExecuting(sql, param);
+            arg.transaction ??= GetDbTransaction();
+            arg.connection ??= useReadOnly && arg.transaction == null ? readOnlyDbConnection : dbConnection;
+            arg.commandTimeout ??= this.commandTimeout ?? defaultCommandTimeout;
 
+            return sqlExecutor.ExecuteReader(arg);
+        }
+        public virtual IDataReader ExecuteReader(string sql, Dictionary<string, object> parameters = null, int? commandTimeout = null, bool useReadOnly = false, bool isProcedure = false)
+        {
+            this.Event_OnExecuting(sql, parameters);
+
+            var transaction = GetDbTransaction();
+            var connection = useReadOnly && transaction == null ? readOnlyDbConnection : dbConnection;
             commandTimeout ??= this.commandTimeout ?? defaultCommandTimeout;
-            var transaction = GetCurrentTransaction();
 
-            if (useReadOnly && transaction == null)
-            {
-                return sqlExecutor.ExecuteReader(readOnlyDbConnection, sql, param: param, commandTimeout: commandTimeout);
-            }
-            else
-            {
-                return sqlExecutor.ExecuteReader(dbConnection, sql, param: param, transaction: transaction, commandTimeout: commandTimeout);
-            }
+            return sqlExecutor.ExecuteReader(connection, sql, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout, isProcedure: isProcedure);
         }
 
-        public virtual object ExecuteScalar(string sql, IDictionary<string, object> param = null, int? commandTimeout = null, bool useReadOnly = false)
+
+        public virtual object ExecuteScalar(ExecuteArgument arg, bool useReadOnly = false)
         {
-            this.Event_OnExecuting(sql, param);
+            arg.transaction ??= GetDbTransaction();
+            arg.connection ??= useReadOnly && arg.transaction == null ? readOnlyDbConnection : dbConnection;
+            arg.commandTimeout ??= this.commandTimeout ?? defaultCommandTimeout;
 
+            return sqlExecutor.ExecuteScalar(arg);
+        }
+        public virtual object ExecuteScalar(string sql, Dictionary<string, object> parameters = null, int? commandTimeout = null, bool useReadOnly = false, bool isProcedure = false)
+        {
+            this.Event_OnExecuting(sql, parameters);
+
+            var transaction = GetDbTransaction();
+            var connection = useReadOnly && transaction == null ? readOnlyDbConnection : dbConnection;
             commandTimeout ??= this.commandTimeout ?? defaultCommandTimeout;
-            var transaction = GetCurrentTransaction();
 
-            if (useReadOnly && transaction == null)
-            {
-                return sqlExecutor.ExecuteScalar(readOnlyDbConnection, sql, param: param, commandTimeout: commandTimeout);
-            }
-            else
-            {
-                return sqlExecutor.ExecuteScalar(dbConnection, sql, param: param, transaction: transaction, commandTimeout: commandTimeout);
-            }
+            return sqlExecutor.ExecuteScalar(connection, sql, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout, isProcedure: isProcedure);
         }
 
         #endregion
@@ -160,56 +173,58 @@ namespace Vitorm.Sql
 
         #region Async Method
 
-        public virtual async Task<int> ExecuteWithTransactionAsync(string sql, IDictionary<string, object> param = null, IDbTransaction transaction = null)
+        public virtual Task<int> ExecuteAsync(ExecuteArgument arg, bool useReadOnly = false)
         {
-            commandTimeout ??= this.commandTimeout ?? defaultCommandTimeout;
+            arg.transaction ??= GetDbTransaction();
+            arg.connection ??= useReadOnly && arg.transaction == null ? readOnlyDbConnection : dbConnection;
+            arg.commandTimeout ??= this.commandTimeout ?? defaultCommandTimeout;
 
-            return await sqlExecutor.ExecuteAsync(dbConnection, sql, param: param, transaction: transaction, commandTimeout: commandTimeout);
+            return sqlExecutor.ExecuteAsync(arg);
         }
 
-        public virtual async Task<int> ExecuteAsync(string sql, IDictionary<string, object> param = null, int? commandTimeout = null, bool useReadOnly = false)
+        public virtual Task<int> ExecuteAsync(string sql, Dictionary<string, object> parameters = null, int? commandTimeout = null, bool useReadOnly = false, bool isProcedure = false)
         {
+            var transaction = GetDbTransaction();
+            var connection = useReadOnly && transaction == null ? readOnlyDbConnection : dbConnection;
             commandTimeout ??= this.commandTimeout ?? defaultCommandTimeout;
-            var transaction = GetCurrentTransaction();
 
-            if (useReadOnly && transaction == null)
-            {
-                return await sqlExecutor.ExecuteAsync(readOnlyDbConnection, sql, param: param, commandTimeout: commandTimeout);
-            }
-            else
-            {
-                return await sqlExecutor.ExecuteAsync(dbConnection, sql, param: param, transaction: transaction, commandTimeout: commandTimeout);
-            }
+            return sqlExecutor.ExecuteAsync(connection, sql, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout, isProcedure: isProcedure);
         }
 
-        public virtual async Task<IDataReader> ExecuteReaderAsync(string sql, IDictionary<string, object> param = null, int? commandTimeout = null, bool useReadOnly = false)
+
+        public virtual Task<IDataReader> ExecuteReaderAsync(ExecuteArgument arg, bool useReadOnly = false)
+        {
+            arg.transaction ??= GetDbTransaction();
+            arg.connection ??= useReadOnly && arg.transaction == null ? readOnlyDbConnection : dbConnection;
+            arg.commandTimeout ??= this.commandTimeout ?? defaultCommandTimeout;
+
+            return sqlExecutor.ExecuteReaderAsync(arg);
+        }
+        public virtual Task<IDataReader> ExecuteReaderAsync(string sql, Dictionary<string, object> parameters = null, int? commandTimeout = null, bool useReadOnly = false, bool isProcedure = false)
         {
+            var transaction = GetDbTransaction();
+            var connection = useReadOnly && transaction == null ? readOnlyDbConnection : dbConnection;
             commandTimeout ??= this.commandTimeout ?? defaultCommandTimeout;
-            var transaction = GetCurrentTransaction();
 
-            if (useReadOnly && transaction == null)
-            {
-                return await sqlExecutor.ExecuteReaderAsync(readOnlyDbConnection, sql, param: param, commandTimeout: commandTimeout);
-            }
-            else
-            {
-                return await sqlExecutor.ExecuteReaderAsync(dbConnection, sql, param: param, transaction: transaction, commandTimeout: commandTimeout);
-            }
+            return sqlExecutor.ExecuteReaderAsync(connection, sql, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout, isProcedure: isProcedure);
         }
 
-        public virtual async Task<object> ExecuteScalarAsync(string sql, IDictionary<string, object> param = null, int? commandTimeout = null, bool useReadOnly = false)
+
+        public virtual Task<object> ExecuteScalarAsync(ExecuteArgument arg, bool useReadOnly = false)
         {
+            arg.transaction ??= GetDbTransaction();
+            arg.connection ??= useReadOnly && arg.transaction == null ? readOnlyDbConnection : dbConnection;
+            arg.commandTimeout ??= this.commandTimeout ?? defaultCommandTimeout;
+
+            return sqlExecutor.ExecuteScalarAsync(arg);
+        }
+        public virtual Task<object> ExecuteScalarAsync(string sql, Dictionary<string, object> parameters = null, int? commandTimeout = null, bool useReadOnly = false, bool isProcedure = false)
+        {
+            var transaction = GetDbTransaction();
+            var connection = useReadOnly && transaction == null ? readOnlyDbConnection : dbConnection;
             commandTimeout ??= this.commandTimeout ?? defaultCommandTimeout;
-            var transaction = GetCurrentTransaction();
 
-            if (useReadOnly && transaction == null)
-            {
-                return await sqlExecutor.ExecuteScalarAsync(readOnlyDbConnection, sql, param: param, commandTimeout: commandTimeout);
-            }
-            else
-            {
-                return await sqlExecutor.ExecuteScalarAsync(dbConnection, sql, param: param, transaction: transaction, commandTimeout: commandTimeout);
-            }
+            return sqlExecutor.ExecuteScalarAsync(connection, sql, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout, isProcedure: isProcedure);
         }
 
         #endregion

+ 1 - 0
src/Vitorm/Sql/SqlDbContext.cs

@@ -5,6 +5,7 @@ using System.Linq.Expressions;
 using Vit.Linq;
 
 using Vitorm.Sql.DataReader.EntityReader;
+using Vitorm.Sql.SqlExecute;
 using Vitorm.Sql.SqlTranslate;
 using Vitorm.StreamQuery;
 

+ 9 - 9
src/Vitorm/Sql/SqlDbSet.Async.cs

@@ -46,7 +46,7 @@ namespace Vitorm.Sql
             // #3 Execute
             if (addType == EAddType.identityKey)
             {
-                var newKeyValue = await sqlDbContext.ExecuteScalarAsync(sql: sql, param: sqlParam);
+                var newKeyValue = await sqlDbContext.ExecuteScalarAsync(sql: sql, parameters: sqlParam);
 
                 // set key value to entity
                 var keyType = TypeUtil.GetUnderlyingType(entityDescriptor.key.type);
@@ -58,7 +58,7 @@ namespace Vitorm.Sql
             }
             else
             {
-                await sqlDbContext.ExecuteAsync(sql: sql, param: sqlParam);
+                await sqlDbContext.ExecuteAsync(sql: sql, parameters: sqlParam);
             }
 
             return entity;
@@ -86,7 +86,7 @@ namespace Vitorm.Sql
                 // #3 Execute
                 if (addType == EAddType.identityKey)
                 {
-                    var newKeyValue = await sqlDbContext.ExecuteScalarAsync(sql: sqlAndGetParams.sql, param: sqlParam);
+                    var newKeyValue = await sqlDbContext.ExecuteScalarAsync(sql: sqlAndGetParams.sql, parameters: sqlParam);
 
                     // set key value to entity
                     var keyType = TypeUtil.GetUnderlyingType(entityDescriptor.key.type);
@@ -99,7 +99,7 @@ namespace Vitorm.Sql
                 }
                 else
                 {
-                    await sqlDbContext.ExecuteAsync(sql: sqlAndGetParams.sql, param: sqlParam);
+                    await sqlDbContext.ExecuteAsync(sql: sqlAndGetParams.sql, parameters: sqlParam);
                     affectedRowCount++;
                 }
             }
@@ -122,7 +122,7 @@ namespace Vitorm.Sql
             sqlParam[entityDescriptor.keyName] = keyValue;
 
             // #3 execute
-            using var reader = await sqlDbContext.ExecuteReaderAsync(sql: sql, param: sqlParam, useReadOnly: true);
+            using var reader = await sqlDbContext.ExecuteReaderAsync(sql: sql, parameters: sqlParam, useReadOnly: true);
 
             if (reader is DbDataReader dataReader ? await dataReader.ReadAsync() : reader.Read())
             {
@@ -155,7 +155,7 @@ namespace Vitorm.Sql
             var sqlParam = GetSqlParams(entity);
 
             // #3 execute
-            var affectedRowCount = await sqlDbContext.ExecuteAsync(sql: sql, param: sqlParam);
+            var affectedRowCount = await sqlDbContext.ExecuteAsync(sql: sql, parameters: sqlParam);
 
             return affectedRowCount;
         }
@@ -173,7 +173,7 @@ namespace Vitorm.Sql
             foreach (var entity in entities)
             {
                 var sqlParam = GetSqlParams(entity);
-                affectedRowCount += await sqlDbContext.ExecuteAsync(sql: sql, param: sqlParam);
+                affectedRowCount += await sqlDbContext.ExecuteAsync(sql: sql, parameters: sqlParam);
             }
             return affectedRowCount;
         }
@@ -205,7 +205,7 @@ namespace Vitorm.Sql
             sqlParam[entityDescriptor.keyName] = keyValue;
 
             // #3 execute
-            var affectedRowCount = await sqlDbContext.ExecuteAsync(sql: sql, param: sqlParam);
+            var affectedRowCount = await sqlDbContext.ExecuteAsync(sql: sql, parameters: sqlParam);
 
             return affectedRowCount;
         }
@@ -219,7 +219,7 @@ namespace Vitorm.Sql
             var sql = sqlTranslateService.PrepareDeleteByKeys(arg, keys);
 
             // #2 execute
-            var affectedRowCount = await sqlDbContext.ExecuteAsync(sql: sql, param: arg.sqlParam);
+            var affectedRowCount = await sqlDbContext.ExecuteAsync(sql: sql, parameters: arg.sqlParam);
             return affectedRowCount;
         }
         #endregion

+ 9 - 9
src/Vitorm/Sql/SqlDbSet.cs

@@ -84,7 +84,7 @@ namespace Vitorm.Sql
             // #3 Execute
             if (addType == EAddType.identityKey)
             {
-                var newKeyValue = sqlDbContext.ExecuteScalar(sql: sql, param: sqlParam);
+                var newKeyValue = sqlDbContext.ExecuteScalar(sql: sql, parameters: sqlParam);
 
                 // set key value to entity
                 var keyType = TypeUtil.GetUnderlyingType(entityDescriptor.key.type);
@@ -96,7 +96,7 @@ namespace Vitorm.Sql
             }
             else
             {
-                sqlDbContext.Execute(sql: sql, param: sqlParam);
+                sqlDbContext.Execute(sql: sql, parameters: sqlParam);
             }
 
             return entity;
@@ -123,7 +123,7 @@ namespace Vitorm.Sql
                 // #3 Execute
                 if (addType == EAddType.identityKey)
                 {
-                    var newKeyValue = sqlDbContext.ExecuteScalar(sql: sqlAndGetParams.sql, param: sqlParam);
+                    var newKeyValue = sqlDbContext.ExecuteScalar(sql: sqlAndGetParams.sql, parameters: sqlParam);
 
                     // set key value to entity
                     var keyType = TypeUtil.GetUnderlyingType(entityDescriptor.key.type);
@@ -136,7 +136,7 @@ namespace Vitorm.Sql
                 }
                 else
                 {
-                    sqlDbContext.Execute(sql: sqlAndGetParams.sql, param: sqlParam);
+                    sqlDbContext.Execute(sql: sqlAndGetParams.sql, parameters: sqlParam);
                     affectedRowCount++;
                 }
             }
@@ -159,7 +159,7 @@ namespace Vitorm.Sql
             sqlParam[entityDescriptor.keyName] = keyValue;
 
             // #3 execute
-            using var reader = sqlDbContext.ExecuteReader(sql: sql, param: sqlParam, useReadOnly: true);
+            using var reader = sqlDbContext.ExecuteReader(sql: sql, parameters: sqlParam, useReadOnly: true);
             if (reader.Read())
             {
                 var entity = (Entity)Activator.CreateInstance(entityDescriptor.entityType);
@@ -189,7 +189,7 @@ namespace Vitorm.Sql
             var sqlParam = GetSqlParams(entity);
 
             // #3 execute
-            var affectedRowCount = sqlDbContext.Execute(sql: sql, param: sqlParam);
+            var affectedRowCount = sqlDbContext.Execute(sql: sql, parameters: sqlParam);
 
             return affectedRowCount;
         }
@@ -207,7 +207,7 @@ namespace Vitorm.Sql
             foreach (var entity in entities)
             {
                 var sqlParam = GetSqlParams(entity);
-                affectedRowCount += sqlDbContext.Execute(sql: sql, param: sqlParam);
+                affectedRowCount += sqlDbContext.Execute(sql: sql, parameters: sqlParam);
             }
             return affectedRowCount;
         }
@@ -239,7 +239,7 @@ namespace Vitorm.Sql
             sqlParam[entityDescriptor.keyName] = keyValue;
 
             // #3 execute
-            var affectedRowCount = sqlDbContext.Execute(sql: sql, param: sqlParam);
+            var affectedRowCount = sqlDbContext.Execute(sql: sql, parameters: sqlParam);
 
             return affectedRowCount;
         }
@@ -253,7 +253,7 @@ namespace Vitorm.Sql
             var sql = sqlTranslateService.PrepareDeleteByKeys(arg, keys);
 
             // #2 execute
-            var affectedRowCount = sqlDbContext.Execute(sql: sql, param: arg.sqlParam);
+            var affectedRowCount = sqlDbContext.Execute(sql: sql, parameters: arg.sqlParam);
             return affectedRowCount;
         }
         #endregion

+ 27 - 0
src/Vitorm/Sql/SqlExecute/ExecuteArgument.cs

@@ -0,0 +1,27 @@
+using System.Collections.Generic;
+using System.Data;
+
+namespace Vitorm.Sql.SqlExecute
+{
+    public class ExecuteArgument
+    {
+        public ExecuteArgument() { }
+        public ExecuteArgument(IDbConnection connection, string text, IDictionary<string, object> parameters = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null)
+        {
+            this.connection = connection;
+            this.text = text;
+            this.parameters = parameters;
+            this.transaction = transaction;
+            this.commandTimeout = commandTimeout;
+            this.commandType = commandType;
+        }
+
+
+        public IDbConnection connection { get; set; }
+        public string text { get; set; }
+        public IDictionary<string, object> parameters { get; set; }
+        public CommandType? commandType { get; set; }
+        public IDbTransaction transaction { get; set; }
+        public int? commandTimeout { get; set; }
+    }
+}

+ 137 - 0
src/Vitorm/Sql/SqlExecute/SqlExecutor.Async.cs

@@ -0,0 +1,137 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Data.Common;
+using System.Threading.Tasks;
+
+using DbCommand = System.Data.Common.DbCommand;
+using DbTransaction = System.Data.Common.DbTransaction;
+
+namespace Vitorm.Sql.SqlExecute
+{
+    public partial class SqlExecutor
+    {
+
+        public Func<DbConnection, Task> CloseAsync = (DbConnection conn) => { conn.Close(); return Task.CompletedTask; };
+
+        public virtual Task<int> ExecuteAsync(IDbConnection connection, string sql, IDictionary<string, object> parameters = null, IDbTransaction transaction = null, int? commandTimeout = null, bool isProcedure = false)
+         => ExecuteAsync(new(connection, sql, parameters, transaction, commandTimeout, commandType: isProcedure ? CommandType.StoredProcedure : null));
+
+        public virtual async Task<int> ExecuteAsync(ExecuteArgument arg)
+        {
+            if (arg.connection is DbConnection connection)
+            {
+                // #1 setup command
+                using var cmd = connection.CreateCommand();
+                cmd.Connection = connection;
+
+                if (arg.transaction != null) cmd.Transaction = (DbTransaction)arg.transaction;
+                if (arg.commandTimeout.HasValue) cmd.CommandTimeout = arg.commandTimeout.Value;
+
+                if (arg.commandType.HasValue) cmd.CommandType = arg.commandType.Value;
+                cmd.CommandText = arg.text;
+
+                AddParameters(cmd, arg.parameters);
+
+
+                // #2 execute
+                bool wasClosed = connection.State == ConnectionState.Closed;
+                try
+                {
+                    if (wasClosed) await connection.OpenAsync();
+                    return await cmd.ExecuteNonQueryAsync();
+                }
+                finally
+                {
+                    if (wasClosed) await CloseAsync(connection);
+                }
+            }
+
+            return await Task.Run(() => Execute(arg));
+        }
+
+        public virtual Task<object> ExecuteScalarAsync(IDbConnection connection, string sql, IDictionary<string, object> parameters = null, IDbTransaction transaction = null, int? commandTimeout = null, bool isProcedure = false)
+            => ExecuteScalarAsync(new(connection, sql, parameters, transaction, commandTimeout, commandType: isProcedure ? CommandType.StoredProcedure : null));
+        public virtual async Task<object> ExecuteScalarAsync(ExecuteArgument arg)
+        {
+            if (arg.connection is DbConnection connection)
+            {
+                // #1 setup command
+                using var cmd = connection.CreateCommand();
+                cmd.Connection = connection;
+
+                if (arg.transaction != null) cmd.Transaction = (DbTransaction)arg.transaction;
+                if (arg.commandTimeout.HasValue) cmd.CommandTimeout = arg.commandTimeout.Value;
+
+                if (arg.commandType.HasValue) cmd.CommandType = arg.commandType.Value;
+                cmd.CommandText = arg.text;
+
+                AddParameters(cmd, arg.parameters);
+
+                // #2 execute
+                bool wasClosed = connection.State == ConnectionState.Closed;
+                try
+                {
+                    if (wasClosed) await connection.OpenAsync();
+                    return await cmd.ExecuteScalarAsync();
+                }
+                finally
+                {
+
+                    if (wasClosed) await CloseAsync(connection);
+                }
+            }
+
+            return await Task.Run(() => ExecuteScalar(arg));
+        }
+
+        public Task<IDataReader> ExecuteReaderAsync(IDbConnection connection, string sql, IDictionary<string, object> parameters = null, IDbTransaction transaction = null, int? commandTimeout = null, bool isProcedure = false)
+             => ExecuteReaderAsync(new(connection, sql, parameters, transaction, commandTimeout, commandType: isProcedure ? CommandType.StoredProcedure : null));
+        public virtual async Task<IDataReader> ExecuteReaderAsync(ExecuteArgument arg)
+        {
+            if (arg.connection is DbConnection connection)
+            {
+                DbCommand cmd = null;
+
+                bool wasClosed = connection.State == ConnectionState.Closed, disposeCommand = true;
+                try
+                {
+                    // #1 setup command
+                    cmd = connection.CreateCommand();
+                    cmd.Connection = connection;
+
+                    if (arg.transaction != null) cmd.Transaction = (DbTransaction)arg.transaction;
+                    if (arg.commandTimeout.HasValue) cmd.CommandTimeout = arg.commandTimeout.Value;
+
+                    if (arg.commandType.HasValue) cmd.CommandType = arg.commandType.Value;
+                    cmd.CommandText = arg.text;
+
+                    AddParameters(cmd, arg.parameters);
+
+                    // #2 execute
+                    var commandBehavior = wasClosed ? CommandBehavior.CloseConnection : CommandBehavior.Default;
+                    if (wasClosed) await connection.OpenAsync();
+
+                    var reader = await cmd.ExecuteReaderAsync(commandBehavior);
+
+                    wasClosed = false; // don't dispose before giving it to them!
+                    disposeCommand = false;
+                    return reader;
+                }
+                finally
+                {
+                    if (wasClosed) await CloseAsync(connection);
+
+                    if (disposeCommand)
+                    {
+                        //cmd.Parameters.Clear();
+                        cmd.Dispose();
+                    }
+                }
+            }
+
+            return await Task.Run(() => ExecuteReader(arg));
+        }
+
+    }
+}

+ 134 - 0
src/Vitorm/Sql/SqlExecute/SqlExecutor.cs

@@ -0,0 +1,134 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+
+namespace Vitorm.Sql.SqlExecute
+{
+    public partial class SqlExecutor
+    {
+        public readonly static SqlExecutor Instance = new();
+
+
+        public virtual int Execute(IDbConnection connection, string sql, IDictionary<string, object> parameters = null, IDbTransaction transaction = null, int? commandTimeout = null, bool isProcedure = false)
+            => Execute(new(connection, sql, parameters, transaction, commandTimeout, commandType: isProcedure ? CommandType.StoredProcedure : null));
+
+        public virtual int Execute(ExecuteArgument arg)
+        {
+            // #1 setup command
+            using var cmd = arg.connection.CreateCommand();
+            cmd.Connection = arg.connection;
+
+            if (arg.transaction != null) cmd.Transaction = arg.transaction;
+            if (arg.commandTimeout.HasValue) cmd.CommandTimeout = arg.commandTimeout.Value;
+
+            if (arg.commandType.HasValue) cmd.CommandType = arg.commandType.Value;
+            cmd.CommandText = arg.text;
+
+            AddParameters(cmd, arg.parameters);
+
+            // #2 execute
+            bool wasClosed = arg.connection.State == ConnectionState.Closed;
+            try
+            {
+                if (wasClosed) arg.connection.Open();
+                return cmd.ExecuteNonQuery();
+            }
+            finally
+            {
+                if (wasClosed) arg.connection.Close();
+            }
+
+        }
+
+
+
+        public virtual object ExecuteScalar(IDbConnection connection, string sql, IDictionary<string, object> parameters = null, IDbTransaction transaction = null, int? commandTimeout = null, bool isProcedure = false)
+            => ExecuteScalar(new(connection, sql, parameters, transaction, commandType: isProcedure ? CommandType.StoredProcedure : null));
+
+        public virtual object ExecuteScalar(ExecuteArgument arg)
+        {
+            // #1 setup command
+            using var cmd = arg.connection.CreateCommand();
+            cmd.Connection = arg.connection;
+
+            if (arg.transaction != null) cmd.Transaction = arg.transaction;
+            if (arg.commandTimeout.HasValue) cmd.CommandTimeout = arg.commandTimeout.Value;
+
+            if (arg.commandType.HasValue) cmd.CommandType = arg.commandType.Value;
+            cmd.CommandText = arg.text;
+
+            AddParameters(cmd, arg.parameters);
+
+            // #2 execute
+            bool wasClosed = arg.connection.State == ConnectionState.Closed;
+            try
+            {
+                if (wasClosed) arg.connection.Open();
+                return cmd.ExecuteScalar();
+            }
+            finally
+            {
+                if (wasClosed) arg.connection.Close();
+            }
+        }
+
+
+
+        public virtual IDataReader ExecuteReader(IDbConnection connection, string sql, IDictionary<string, object> parameters = null, IDbTransaction transaction = null, int? commandTimeout = null, bool isProcedure = false)
+            => ExecuteReader(new(connection, sql, parameters, transaction, commandTimeout, commandType: isProcedure ? CommandType.StoredProcedure : null));
+
+        public virtual IDataReader ExecuteReader(ExecuteArgument arg)
+        {
+            IDbCommand cmd = null;
+
+            bool wasClosed = arg.connection.State == ConnectionState.Closed, disposeCommand = true;
+            try
+            {
+                // #1 setup command
+                cmd = arg.connection.CreateCommand();
+                cmd.Connection = arg.connection;
+
+                if (arg.transaction != null) cmd.Transaction = arg.transaction;
+                if (arg.commandTimeout.HasValue) cmd.CommandTimeout = arg.commandTimeout.Value;
+
+                if (arg.commandType.HasValue) cmd.CommandType = arg.commandType.Value;
+                cmd.CommandText = arg.text;
+
+                AddParameters(cmd, arg.parameters);
+
+                // #2 execute
+                var commandBehavior = wasClosed ? CommandBehavior.CloseConnection : CommandBehavior.Default;
+                if (wasClosed) arg.connection.Open();
+
+                var reader = cmd.ExecuteReader(commandBehavior);
+                wasClosed = false; // don't dispose before giving it to them!
+                disposeCommand = false;
+                return reader;
+            }
+            finally
+            {
+                if (wasClosed) arg.connection.Close();
+                if (disposeCommand)
+                {
+                    //cmd.Parameters.Clear();
+                    cmd.Dispose();
+                }
+            }
+        }
+
+
+        public virtual void AddParameters(IDbCommand cmd, IDictionary<string, object> parameters)
+        {
+            if (parameters != null)
+            {
+                foreach (var parameter in parameters)
+                {
+                    var p = cmd.CreateParameter();
+                    p.ParameterName = parameter.Key;
+                    p.Value = parameter.Value ?? DBNull.Value;
+                    cmd.Parameters.Add(p);
+                }
+            }
+        }
+    }
+}

+ 0 - 119
src/Vitorm/Sql/SqlExecutor.Async.cs

@@ -1,119 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Data;
-using System.Data.Common;
-using System.Threading.Tasks;
-
-using DbCommand = System.Data.Common.DbCommand;
-using DbTransaction = System.Data.Common.DbTransaction;
-namespace Vitorm.Sql
-{
-
-    public partial class SqlExecutor
-    {
-
-        public Func<DbConnection, Task> CloseAsync = (DbConnection conn) => { conn.Close(); return Task.CompletedTask; };
-
-
-        public virtual async Task<int> ExecuteAsync(IDbConnection _conn, string sql, IDictionary<string, object> param = null, IDbTransaction transaction = null, int? commandTimeout = null)
-        {
-            if (_conn is DbConnection conn)
-            {
-                // #1 setup command
-                using var cmd = conn.CreateCommand();
-                if (transaction != null) cmd.Transaction = (DbTransaction)transaction;
-                if (commandTimeout.HasValue) cmd.CommandTimeout = commandTimeout.Value;
-                cmd.Connection = conn;
-                cmd.CommandText = sql;
-                AddParameter(cmd, param);
-
-
-                // #2 execute
-                bool wasClosed = conn.State == ConnectionState.Closed;
-                try
-                {
-                    if (wasClosed) await conn.OpenAsync();
-                    return await cmd.ExecuteNonQueryAsync();
-                }
-                finally
-                {
-                    if (wasClosed) await CloseAsync(conn);
-                }
-            }
-
-            return await Task.Run(() => Execute(_conn, sql, param: param, transaction: transaction, commandTimeout: commandTimeout));
-        }
-
-        public virtual async Task<object> ExecuteScalarAsync(IDbConnection _conn, string sql, IDictionary<string, object> param = null, IDbTransaction transaction = null, int? commandTimeout = null)
-        {
-            if (_conn is DbConnection conn)
-            {
-                // #1 setup command
-                using var cmd = conn.CreateCommand();
-                if (transaction != null) cmd.Transaction = (DbTransaction)transaction;
-                if (commandTimeout.HasValue) cmd.CommandTimeout = commandTimeout.Value;
-                cmd.Connection = conn;
-                cmd.CommandText = sql;
-                AddParameter(cmd, param);
-
-                // #2 execute
-                bool wasClosed = conn.State == ConnectionState.Closed;
-                try
-                {
-                    if (wasClosed) await conn.OpenAsync();
-                    return await cmd.ExecuteScalarAsync();
-                }
-                finally
-                {
-
-                    if (wasClosed) await CloseAsync(conn);
-                }
-            }
-
-            return await Task.Run(() => ExecuteScalar(_conn, sql, param: param, transaction: transaction, commandTimeout: commandTimeout));
-        }
-
-        public virtual async Task<IDataReader> ExecuteReaderAsync(IDbConnection _conn, string sql, IDictionary<string, object> param = null, IDbTransaction transaction = null, int? commandTimeout = null)
-        {
-            if (_conn is DbConnection conn)
-            {
-                DbCommand cmd = null;
-
-                bool wasClosed = conn.State == ConnectionState.Closed, disposeCommand = true;
-                try
-                {
-                    // #1 setup command
-                    cmd = conn.CreateCommand();
-                    if (transaction != null) cmd.Transaction = (DbTransaction)transaction;
-                    if (commandTimeout.HasValue) cmd.CommandTimeout = commandTimeout.Value;
-                    cmd.Connection = conn;
-                    cmd.CommandText = sql;
-                    AddParameter(cmd, param);
-
-                    // #2 execute
-                    var commandBehavior = wasClosed ? CommandBehavior.CloseConnection : CommandBehavior.Default;
-                    if (wasClosed) await conn.OpenAsync();
-
-                    var reader = await cmd.ExecuteReaderAsync(commandBehavior);
-
-                    wasClosed = false; // don't dispose before giving it to them!
-                    disposeCommand = false;
-                    return reader;
-                }
-                finally
-                {
-                    if (wasClosed) await CloseAsync(conn);
-
-                    if (disposeCommand)
-                    {
-                        //cmd.Parameters.Clear();
-                        cmd.Dispose();
-                    }
-                }
-            }
-
-            return await Task.Run(() => ExecuteReader(_conn, sql, param: param, transaction: transaction, commandTimeout: commandTimeout));
-        }
-
-    }
-}

+ 0 - 110
src/Vitorm/Sql/SqlExecutor.cs

@@ -1,110 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Data;
-
-namespace Vitorm.Sql
-{
-    public partial class SqlExecutor
-    {
-        public readonly static SqlExecutor Instance = new();
-
-        public virtual int Execute(IDbConnection conn, string sql, IDictionary<string, object> param = null, IDbTransaction transaction = null, int? commandTimeout = null)
-        {
-            // #1 setup command
-            using var cmd = conn.CreateCommand();
-            if (transaction != null) cmd.Transaction = transaction;
-            if (commandTimeout.HasValue) cmd.CommandTimeout = commandTimeout.Value;
-            cmd.Connection = conn;
-            cmd.CommandText = sql;
-            AddParameter(cmd, param);
-
-
-            // #2 execute
-            bool wasClosed = conn.State == ConnectionState.Closed;
-            try
-            {
-                if (wasClosed) conn.Open();
-                return cmd.ExecuteNonQuery();
-            }
-            finally
-            {
-                if (wasClosed) conn.Close();
-            }
-
-        }
-
-
-        public virtual object ExecuteScalar(IDbConnection conn, string sql, IDictionary<string, object> param = null, IDbTransaction transaction = null, int? commandTimeout = null)
-        {
-            // #1 setup command
-            using var cmd = conn.CreateCommand();
-            if (transaction != null) cmd.Transaction = transaction;
-            if (commandTimeout.HasValue) cmd.CommandTimeout = commandTimeout.Value;
-            cmd.Connection = conn;
-            cmd.CommandText = sql;
-            AddParameter(cmd, param);
-
-            // #2 execute
-            bool wasClosed = conn.State == ConnectionState.Closed;
-            try
-            {
-                if (wasClosed) conn.Open();
-                return cmd.ExecuteScalar();
-            }
-            finally
-            {
-                if (wasClosed) conn.Close();
-            }
-        }
-
-        public virtual IDataReader ExecuteReader(IDbConnection conn, string sql, IDictionary<string, object> param = null, IDbTransaction transaction = null, int? commandTimeout = null)
-        {
-            IDbCommand cmd = null;
-
-            bool wasClosed = conn.State == ConnectionState.Closed, disposeCommand = true;
-            try
-            {
-                // #1 setup command
-                cmd = conn.CreateCommand();
-                if (transaction != null) cmd.Transaction = transaction;
-                if (commandTimeout.HasValue) cmd.CommandTimeout = commandTimeout.Value;
-                cmd.Connection = conn;
-                cmd.CommandText = sql;
-                AddParameter(cmd, param);
-
-                // #2 execute
-                var commandBehavior = wasClosed ? CommandBehavior.CloseConnection : CommandBehavior.Default;
-                if (wasClosed) conn.Open();
-
-                var reader = cmd.ExecuteReader(commandBehavior);
-                wasClosed = false; // don't dispose before giving it to them!
-                disposeCommand = false;
-                return reader;
-            }
-            finally
-            {
-                if (wasClosed) conn.Close();
-                if (disposeCommand)
-                {
-                    //cmd.Parameters.Clear();
-                    cmd.Dispose();
-                }
-            }
-        }
-
-
-        public virtual void AddParameter(IDbCommand cmd, IDictionary<string, object> param)
-        {
-            if (param != null)
-            {
-                foreach (var entry in param)
-                {
-                    var p = cmd.CreateParameter();
-                    p.ParameterName = entry.Key;
-                    p.Value = entry.Value ?? DBNull.Value;
-                    cmd.Parameters.Add(p);
-                }
-            }
-        }
-    }
-}

+ 2 - 0
src/Vitorm/Sql/SqlTranslate/QueryTranslateArgument.cs

@@ -4,6 +4,8 @@ using System.Linq;
 
 using Vit.Linq.ExpressionNodes.ComponentModel;
 
+using Vitorm.Sql.DataReader;
+
 namespace Vitorm.Sql.SqlTranslate
 {
     public class QueryTranslateArgument

+ 3 - 6
src/Vitorm/Sql/SqlTranslate/QueryTranslateService.cs

@@ -46,16 +46,13 @@ namespace Vitorm.Sql.SqlTranslate
                     }
                 case nameof(Queryable.FirstOrDefault) or nameof(Queryable.First) or nameof(Queryable.LastOrDefault) or nameof(Queryable.Last):
                     {
-                        if (stream.skip.HasValue)
+                        if (stream.method.Contains("Last"))
                         {
-                            if (stream.method.Contains("Last"))
+                            if (stream.skip.HasValue)
                             {
                                 stream.skip = stream.skip.Value + (stream.take ?? 0) - 1;
                             }
-                        }
-                        else
-                        {
-                            if (stream.method.Contains("Last"))
+                            else
                             {
                                 ReverseOrder(arg, stream);
                             }

+ 8 - 0
src/Vitorm/Sql/Transaction/ETransactionState.cs

@@ -0,0 +1,8 @@
+namespace Vitorm.Sql.Transaction
+{
+    public enum ETransactionState
+    {
+        Active, Committed, RolledBack, Disposed
+    }
+
+}

+ 0 - 11
src/Vitorm/Sql/Transaction/ITransactionScope.cs

@@ -1,11 +0,0 @@
-using System;
-using System.Data;
-
-namespace Vitorm.Sql.Transaction
-{
-    public interface ITransactionScope : IDisposable
-    {
-        IDbTransaction BeginTransaction();
-        IDbTransaction GetCurrentTransaction();
-    }
-}

+ 6 - 8
src/Vitorm/Sql/Transaction/DbTransactionWrap.cs → src/Vitorm/Sql/Transaction/SqlTransaction.cs

@@ -1,23 +1,21 @@
 using System.Data;
 
+using Vitorm.Transaction;
+
 namespace Vitorm.Sql.Transaction
 {
-    public class DbTransactionWrap : IDbTransaction
+    public partial class SqlTransaction : ITransaction
     {
-        public enum ETransactionState
-        {
-            Active, Committed, RolledBack, Disposed
-        }
         public virtual ETransactionState TransactionState { get; protected set; } = ETransactionState.Active;
-        public DbTransactionWrap(IDbTransaction transaction)
+        public SqlTransaction(IDbTransaction transaction)
         {
             originalTransaction = transaction;
         }
         public IDbTransaction originalTransaction;
 
-        public virtual IDbConnection Connection => originalTransaction.Connection;
+        //public virtual IDbConnection Connection => originalTransaction.Connection;
 
-        public virtual System.Data.IsolationLevel IsolationLevel => originalTransaction.IsolationLevel;
+        //public virtual System.Data.IsolationLevel IsolationLevel => originalTransaction.IsolationLevel;
 
         public virtual void Commit()
         {

+ 12 - 10
src/Vitorm/Sql/Transaction/SqlTransactionScope.cs → src/Vitorm/Sql/Transaction/SqlTransactionManager.cs

@@ -1,28 +1,30 @@
 using System.Collections.Generic;
 using System.Data;
 
+using Vitorm.Transaction;
+
 namespace Vitorm.Sql.Transaction
 {
-    public class SqlTransactionScope : ITransactionScope
+    public class SqlTransactionManager : ITransactionManager
     {
-        public SqlTransactionScope(SqlDbContext dbContext)
+        public SqlTransactionManager(SqlDbContext dbContext)
         {
             this.dbContext = dbContext;
         }
 
         protected SqlDbContext dbContext;
-        protected Stack<DbTransactionWrap> transactions = new();
+        protected Stack<SqlTransaction> transactions = new();
 
 
-        public virtual IDbTransaction BeginTransaction()
+        public virtual ITransaction BeginTransaction()
         {
             var dbConnection = dbContext.dbConnection;
             if (dbConnection.State != ConnectionState.Open) dbConnection.Open();
             var transaction = dbConnection.BeginTransaction();
 
-            var transactionWrap = new DbTransactionWrap(transaction);
-            transactions.Push(transactionWrap);
-            return transactionWrap;
+            var sqlTransaction = new SqlTransaction(transaction);
+            transactions.Push(sqlTransaction);
+            return sqlTransaction;
         }
 
         public virtual void Dispose()
@@ -30,7 +32,7 @@ namespace Vitorm.Sql.Transaction
             while (transactions?.Count > 0)
             {
                 var transaction = transactions.Pop();
-                if (transaction?.TransactionState != DbTransactionWrap.ETransactionState.Disposed)
+                if (transaction?.TransactionState != ETransactionState.Disposed)
                 {
                     transaction?.Dispose();
                 }
@@ -38,12 +40,12 @@ namespace Vitorm.Sql.Transaction
             transactions = null;
         }
 
-        public virtual IDbTransaction GetCurrentTransaction()
+        public virtual IDbTransaction GetDbTransaction()
         {
             while (transactions?.Count > 0)
             {
                 var tran = transactions.Peek();
-                if (tran?.TransactionState == DbTransactionWrap.ETransactionState.Active) return tran.originalTransaction;
+                if (tran?.TransactionState == ETransactionState.Active) return tran.originalTransaction;
                 transactions.Pop();
             }
             return null;

+ 10 - 0
src/Vitorm/Transaction/ITransaction.cs

@@ -0,0 +1,10 @@
+using System;
+
+namespace Vitorm.Transaction
+{
+    public interface ITransaction : IDisposable
+    {
+        void Commit();
+        void Rollback();
+    }
+}

+ 9 - 0
src/Vitorm/Transaction/ITransactionManager.cs

@@ -0,0 +1,9 @@
+using System;
+
+namespace Vitorm.Transaction
+{
+    public interface ITransactionManager : IDisposable
+    {
+        ITransaction BeginTransaction();
+    }
+}

+ 1 - 1
src/Vitorm/TypeUtil.cs

@@ -37,7 +37,7 @@ namespace Vitorm
         public static bool IsArrayType(Type type)
         {
             if (type.IsArray) return true;
-            if (type.IsGenericType && typeof(ICollection).IsAssignableFrom(type)) return true;
+            if (type.IsGenericType && typeof(IEnumerable).IsAssignableFrom(type)) return true;
             return false;
         }
 

+ 2 - 2
test/IssuesTest/Vitorm.MsTest.Issue000_099/Issues/Issue_004_Test.cs

@@ -25,7 +25,7 @@ namespace Vitorm.MsTest.Issue000_099.Issues
             dbContext.Execute(@"
 CREATE TABLE issue004_schema.Issue004_MyUser (id int NOT NULL primary key,  name varchar(1000) DEFAULT NULL);
 insert into issue004_schema.Issue004_MyUser(id,name) values(1,@name);
-", param: new Dictionary<string, object> { ["name"] = name });
+", parameters: new Dictionary<string, object> { ["name"] = name });
 
 
             // #2 Assert
@@ -51,7 +51,7 @@ use `issue004_schema`;
 drop table if exists `issue004_schema`.`Issue004_MyUser`;
 CREATE TABLE IF NOT EXISTS `issue004_schema`.`Issue004_MyUser` (`id` int NOT NULL primary key,  `name` varchar(1000) DEFAULT NULL);
 insert into `issue004_schema`.`Issue004_MyUser`(`id`,name) values(1,@name);
-", param: new Dictionary<string, object> { ["name"] = name });
+", parameters: new Dictionary<string, object> { ["name"] = name });
 
             // #2 Assert
             {

+ 4 - 4
test/IssuesTest/Vitorm.MsTest.Issue000_099/Vitorm.MsTest.Issue000_099.csproj

@@ -5,7 +5,7 @@
     </PropertyGroup>
 
     <PropertyGroup>
-        <TargetFramework>net6.0</TargetFramework>
+        <TargetFramework>net8.0</TargetFramework>
         <ImplicitUsings>enable</ImplicitUsings>
 
         <IsPackable>false</IsPackable>
@@ -14,9 +14,9 @@
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
-        <PackageReference Include="MSTest.TestAdapter" Version="3.6.1" />
-        <PackageReference Include="MSTest.TestFramework" Version="3.6.1" />
+        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
+        <PackageReference Include="MSTest.TestAdapter" Version="3.6.4" />
+        <PackageReference Include="MSTest.TestFramework" Version="3.6.4" />
 
         <PackageReference Include="Vit.Core" Version="2.3.0" />
     </ItemGroup>

+ 6 - 6
test/Vitorm.Data.Benchmark/Vitorm.Data.Benchmark.csproj

@@ -2,7 +2,7 @@
 
     <PropertyGroup>
         <OutputType>Exe</OutputType>
-        <TargetFramework>net6.0</TargetFramework>
+        <TargetFramework>net8.0</TargetFramework>
         <ImplicitUsings>enable</ImplicitUsings>
         <Nullable>enable</Nullable>
         <RootNamespace>App</RootNamespace>
@@ -10,12 +10,12 @@
 
     <ItemGroup>
       <PackageReference Include="BenchmarkDotNet" Version="0.14.0" />
-      <PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.32" />
-      <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.32" />
-      <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.32" />
-      <PackageReference Include="MySql.EntityFrameworkCore" Version="8.0.5" />
+      <PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.0" />
+      <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.0" />
+      <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.0" />
+      <PackageReference Include="MySql.EntityFrameworkCore" Version="8.0.8" />
  
-      <PackageReference Include="SqlSugarCore" Version="5.1.4.169" />
+      <PackageReference Include="SqlSugarCore" Version="5.1.4.171" />
     </ItemGroup>
 
     <ItemGroup>

+ 1 - 1
test/Vitorm.Data.Console/Vitorm.Data.Console.csproj

@@ -2,7 +2,7 @@
 
     <PropertyGroup>
         <OutputType>Exe</OutputType>
-        <TargetFramework>net6.0</TargetFramework>
+        <TargetFramework>net8.0</TargetFramework>
         <ImplicitUsings>enable</ImplicitUsings>
         <Nullable>enable</Nullable>
     </PropertyGroup>

+ 4 - 4
test/Vitorm.Data.MsTest/Vitorm.Data.MsTest.csproj

@@ -5,7 +5,7 @@
     </PropertyGroup>
 
     <PropertyGroup>
-        <TargetFramework>net6.0</TargetFramework>
+        <TargetFramework>net8.0</TargetFramework>
         <ImplicitUsings>enable</ImplicitUsings>
 
         <IsPackable>false</IsPackable>
@@ -14,9 +14,9 @@
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
-        <PackageReference Include="MSTest.TestAdapter" Version="3.6.1" />
-        <PackageReference Include="MSTest.TestFramework" Version="3.6.1" />
+        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
+        <PackageReference Include="MSTest.TestAdapter" Version="3.6.4" />
+        <PackageReference Include="MSTest.TestFramework" Version="3.6.4" />
 
         <PackageReference Include="Vit.Core" Version="2.3.0" />
     </ItemGroup>

+ 4 - 4
test/Vitorm.EntityGenerate.MsTest/Vitorm.EntityGenerate.MsTest.csproj

@@ -5,7 +5,7 @@
     </PropertyGroup>
 
     <PropertyGroup>
-        <TargetFramework>net6.0</TargetFramework>
+        <TargetFramework>net8.0</TargetFramework>
         <ImplicitUsings>enable</ImplicitUsings>
 
         <IsPackable>false</IsPackable>
@@ -14,9 +14,9 @@
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
-        <PackageReference Include="MSTest.TestAdapter" Version="3.6.1" />
-        <PackageReference Include="MSTest.TestFramework" Version="3.6.1" />
+        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
+        <PackageReference Include="MSTest.TestAdapter" Version="3.6.4" />
+        <PackageReference Include="MSTest.TestFramework" Version="3.6.4" />
 
         <PackageReference Include="Vit.Core" Version="2.3.0" />
     </ItemGroup>

+ 4 - 4
test/Vitorm.MsTest/Vitorm.MsTest.csproj

@@ -5,7 +5,7 @@
     </PropertyGroup>
 
     <PropertyGroup>
-        <TargetFramework>net6.0</TargetFramework>
+        <TargetFramework>net8.0</TargetFramework>
         <ImplicitUsings>enable</ImplicitUsings>
 
         <IsPackable>false</IsPackable>
@@ -14,9 +14,9 @@
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
-        <PackageReference Include="MSTest.TestAdapter" Version="3.6.1" />
-        <PackageReference Include="MSTest.TestFramework" Version="3.6.1" />
+        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
+        <PackageReference Include="MSTest.TestAdapter" Version="3.6.4" />
+        <PackageReference Include="MSTest.TestFramework" Version="3.6.4" />
 
         <PackageReference Include="Vit.Core" Version="2.3.0" />
     </ItemGroup>

+ 141 - 0
test/Vitorm.MySql.MsTest/CustomTest/Procedure_Test.cs

@@ -0,0 +1,141 @@
+using System.Data;
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace Vitorm.MsTest.CustomTest
+{
+
+    [TestClass]
+    public class Procedure_Test
+    {
+
+        [TestMethod]
+        public void Procedure()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+
+            // create procedure
+            {
+                dbContext.Execute(@"
+                    DROP PROCEDURE IF EXISTS GetUser;
+
+                    CREATE PROCEDURE GetUser(IN uid INT)
+                    BEGIN
+                        SELECT * FROM `User` WHERE userId = uid order by userId;
+                        SELECT * FROM `User` WHERE userId != uid order by userId;
+                    END
+                    ");
+            }
+
+
+            // ExecuteReader
+            {
+                using var reader = dbContext.ExecuteReader("GetUser", new() { ["uid"] = 1 }, isProcedure: true);
+
+                var userList = reader.ReadEntity<User>().ToList();
+                Assert.AreEqual("1", String.Join(",", userList.Select(u => u.id)));
+            }
+
+            // ExecuteReader
+            {
+                using var reader = dbContext.ExecuteReader("GetUser", new() { ["uid"] = 1 }, isProcedure: true);
+                {
+                    var userList = reader.ReadEntity<User>().ToList();
+                    Assert.AreEqual("1", String.Join(",", userList.Select(u => u.id)));
+                }
+                reader.NextResult();
+                {
+                    var userList = reader.ReadEntity<User>().ToList();
+                    Assert.AreEqual("2,3,4,5,6", String.Join(",", userList.Select(u => u.id)));
+                }
+            }
+
+            // ExecuteReader
+            {
+                using var reader = dbContext.ExecuteReader("GetUser", new() { ["uid"] = 0 }, isProcedure: true);
+                {
+                    var userList = reader.ReadEntity<User>().ToList();
+                    Assert.AreEqual("", String.Join(",", userList.Select(u => u.id)));
+                }
+                reader.NextResult();
+                {
+                    var userList = reader.ReadEntity<User>().ToList();
+                    Assert.AreEqual("1,2,3,4,5,6", String.Join(",", userList.Select(u => u.id)));
+                }
+            }
+
+            // ExecuteReader
+            {
+                var userId = dbContext.ExecuteScalar("GetUser", new() { ["uid"] = 1 }, isProcedure: true);
+                Assert.AreEqual(1, userId);
+            }
+        }
+
+
+
+        [TestMethod]
+        public async Task ProcedureAsync()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+
+            // create procedure
+            {
+                await dbContext.ExecuteAsync(@"
+                    DROP PROCEDURE IF EXISTS GetUser;
+
+                    CREATE PROCEDURE GetUser(IN uid INT)
+                    BEGIN
+                        SELECT * FROM `User` WHERE userId = uid order by userId;
+                        SELECT * FROM `User` WHERE userId != uid order by userId;
+                    END
+                    ");
+            }
+
+
+            // ExecuteReader
+            {
+                using var reader = await dbContext.ExecuteReaderAsync("GetUser", new() { ["uid"] = 1 }, isProcedure: true);
+
+                var userList = reader.ReadEntity<User>().ToList();
+                Assert.AreEqual("1", String.Join(",", userList.Select(u => u.id)));
+            }
+
+            // ExecuteReader
+            {
+                using var reader = await dbContext.ExecuteReaderAsync("GetUser", new() { ["uid"] = 1 }, isProcedure: true);
+                {
+                    var userList = reader.ReadEntity<User>().ToList();
+                    Assert.AreEqual("1", String.Join(",", userList.Select(u => u.id)));
+                }
+                reader.NextResult();
+                {
+                    var userList = reader.ReadEntity<User>().ToList();
+                    Assert.AreEqual("2,3,4,5,6", String.Join(",", userList.Select(u => u.id)));
+                }
+            }
+
+            // ExecuteReader
+            {
+                using var reader = await dbContext.ExecuteReaderAsync("GetUser", new() { ["uid"] = 0 }, isProcedure: true);
+                {
+                    var userList = reader.ReadEntity<User>().ToList();
+                    Assert.AreEqual("", String.Join(",", userList.Select(u => u.id)));
+                }
+                reader.NextResult();
+                {
+                    var userList = reader.ReadEntity<User>().ToList();
+                    Assert.AreEqual("1,2,3,4,5,6", String.Join(",", userList.Select(u => u.id)));
+                }
+            }
+
+            // ExecuteReader
+            {
+                var userId = await dbContext.ExecuteScalarAsync("GetUser", new() { ["uid"] = 1 }, isProcedure: true);
+                Assert.AreEqual(1, userId);
+            }
+        }
+
+
+
+    }
+}

+ 4 - 4
test/Vitorm.MySql.MsTest/Vitorm.MySql.MsTest.csproj

@@ -5,7 +5,7 @@
     </PropertyGroup>
 
     <PropertyGroup>
-        <TargetFramework>net6.0</TargetFramework>
+        <TargetFramework>net8.0</TargetFramework>
         <ImplicitUsings>enable</ImplicitUsings>
 
         <IsPackable>false</IsPackable>
@@ -14,9 +14,9 @@
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
-        <PackageReference Include="MSTest.TestAdapter" Version="3.6.1" />
-        <PackageReference Include="MSTest.TestFramework" Version="3.6.1" />
+        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
+        <PackageReference Include="MSTest.TestAdapter" Version="3.6.4" />
+        <PackageReference Include="MSTest.TestFramework" Version="3.6.4" />
 
         <PackageReference Include="Vit.Core" Version="2.3.0" />
     </ItemGroup>

+ 4 - 4
test/Vitorm.SqlServer.MsTest/Vitorm.SqlServer.MsTest.csproj

@@ -5,7 +5,7 @@
     </PropertyGroup>
 
     <PropertyGroup>
-        <TargetFramework>net6.0</TargetFramework>
+        <TargetFramework>net8.0</TargetFramework>
         <ImplicitUsings>enable</ImplicitUsings>
 
         <IsPackable>false</IsPackable>
@@ -14,9 +14,9 @@
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
-        <PackageReference Include="MSTest.TestAdapter" Version="3.6.1" />
-        <PackageReference Include="MSTest.TestFramework" Version="3.6.1" />
+        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
+        <PackageReference Include="MSTest.TestAdapter" Version="3.6.4" />
+        <PackageReference Include="MSTest.TestFramework" Version="3.6.4" />
 
         <PackageReference Include="Vit.Core" Version="2.3.0" />
     </ItemGroup>

+ 1 - 1
test/Vitorm.Sqlite.Console/Vitorm.Sqlite.Console.csproj

@@ -2,7 +2,7 @@
 
     <PropertyGroup>
         <OutputType>Exe</OutputType>
-        <TargetFramework>net6.0</TargetFramework>
+        <TargetFramework>net8.0</TargetFramework>
         <ImplicitUsings>enable</ImplicitUsings>
         <Nullable>enable</Nullable>
     </PropertyGroup>

+ 56 - 0
test/Vitorm.Sqlite.MsTest/CommonTest/Query_Group_FirstOrDefault_Test.cs

@@ -0,0 +1,56 @@
+using System.Data;
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+
+namespace Vitorm.MsTest.CommonTest
+{
+    [TestClass]
+    public class Query_Group_FirstOrDefault_Test
+    {
+
+        [TestMethod]
+        public void Test_Group_FirstOrDefault()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            // Linq Expression
+            {
+                var query =
+                        from user in userQuery
+                        where user.fatherId != null
+                        group user by new { user.fatherId, user.motherId } into userGroup
+                        orderby userGroup.Key.fatherId, userGroup.Key.motherId
+                        select new { userGroup.Key.fatherId, userGroup.Key.motherId };
+
+                var row = query.FirstOrDefault();
+
+                Assert.AreEqual(4, row?.fatherId);
+                Assert.AreEqual(6, row?.motherId);
+            }
+
+            // Lambda Expression
+            {
+                var query =
+                        userQuery.Where(m => m.fatherId != null)
+                        .GroupBy(user => new { user.fatherId, user.motherId })
+                        .OrderBy(userGroup => userGroup.Key.fatherId).ThenBy(userGroup => userGroup.Key.motherId)
+                        .Select(userGroup => new
+                        {
+                            userGroup.Key.fatherId,
+                            userGroup.Key.motherId
+                        })
+                        ;
+
+                var row = query.FirstOrDefault();
+
+                Assert.AreEqual(4, row?.fatherId);
+                Assert.AreEqual(6, row?.motherId);
+            }
+        }
+
+
+
+    }
+}

+ 54 - 0
test/Vitorm.Sqlite.MsTest/CommonTest/Transaction_Nested_Test.cs

@@ -0,0 +1,54 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace Vitorm.MsTest.CommonTest
+{
+
+    [TestClass]
+    public class Transaction_Nested_Test
+    {
+
+        [TestMethod]
+        public void Test_NestedTransaction()
+        {
+            {
+                using var dbContext = DataSource.CreateDbContext();
+                var userSet = dbContext.DbSet<User>();
+
+                Assert.AreEqual("u400", userSet.Get(4).name);
+
+                using (var tran1 = dbContext.BeginTransaction())
+                {
+                    dbContext.Update(new User { id = 4, name = "u4001" });
+                    Assert.AreEqual("u4001", userSet.Get(4).name);
+
+                    using (var tran2 = dbContext.BeginTransaction())
+                    {
+                        dbContext.Update(new User { id = 4, name = "u4002" });
+                        Assert.AreEqual("u4002", userSet.Get(4).name);
+                    }
+                    Assert.AreEqual("u4001", userSet.Get(4).name);
+
+                    using (var tran2 = dbContext.BeginTransaction())
+                    {
+                        dbContext.Update(new User { id = 4, name = "u4002" });
+                        Assert.AreEqual("u4002", userSet.Get(4).name);
+                        tran2.Rollback();
+                    }
+                    Assert.AreEqual("u4001", userSet.Get(4).name);
+
+                    using (var tran2 = dbContext.BeginTransaction())
+                    {
+                        dbContext.Update(new User { id = 4, name = "u4003" });
+                        Assert.AreEqual("u4003", userSet.Get(4).name);
+                        tran2.Commit();
+                    }
+                    Assert.AreEqual("u4003", userSet.Get(4).name);
+                }
+
+                Assert.AreEqual("u400", userSet.Get(4).name);
+            }
+        }
+
+
+    }
+}

+ 4 - 46
test/Vitorm.Sqlite.MsTest/CommonTest/Transaction_Test.cs

@@ -48,8 +48,7 @@ namespace Vitorm.MsTest.CommonTest
         }
 
 
-        // can not test for db is not durable
-        //[TestMethod]
+        [TestMethod]
         public void Test_Dispose()
         {
             {
@@ -72,58 +71,17 @@ namespace Vitorm.MsTest.CommonTest
                 }
                 Assert.AreEqual("u4003", userSet.Get(4).name);
             }
+
             {
-                using var dbContext = DataSource.CreateDbContext();
+                using var dbContext = DataSource.CreateDbContext(autoInit: false);
                 var userSet = dbContext.DbSet<User>();
 
-                //Assert.AreEqual("u4002", userSet.Get(4).name);
+                Assert.AreEqual("u4002", userSet.Get(4).name);
             }
 
         }
 
-        [TestMethod]
-        public void Test_NestedTransaction()
-        {
-            #region NestedTransaction
-            {
-                using var dbContext = DataSource.CreateDbContext();
-                var userSet = dbContext.DbSet<User>();
 
-                Assert.AreEqual("u400", userSet.Get(4).name);
-
-                using (var tran1 = dbContext.BeginTransaction())
-                {
-                    dbContext.Update(new User { id = 4, name = "u4001" });
-                    Assert.AreEqual("u4001", userSet.Get(4).name);
-
-                    using (var tran2 = dbContext.BeginTransaction())
-                    {
-                        dbContext.Update(new User { id = 4, name = "u4002" });
-                        Assert.AreEqual("u4002", userSet.Get(4).name);
-                    }
-                    Assert.AreEqual("u4001", userSet.Get(4).name);
-
-                    using (var tran2 = dbContext.BeginTransaction())
-                    {
-                        dbContext.Update(new User { id = 4, name = "u4002" });
-                        Assert.AreEqual("u4002", userSet.Get(4).name);
-                        tran2.Rollback();
-                    }
-                    Assert.AreEqual("u4001", userSet.Get(4).name);
-
-                    using (var tran2 = dbContext.BeginTransaction())
-                    {
-                        dbContext.Update(new User { id = 4, name = "u4003" });
-                        Assert.AreEqual("u4003", userSet.Get(4).name);
-                        tran2.Commit();
-                    }
-                    Assert.AreEqual("u4003", userSet.Get(4).name);
-                }
-
-                Assert.AreEqual("u400", userSet.Get(4).name);
-            }
-            #endregion
-        }
 
 
 

+ 322 - 0
test/Vitorm.Sqlite.MsTest/CustomTest/ReadEntity_Test.cs

@@ -0,0 +1,322 @@
+using System.Data;
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace Vitorm.MsTest.CustomTest
+{
+
+    [TestClass]
+    public class ReadEntity_Test
+    {
+
+        [TestMethod]
+        public void ReadValue()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+
+            // ReadValue
+            {
+                using var reader = dbContext.ExecuteReader("select userId,userName,userBirth,userFatherId,userMotherId from `User`  where userId<=4 order by userId");
+
+                var ids = reader.ReadValue<int>().ToList();
+
+                Assert.AreEqual("1,2,3,4", String.Join(",", ids));
+            }
+
+            // ReadValue
+            {
+                using var reader = dbContext.ExecuteReader("select userId,userName,userBirth,userFatherId,userMotherId from `User`  where userId<=4 order by userId");
+
+                var names = reader.ReadValue<string>(1).ToList();
+
+                Assert.AreEqual("u146,u246,u356,u400", String.Join(",", names));
+            }
+
+            // ReadValue
+            {
+                using var reader = dbContext.ExecuteReader("select userId,userName,userBirth,userFatherId,userMotherId from `User`  where userId<=4 order by userId");
+
+                var ids = reader.ReadValue<int?>("UserFatherId").ToList();
+
+                Assert.AreEqual("4,4,5,", String.Join(",", ids));
+            }
+
+        }
+
+
+
+        [TestMethod]
+        public void ReadTuple()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            // ReadTuple
+            {
+                using var reader = dbContext.ExecuteReader("select userId,userName,userBirth,userFatherId,userMotherId from `User`  where userId<=4 order by userId");
+
+                List<(int id, string name)> userList = reader.ReadTuple<int, string>().ToList();
+
+                Assert.AreEqual("1,2,3,4", String.Join(",", userList.Select(u => u.id)));
+            }
+
+            // ReadTuple by indexes
+            {
+                using var reader = dbContext.ExecuteReader("select userId,userName,userBirth,userFatherId,userMotherId from `User`  where userId<=4 order by userId");
+
+                List<(int id, int? fatherId)> userList = reader.ReadTuple<int, int?>([0, 3]).ToList();
+
+                Assert.AreEqual("1,2,3,4", String.Join(",", userList.Select(u => u.id)));
+
+                Assert.AreEqual("4,4,5,", String.Join(",", userList.Select(u => u.fatherId)));
+            }
+
+
+            // ReadTuple by columnNames
+            {
+                using var reader = dbContext.ExecuteReader("select userId,userName,userBirth,userFatherId,userMotherId from `User`  where userId<=4 order by userId");
+
+                List<(int id, int? fatherId)> userList = reader.ReadTuple<int, int?>(["userId", "userFatherId"]).ToList();
+
+                Assert.AreEqual("1,2,3,4", String.Join(",", userList.Select(u => u.id)));
+
+                Assert.AreEqual("4,4,5,", String.Join(",", userList.Select(u => u.fatherId)));
+            }
+        }
+
+
+        [TestMethod]
+        public void ReadTuple3()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            // ReadTuple
+            {
+                using var reader = dbContext.ExecuteReader("select userId,userName,userBirth,userFatherId,userMotherId from `User`  where userId<=4 order by userId");
+
+                List<(int id, string name, DateTime? birth)> userList = reader.ReadTuple<int, string, DateTime?>().ToList();
+
+                Assert.AreEqual("1,2,3,4", String.Join(",", userList.Select(u => u.id)));
+            }
+
+            // ReadTuple by indexes
+            {
+                using var reader = dbContext.ExecuteReader("select userId,userName,userBirth,userFatherId,userMotherId from `User`  where userId<=4 order by userId");
+
+                List<(int id, int? fatherId, int? motherId)> userList = reader.ReadTuple<int, int?, int?>([0, 3, 4]).ToList();
+
+                Assert.AreEqual("1,2,3,4", String.Join(",", userList.Select(u => u.id)));
+                Assert.AreEqual("4,4,5,", String.Join(",", userList.Select(u => u.fatherId)));
+                Assert.AreEqual("6,6,6,", String.Join(",", userList.Select(u => u.motherId)));
+            }
+
+
+            // ReadTuple by columnNames
+            {
+                using var reader = dbContext.ExecuteReader("select userId,userName,userBirth,userFatherId,userMotherId from `User`  where userId<=4 order by userId");
+
+                List<(int id, int? fatherId, int? motherId)> userList = reader.ReadTuple<int, int?, int?>(["userId", "userFatherId", "UserMotherId"]).ToList();
+
+                Assert.AreEqual("1,2,3,4", String.Join(",", userList.Select(u => u.id)));
+                Assert.AreEqual("4,4,5,", String.Join(",", userList.Select(u => u.fatherId)));
+                Assert.AreEqual("6,6,6,", String.Join(",", userList.Select(u => u.motherId)));
+            }
+        }
+
+
+        [TestMethod]
+        public void ReadTuple4()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            // ReadTuple
+            {
+                using var reader = dbContext.ExecuteReader("select userId,userName,userBirth,userFatherId,userMotherId from `User`  where userId<=4 order by userId");
+
+                List<(int id, string name, DateTime? birth, int? fatherId)> userList = reader.ReadTuple<int, string, DateTime?, int?>().ToList();
+
+                Assert.AreEqual("1,2,3,4", String.Join(",", userList.Select(u => u.id)));
+                Assert.AreEqual("4,4,5,", String.Join(",", userList.Select(u => u.fatherId)));
+            }
+
+            // ReadTuple by indexes
+            {
+                using var reader = dbContext.ExecuteReader("select userId,userName,userBirth,userFatherId,userMotherId from `User`  where userId<=4 order by userId");
+
+                List<(int id, int? fatherId, int? motherId, string name)> userList = reader.ReadTuple<int, int?, int?, string>([0, 3, 4, 1]).ToList();
+
+                Assert.AreEqual("1,2,3,4", String.Join(",", userList.Select(u => u.id)));
+                Assert.AreEqual("4,4,5,", String.Join(",", userList.Select(u => u.fatherId)));
+                Assert.AreEqual("6,6,6,", String.Join(",", userList.Select(u => u.motherId)));
+                Assert.AreEqual("u146,u246,u356,u400", String.Join(",", userList.Select(u => u.name)));
+            }
+
+
+            // ReadTuple by columnNames
+            {
+                using var reader = dbContext.ExecuteReader("select userId,userName,userBirth,userFatherId,userMotherId from `User`  where userId<=4 order by userId");
+
+                List<(int id, int? fatherId, int? motherId, string name)> userList = reader.ReadTuple<int, int?, int?, string>(["userId", "userFatherId", "UserMotherId", "userName"]).ToList();
+
+                Assert.AreEqual("1,2,3,4", String.Join(",", userList.Select(u => u.id)));
+                Assert.AreEqual("4,4,5,", String.Join(",", userList.Select(u => u.fatherId)));
+                Assert.AreEqual("6,6,6,", String.Join(",", userList.Select(u => u.motherId)));
+                Assert.AreEqual("u146,u246,u356,u400", String.Join(",", userList.Select(u => u.name)));
+            }
+        }
+
+
+
+        [TestMethod]
+        public void ReadEntity()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            // ReadEntity
+            {
+                using var reader = dbContext.ExecuteReader("select * from `User`  where userId<=4 order by userId");
+
+                var userList = reader.ReadEntity<User>().ToList();
+
+                Assert.AreEqual("1,2,3,4", String.Join(",", userList.Select(u => u.id)));
+                Assert.AreEqual("4,4,5,", String.Join(",", userList.Select(u => u.fatherId)));
+                Assert.AreEqual("6,6,6,", String.Join(",", userList.Select(u => u.motherId)));
+                Assert.AreEqual("u146,u246,u356,u400", String.Join(",", userList.Select(u => u.name)));
+            }
+        }
+
+
+        [TestMethod]
+        public void ReadEntity2()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            // ReadEntity :  try read all columns for each entities
+            {
+                using var reader = dbContext.ExecuteReader("select userId,userName,userBirth,userFatherId,userMotherId from `User`  where userId<=4 order by userId");
+
+                var userList = reader.ReadEntity<User, User, (int id, string name, int? fatherId, int? motherId)>((u0, u1) => (u0.id, u0.name, u1.fatherId, u1.motherId)).ToList();
+
+                Assert.AreEqual("1,2,3,4", String.Join(",", userList.Select(u => u.id)));
+                Assert.AreEqual("u146,u246,u356,u400", String.Join(",", userList.Select(u => u.name)));
+                Assert.AreEqual("4,4,5,", String.Join(",", userList.Select(u => u.fatherId)));
+                Assert.AreEqual("6,6,6,", String.Join(",", userList.Select(u => u.motherId)));
+            }
+
+            // ReadEntity splitOn column not exist 
+            {
+                using var reader = dbContext.ExecuteReader("select userId,userName,userBirth,userFatherId,userMotherId from `User`  where userId<=4 order by userId");
+
+                var userList = reader.ReadEntity<User, User, (int id, string name, int? fatherId, int? motherId)>((u0, u1) => (u0.id, u0.name, u1.fatherId, u1.motherId), "dummyField").ToList();
+
+                Assert.AreEqual("1,2,3,4", String.Join(",", userList.Select(u => u.id)));
+                Assert.AreEqual("u146,u246,u356,u400", String.Join(",", userList.Select(u => u.name)));
+                Assert.AreEqual("4,4,5,", String.Join(",", userList.Select(u => u.fatherId)));
+                Assert.AreEqual("6,6,6,", String.Join(",", userList.Select(u => u.motherId)));
+            }
+
+            // ReadEntity splitOn
+            {
+                using var reader = dbContext.ExecuteReader("select userId,userName,userBirth,userFatherId,userMotherId from `User`  where userId<=4 order by userId");
+
+                var userList = reader.ReadEntity<User, User, (int id, string name, int? fatherId, int? motherId)>((u0, u1) => (u0.id, u0.name, u1.fatherId, u1.motherId), "userFatherId").ToList();
+
+                Assert.AreEqual("1,2,3,4", String.Join(",", userList.Select(u => u.id)));
+                Assert.AreEqual("u146,u246,u356,u400", String.Join(",", userList.Select(u => u.name)));
+                Assert.AreEqual("4,4,5,", String.Join(",", userList.Select(u => u.fatherId)));
+                Assert.AreEqual("6,6,6,", String.Join(",", userList.Select(u => u.motherId)));
+            }
+
+            // ReadEntity splitIndex
+            {
+                using var reader = dbContext.ExecuteReader("select userId,userName,userBirth,userFatherId,userMotherId from `User`  where userId<=4 order by userId");
+
+                var userList = reader.ReadEntity<User, User, (int id, string name, int? fatherId, int? motherId)>((u0, u1) => (u0.id, u0.name, u1.fatherId, u1.motherId), 3).ToList();
+
+                Assert.AreEqual("1,2,3,4", String.Join(",", userList.Select(u => u.id)));
+                Assert.AreEqual("u146,u246,u356,u400", String.Join(",", userList.Select(u => u.name)));
+                Assert.AreEqual("4,4,5,", String.Join(",", userList.Select(u => u.fatherId)));
+                Assert.AreEqual("6,6,6,", String.Join(",", userList.Select(u => u.motherId)));
+            }
+        }
+
+
+        [TestMethod]
+        public void ReadEntity3()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            // ReadEntity :  try read all columns for each entities
+            {
+                using var reader = dbContext.ExecuteReader("select userId,userName,userBirth,userFatherId,userMotherId from `User`  where userId<=4 order by userId");
+
+                var userList = reader.ReadEntity<User, User, User, (int id, string name, int? fatherId, int? motherId)>((u0, u1, u2) => (u0.id, u0.name, u1.fatherId, u2.motherId)).ToList();
+
+                Assert.AreEqual("1,2,3,4", String.Join(",", userList.Select(u => u.id)));
+                Assert.AreEqual("u146,u246,u356,u400", String.Join(",", userList.Select(u => u.name)));
+                Assert.AreEqual("4,4,5,", String.Join(",", userList.Select(u => u.fatherId)));
+                Assert.AreEqual("6,6,6,", String.Join(",", userList.Select(u => u.motherId)));
+            }
+
+            // ReadEntity splitOn column not exist 
+            {
+                using var reader = dbContext.ExecuteReader("select userId,userName,userBirth,userFatherId,userMotherId from `User`  where userId<=4 order by userId");
+
+                var userList = reader.ReadEntity<User, User, User, (int id, string name, int? fatherId, int? motherId)>((u0, u1, u2) => (u0.id, u0.name, u1.fatherId, u2.motherId), "dummyField", "dummyField").ToList();
+
+                Assert.AreEqual("1,2,3,4", String.Join(",", userList.Select(u => u.id)));
+                Assert.AreEqual("u146,u246,u356,u400", String.Join(",", userList.Select(u => u.name)));
+                Assert.AreEqual("4,4,5,", String.Join(",", userList.Select(u => u.fatherId)));
+                Assert.AreEqual("6,6,6,", String.Join(",", userList.Select(u => u.motherId)));
+            }
+
+            // ReadEntity splitOns
+            {
+                using var reader = dbContext.ExecuteReader("select userId,userName,userBirth,userFatherId,userMotherId from `User`  where userId<=4 order by userId");
+
+                var userList = reader.ReadEntity<User, User, User, (int id, string name, int? fatherId, int? motherId)>((u0, u1, u2) => (u0.id, u0.name, u1.fatherId, u2.motherId), "userFatherId", "userMotherId").ToList();
+
+                Assert.AreEqual("1,2,3,4", String.Join(",", userList.Select(u => u.id)));
+                Assert.AreEqual("u146,u246,u356,u400", String.Join(",", userList.Select(u => u.name)));
+                Assert.AreEqual("4,4,5,", String.Join(",", userList.Select(u => u.fatherId)));
+                Assert.AreEqual("6,6,6,", String.Join(",", userList.Select(u => u.motherId)));
+            }
+
+
+            // ReadEntity splitOns
+            {
+                using var reader = dbContext.ExecuteReader("select userId,userName,userBirth,   userFatherId,userId,  userMotherId,userId from `User`  where userId<=4 order by userId");
+
+                var userList = reader.ReadEntity<User, User, User, (int id, string name, int? fatherId, int? motherId)>((u0, u1, u2) => (u0.id, u0.name, u1.fatherId, u2.motherId), "userFatherId", "userMotherId").ToList();
+
+                Assert.AreEqual("1,2,3,4", String.Join(",", userList.Select(u => u.id)));
+                Assert.AreEqual("u146,u246,u356,u400", String.Join(",", userList.Select(u => u.name)));
+                Assert.AreEqual("4,4,5,", String.Join(",", userList.Select(u => u.fatherId)));
+                Assert.AreEqual("6,6,6,", String.Join(",", userList.Select(u => u.motherId)));
+            }
+
+            // ReadEntity splitIndex
+            {
+                using var reader = dbContext.ExecuteReader("select userId,userName,userBirth,userFatherId,userMotherId from `User`  where userId<=4 order by userId");
+
+                var userList = reader.ReadEntity<User, User, User, (int id, string name, int? fatherId, int? motherId)>((u0, u1, u2) => (u0.id, u0.name, u1.fatherId, u2.motherId), 3, 4).ToList();
+
+                Assert.AreEqual("1,2,3,4", String.Join(",", userList.Select(u => u.id)));
+                Assert.AreEqual("u146,u246,u356,u400", String.Join(",", userList.Select(u => u.name)));
+                Assert.AreEqual("4,4,5,", String.Join(",", userList.Select(u => u.fatherId)));
+                Assert.AreEqual("6,6,6,", String.Join(",", userList.Select(u => u.motherId)));
+            }
+        }
+
+
+
+
+    }
+}

+ 1 - 2
test/Vitorm.Sqlite.MsTest/DataSource.cs

@@ -62,8 +62,7 @@ namespace Vitorm.MsTest
         public static SqlDbContext CreateDbContextForWriting(bool autoInit = true) => CreateDbContext(autoInit);
         public static SqlDbContext CreateDbContext(bool autoInit = true)
         {
-            var guid = Guid.NewGuid().ToString();
-            var filePath = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, $"{guid}.sqlite.db");
+            var filePath = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, $"db_orm.sqlite.db");
             var connectionString = $"data source={filePath}";
 
             var dbContext = new SqlDbContext();

+ 4 - 4
test/Vitorm.Sqlite.MsTest/Vitorm.Sqlite.MsTest.csproj

@@ -5,7 +5,7 @@
     </PropertyGroup>
 
     <PropertyGroup>
-        <TargetFramework>net6.0</TargetFramework>
+        <TargetFramework>net8.0</TargetFramework>
         <ImplicitUsings>enable</ImplicitUsings>
 
         <IsPackable>false</IsPackable>
@@ -14,9 +14,9 @@
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
-        <PackageReference Include="MSTest.TestAdapter" Version="3.6.1" />
-        <PackageReference Include="MSTest.TestFramework" Version="3.6.1" />
+        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
+        <PackageReference Include="MSTest.TestAdapter" Version="3.6.4" />
+        <PackageReference Include="MSTest.TestFramework" Version="3.6.4" />
 
         <PackageReference Include="Vit.Core" Version="2.3.0" />
     </ItemGroup>