Browse Source

implement Mysql

Lith 11 tháng trước cách đây
mục cha
commit
3a5226943b

+ 12 - 3
README.md

@@ -20,10 +20,14 @@ sqlite/transactions  https://learn.microsoft.com/en-us/dotnet/standard/data/sqli
 --------------
 # cur
 
+
+
+# rename to Vitorm
+
 # support ElasticSearch
 # support ClickHouse
 
-# support Mysql
+
 
 # remove depency of Dapper
 # try to make it clean
@@ -35,7 +39,7 @@ sqlite/transactions  https://learn.microsoft.com/en-us/dotnet/standard/data/sqli
 # TODO
 
 # Save SaveRange
-
+# DbFunction.PrimitiveSql
 
 
 #region #4 cross database join
@@ -68,4 +72,9 @@ select `t2`.`id`,`t2`.`name`,`t2`.`birth`,`t2`.`fatherId`,`t2`.`motherId`,`t3`.`
 	 left join `User` as t1 on `t0`.`fatherId` = `t1`.`id`
  ) as t2
  left join `User` as t3 on `t2`.`motherId` = `t3`.`id`
-  
+
+
+--------------
+# Done
+
+# support Mysql

+ 48 - 0
src/Vit.Orm.Mysql/Test/Vit.Orm.Mysql.MsTest/DbFunction_Test.cs

@@ -0,0 +1,48 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Vit.Extensions.Linq_Extensions;
+using System.Data;
+
+namespace Vit.Orm.MsTest
+{
+
+    [TestClass]
+    public class DbFunction_Test
+    {
+        [TestMethod]
+        public void Test_DbFunction()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+
+            // select IF(500<1000,true,false)
+            {
+                var query = userQuery.Where(u => DbFunction.Call<bool>("IF", u.fatherId != null, true, false));
+                var sql = query.ToExecuteString();
+                var userList = query.ToList();
+                Assert.AreEqual(3, userList.Count);
+                Assert.AreEqual(3, userList.Last().id);
+            }
+
+            {
+                var query = userQuery.Where(u => u.birth < DbFunction.Call<DateTime>("now"));
+                var sql = query.ToExecuteString();
+                var userList = query.ToList();
+                Assert.AreEqual(6, userList.Count);
+            }
+
+            // coalesce(parameter1,parameter2, …)
+            {
+                var query = userQuery.Where(u => DbFunction.Call<int?>("coalesce", u.fatherId, u.motherId) != null);
+                var sql = query.ToExecuteString();
+                var userList = query.ToList();
+                Assert.AreEqual(3, userList.Count);
+                Assert.AreEqual(1, userList.First().id);
+            }
+
+
+        }
+
+
+    }
+}

+ 16 - 2
src/Vit.Orm.Mysql/Vit.Orm.Mysql/DbContext_Extensions.cs

@@ -10,18 +10,32 @@ namespace Vit.Extensions
 {
     public static class DbContext_Extensions
     {
+        /*
+         // ref: https://dev.mysql.com/doc/refman/8.4/en/savepoint.html
+         //  https://dev.mysql.com/doc/refman/8.4/en/commit.html
+
+        START TRANSACTION;
+            SET autocommit=0;
+            SAVEPOINT tran0;
+                select '';
+            -- ROLLBACK WORK TO SAVEPOINT tran0;
+            RELEASE SAVEPOINT tran0;
+        COMMIT;
+        -- ROLLBACK;
+         */
         public static SqlDbContext UseMysql(this SqlDbContext dbContext, string ConnectionString)
         {
             ISqlTranslateService sqlTranslateService =   Vit.Orm.Mysql.SqlTranslateService.Instance;
 
-            Func<IDbConnection> createDbConnection = () => new MySql.Data.MySqlClient.MySqlConnection(ConnectionString);
+            Func<IDbConnection> createDbConnection = () => new MySqlConnector.MySqlConnection(ConnectionString);
 
             Func<Type, IEntityDescriptor> getEntityDescriptor = (type) => EntityDescriptor.GetEntityDescriptor(type);
 
 
             dbContext.Init(sqlTranslateService: sqlTranslateService, createDbConnection: createDbConnection, getEntityDescriptor: getEntityDescriptor);
 
-            //dbContext.createTransactionScope = (dbContext) => new Vit.Orm.Mysql.SqlTransactionScope(dbContext);
+            dbContext.createTransactionScope = (dbContext) => new Vit.Orm.Mysql.SqlTransactionScope(dbContext);
+            //dbContext.createTransactionScope = (dbContext) => new Vit.Orm.Mysql.SqlTransactionScope_Command(dbContext);
 
             return dbContext;
         }

+ 31 - 28
src/Vit.Orm.Mysql/Vit.Orm.Mysql/SqlTransactionScope.cs

@@ -3,8 +3,8 @@ using System.Data;
 
 using Vit.Orm.Sql;
 using Vit.Orm.Sql.Transaction;
-
-using SqlTransaction = MySql.Data.MySqlClient.MySqlTransaction;
+using Dapper;
+using SqlTransaction = MySqlConnector.MySqlTransaction;
 
 namespace Vit.Orm.Mysql
 {
@@ -26,9 +26,11 @@ namespace Vit.Orm.Mysql
             IDbTransaction originalTransaction = GetCurrentTransaction();
             if (originalTransaction == null)
             {
-                var dbConnection = dbContext.dbConnection;
+                var dbConnection = dbContext.dbConnection as MySqlConnector.MySqlConnection;
                 if (dbConnection.State != ConnectionState.Open) dbConnection.Open();
+
                 originalTransaction = dbConnection.BeginTransaction();
+                dbConnection.Execute("SET autocommit=0;", transaction: originalTransaction);
 
                 transactionWrap = new DbTransactionWrap(originalTransaction);
             }
@@ -41,35 +43,36 @@ namespace Vit.Orm.Mysql
             return transactionWrap;
         }
 
-    }
 
-    public class DbTransactionWrapSavePoint : DbTransactionWrap
-    {
-        public SqlTransaction sqlTran => (SqlTransaction)originalTransaction;
-        string savePoint;
-        public DbTransactionWrapSavePoint(IDbTransaction transaction, string savePoint) : base(transaction)
-        {
-            this.savePoint = savePoint;
-            sqlTran.Save(savePoint);
-        }
 
-        public override void Commit()
+        public class DbTransactionWrapSavePoint : DbTransactionWrap
         {
-            sqlTran.Release(savePoint);
-            TransactionState = ETransactionState.Committed;
-        }
+            public SqlTransaction sqlTran => (SqlTransaction)originalTransaction;
+            string savePointName;
+            public DbTransactionWrapSavePoint(IDbTransaction transaction, string savePointName) : base(transaction)
+            {
+                this.savePointName = savePointName;
+                sqlTran.Save(savePointName);
+            }
 
-        public override void Dispose()
-        {
-            if (TransactionState == ETransactionState.Active)
-                sqlTran.Rollback(savePoint);
-            TransactionState = ETransactionState.Disposed;
-        }
+            public override void Commit()
+            {
+                sqlTran.Release(savePointName);
+                TransactionState = ETransactionState.Committed;
+            }
 
-        public override void Rollback()
-        {
-            sqlTran.Rollback(savePoint);
-            TransactionState = ETransactionState.RolledBack;
+            public override void Dispose()
+            {
+                if (TransactionState == ETransactionState.Active)
+                    sqlTran.Rollback(savePointName);
+                TransactionState = ETransactionState.Disposed;
+            }
+
+            public override void Rollback()
+            {
+                sqlTran.Rollback(savePointName);
+                TransactionState = ETransactionState.RolledBack;
+            }
         }
     }
-}
+}

+ 137 - 0
src/Vit.Orm.Mysql/Vit.Orm.Mysql/SqlTransactionScope_Command.cs

@@ -0,0 +1,137 @@
+using System.Data;
+
+using Vit.Orm.Sql;
+using Vit.Orm.Sql.Transaction;
+using Dapper;
+using System.Collections.Generic;
+using static Vit.Orm.Sql.Transaction.DbTransactionWrap;
+
+
+namespace Vit.Orm.Mysql
+{
+    public class SqlTransactionScope_Command : ITransactionScope
+    {
+        protected SqlDbContext dbContext;
+        protected Stack<DbTransactionWrapSavePoint> savePoints = new();
+        int savePointCount = 0;
+        public SqlTransactionScope_Command(SqlDbContext dbContext)
+        {
+            this.dbContext = dbContext;
+        }
+
+        DbTransactionWrap_Command dbTransactionWrap;
+        public virtual IDbTransaction BeginTransaction()
+        {
+            if (dbTransactionWrap == null)
+            {
+                var dbConnection = dbContext.dbConnection;
+                if (dbConnection.State != ConnectionState.Open) dbConnection.Open();
+
+                dbTransactionWrap = new DbTransactionWrap_Command(dbConnection);
+                return dbTransactionWrap;
+
+            }
+            var savePointName = "tran" + savePointCount++;
+            var savePoint = dbTransactionWrap.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)
+                {
+                    transaction?.Dispose();
+                }
+            }
+            savePoints = null;
+
+            dbTransactionWrap?.Dispose();
+            dbTransactionWrap = null;
+        }
+
+
+
+        public class DbTransactionWrap_Command : IDbTransaction
+        {
+            public virtual System.Data.IsolationLevel IsolationLevel => default;
+            public IDbConnection Connection { get; protected set; }
+
+            public virtual ETransactionState TransactionState { get; protected set; } = ETransactionState.Active;
+
+            public DbTransactionWrap_Command(IDbConnection connection)
+            {
+                this.Connection = connection;
+                Connection.Execute($"START TRANSACTION; SET autocommit=0;");
+            }
+
+            public void Commit()
+            {
+                Connection.Execute($"COMMIT;");
+                TransactionState = ETransactionState.Committed;
+            }
+
+            public void Dispose()
+            {
+                if (TransactionState == ETransactionState.Active)
+                {
+                    Connection.Execute($"ROLLBACK;");
+                }
+                TransactionState = ETransactionState.Disposed;
+            }
+
+            public void Rollback()
+            {
+                Connection.Execute($"ROLLBACK;");
+                TransactionState = ETransactionState.RolledBack;
+            }
+            public DbTransactionWrapSavePoint BeginSavePoint(string savePoint)
+            {
+                return new DbTransactionWrapSavePoint(Connection, savePoint);
+            }
+        }
+
+        public class DbTransactionWrapSavePoint : IDbTransaction
+        {
+            public virtual System.Data.IsolationLevel IsolationLevel => default;
+            public IDbConnection Connection { get; protected set; }
+
+            public virtual ETransactionState TransactionState { get; protected set; } = ETransactionState.Active;
+            protected string savePointName;
+
+            public DbTransactionWrapSavePoint(IDbConnection connection, string savePointName)
+            {
+                this.Connection = connection;
+                this.savePointName = savePointName;
+                Connection.Execute($"SAVEPOINT {savePointName};");
+            }
+
+            public void Commit()
+            {
+                Connection.Execute($"RELEASE SAVEPOINT {savePointName};");
+                TransactionState = ETransactionState.Committed;
+            }
+
+            public void Dispose()
+            {
+                if (TransactionState == ETransactionState.Active)
+                {
+                    Connection.Execute($"ROLLBACK WORK TO SAVEPOINT {savePointName};");
+                }
+                TransactionState = ETransactionState.Disposed;
+            }
+
+            public void Rollback()
+            {
+                Connection.Execute($"ROLLBACK WORK TO SAVEPOINT {savePointName};");
+                TransactionState = ETransactionState.RolledBack;
+            }
+        }
+    }
+}

+ 11 - 5
src/Vit.Orm.Mysql/Vit.Orm.Mysql/SqlTranslateService.cs

@@ -67,7 +67,7 @@ namespace Vit.Orm.Mysql
                             // ##1 ToString
                             case nameof(object.ToString):
                                 {
-                                    return $"cast({EvalExpression(arg, methodCall.@object)} as varchar(1000))";
+                                    return $"cast({EvalExpression(arg, methodCall.@object)} as char)";
                                 }
 
                             #region ##2 String method:  StartsWith EndsWith Contains
@@ -75,19 +75,19 @@ namespace Vit.Orm.Mysql
                                 {
                                     var str = methodCall.@object;
                                     var value = methodCall.arguments[0];
-                                    return $"{EvalExpression(arg, str)} like {EvalExpression(arg, value)}+'%'";
+                                    return $"{EvalExpression(arg, str)} like concat({EvalExpression(arg, value)},'%')";
                                 }
                             case nameof(string.EndsWith): // String.EndsWith
                                 {
                                     var str = methodCall.@object;
                                     var value = methodCall.arguments[0];
-                                    return $"{EvalExpression(arg, str)} like '%'+{EvalExpression(arg, value)}";
+                                    return $"{EvalExpression(arg, str)} like concat('%',{EvalExpression(arg, value)})";
                                 }
                             case nameof(string.Contains) when methodCall.methodCall_typeName == "String": // String.Contains
                                 {
                                     var str = methodCall.@object;
                                     var value = methodCall.arguments[0];
-                                    return $"{EvalExpression(arg, str)} like '%'+{EvalExpression(arg, value)}+'%'";
+                                    return $"{EvalExpression(arg, str)} like concat('%',{EvalExpression(arg, value)},'%')";
                                 }
                                 #endregion
                         }
@@ -117,6 +117,12 @@ namespace Vit.Orm.Mysql
 
                             if (targetDbType == GetDbType(sourceType)) return EvalExpression(arg, convert.body);
                         }
+
+                        if (targetType == typeof(string))
+                        {
+                            return $"cast({EvalExpression(arg, convert.body)} as char)";
+                        }
+
                         return $"cast({EvalExpression(arg, convert.body)} as {targetDbType})";
                     }
                 case nameof(ExpressionType.Add):
@@ -219,7 +225,7 @@ CREATE TABLE {DelimitIdentifier(entityDescriptor.tableName)} (
               */
             var entityDescriptor = arg.entityDescriptor;
 
-            var columns = entityDescriptor.allColumns;
+            var columns = entityDescriptor.columns;
 
             // #1 GetSqlParams 
             Func<object, Dictionary<string, object>> GetSqlParams = (entity) =>

+ 11 - 12
src/Vit.Orm.Mysql/Vit.Orm.Mysql/TranslateService/ExecuteUpdateTranslateService.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using System.Xml.Linq;
 
 using Vit.Linq.ExpressionTree.CollectionsQuery;
 using Vit.Orm.Sql.SqlTranslate;
@@ -13,15 +14,14 @@ namespace Vit.Orm.Mysql.TranslateService
 
 -- multiple
 WITH tmp AS (
-    select  ('u' + cast(u.id as varchar(max)) + '_' + COALESCE(cast(father.id as varchar(max)),'') ) as name , u.id 
-    from [User] u
-    left join [User] father on u.fatherId = father.id 
+    select  concat('u' , cast(u.id as char) , '_' , COALESCE(cast(father.id as char),'') ) as name , u.id 
+    from `User` u
+    left join `User` father on u.fatherId = father.id 
     where u.id > 0
 )
-UPDATE [User]
-  SET name =  tmp.name
-  from [User] t0
-  inner join tmp on t0.id=tmp.id ;
+UPDATE `User` t0,tmp
+  SET t0.name =  tmp.name
+where t0.id = tmp.id ;
          */
         public override string BuildQuery(QueryTranslateArgument arg, CombinedStream stream)
         {
@@ -39,21 +39,20 @@ UPDATE [User]
             sql += sqlInner;
 
             sql += $"{NewLine}){NewLine}";
-            sql += $"UPDATE {sqlTranslator.DelimitIdentifier(tableName)}{NewLine}";
+            sql += $"UPDATE {sqlTranslator.DelimitIdentifier(tableName)} t0, tmp{NewLine}";
             sql += $"Set ";
 
             var sqlToUpdateCols = columnsToUpdate
                 .Select(m => m.name)
-                .Select(name => $"{NewLine}  {sqlTranslator.DelimitIdentifier(name)} = {sqlTranslator.GetSqlField("tmp", name)} ");
+                .Select(name => $"{NewLine}  {sqlTranslator.GetSqlField("t0", name)} = {sqlTranslator.GetSqlField("tmp", name)} ");
 
             sql += string.Join(",", sqlToUpdateCols);
 
-            sql += $"{NewLine}from {sqlTranslator.DelimitIdentifier(tableName)} t0";
-            sql += $"{NewLine}inner join tmp on t0.{sqlTranslator.DelimitIdentifier(keyName)}=tmp.{sqlTranslator.DelimitIdentifier(keyName)}";
+            sql += $"{NewLine}where {sqlTranslator.GetSqlField("t0", keyName)}={sqlTranslator.GetSqlField("tmp", keyName)} ";
 
             return sql;
         }
- 
+
 
         public ExecuteUpdateTranslateService(SqlTranslateService sqlTranslator) : base(sqlTranslator)
         {

+ 1 - 4
src/Vit.Orm.Mysql/Vit.Orm.Mysql/Vit.Orm.Mysql.csproj

@@ -10,12 +10,9 @@
         <LangVersion>9.0</LangVersion>
     </PropertyGroup>
 
-    <ItemGroup>
-      <Compile Remove="SqlTransactionScope.cs" />
-    </ItemGroup>
 
     <ItemGroup>
-        <PackageReference Include="MySql.Data" Version="8.4.0" />
+        <PackageReference Include="MySqlConnector" Version="2.3.7" />
     </ItemGroup>
 
     <ItemGroup>

+ 7 - 7
src/Vit.Orm.SqlServer/Vit.Orm.SqlServer/SqlTransactionScope.cs

@@ -46,31 +46,31 @@ namespace Vit.Orm.SqlServer
     public class DbTransactionWrapSavePoint : DbTransactionWrap
     {
         public SqlTransaction sqlTran => (SqlTransaction)originalTransaction;
-        string savePoint;
-        public DbTransactionWrapSavePoint(IDbTransaction transaction, string savePoint) : base(transaction)
+        string savePointName;
+        public DbTransactionWrapSavePoint(IDbTransaction transaction, string savePointName) : base(transaction)
         {
-            this.savePoint = savePoint;
-            sqlTran.Save(savePoint);
+            this.savePointName = savePointName;
+            sqlTran.Save(savePointName);
         }
 
         public override void Commit()
         {
             // no need to commit savepoint for sqlserver, ref: https://learn.microsoft.com/en-us/dotnet/api/microsoft.data.sqlclient.sqltransaction.save
 
-            //sqlTran.Commit(savePoint);
+            //sqlTran.Commit(savePointName);
             TransactionState = ETransactionState.Committed;
         }
 
         public override void Dispose()
         {
             if (TransactionState == ETransactionState.Active)
-                sqlTran.Rollback(savePoint);
+                sqlTran.Rollback(savePointName);
             TransactionState = ETransactionState.Disposed;
         }
 
         public override void Rollback()
         {
-            sqlTran.Rollback(savePoint);
+            sqlTran.Rollback(savePointName);
             TransactionState = ETransactionState.RolledBack;
         }
     }

+ 4 - 8
src/Vit.Orm.Sqlite/Test/Vit.Orm.Sqlite.MsTest/CommonTest/Query_Group_Test.cs

@@ -26,10 +26,8 @@ namespace Vit.Orm.MsTest.CommonTest
                 var rows = query.ToList();
 
                 Assert.AreEqual(3, rows.Count);
-                Assert.AreEqual(4, rows[1].fatherId);
-                Assert.AreEqual(6, rows[1].motherId);
-                Assert.AreEqual(5, rows[2].fatherId);
-                Assert.AreEqual(6, rows[2].motherId);
+                Assert.AreEqual(0, rows.Select(u => u.fatherId).Except(new int?[] { 4, 5, null }).Count());
+                Assert.AreEqual(0, rows.Select(u => u.motherId).Except(new int?[] { 6, null }).Count());
             }
 
             // Lambda Expression
@@ -48,10 +46,8 @@ namespace Vit.Orm.MsTest.CommonTest
                 var rows = query.ToList();
 
                 Assert.AreEqual(3, rows.Count);
-                Assert.AreEqual(4, rows[1].fatherId);
-                Assert.AreEqual(6, rows[1].motherId);
-                Assert.AreEqual(5, rows[2].fatherId);
-                Assert.AreEqual(6, rows[2].motherId);
+                Assert.AreEqual(0, rows.Select(u => u.fatherId).Except(new int?[] { 4, 5, null }).Count());
+                Assert.AreEqual(0, rows.Select(u => u.motherId).Except(new int?[] { 6, null }).Count());
             }
         }
 

+ 7 - 7
src/Vit.Orm.Sqlite/Vit.Orm.Sqlite/SqlTransactionScope.cs

@@ -46,29 +46,29 @@ namespace Vit.Orm.Sqlite
     public class DbTransactionWrapSavePoint : DbTransactionWrap
     {
         public SqlTransaction sqlTran => (SqlTransaction)originalTransaction;
-        string savePoint;
-        public DbTransactionWrapSavePoint(IDbTransaction transaction, string savePoint) : base(transaction)
+        string savePointName;
+        public DbTransactionWrapSavePoint(IDbTransaction transaction, string savePointName) : base(transaction)
         {
-            this.savePoint = savePoint;
-            sqlTran.Save(savePoint);
+            this.savePointName = savePointName;
+            sqlTran.Save(savePointName);
         }
 
         public override void Commit()
         {
-            sqlTran.Release(savePoint);
+            sqlTran.Release(savePointName);
             TransactionState = ETransactionState.Committed;
         }
 
         public override void Dispose()
         {
             if (TransactionState == ETransactionState.Active)
-                sqlTran.Rollback(savePoint);
+                sqlTran.Rollback(savePointName);
             TransactionState = ETransactionState.Disposed;
         }
 
         public override void Rollback()
         {
-            sqlTran.Rollback(savePoint);
+            sqlTran.Rollback(savePointName);
             TransactionState = ETransactionState.RolledBack;
         }
     }