瀏覽代碼

[Vitorm.Data] auto load entityLoader from appsettings.json, fixes #6

Lith 8 月之前
父節點
當前提交
0a4b063746

+ 6 - 6
Publish/DevOps3/environment/build-bash__10.Test__#1.InitEnv.sh

@@ -13,9 +13,9 @@ export basePath=/root/temp
 
 #---------------------------------------------------------------------
 echo '#build-bash__10.Test__#1.InitEnv.sh -> #1 start MySql container'
-docker rm orm-mysql -f || true
+docker rm vitorm-mysql -f || true
 docker run -d \
---name orm-mysql \
+--name vitorm-mysql \
 -p 3306:3306 \
 -e MYSQL_DATABASE=db_orm \
 -e MYSQL_ROOT_PASSWORD=123456 \
@@ -25,9 +25,9 @@ mysql:8.0.26
 
 #---------------------------------------------------------------------
 echo '#build-bash__10.Test__#1.InitEnv.sh -> #2 start SqlServer container'
-docker rm orm-mssql -f || true
+docker rm vitorm-sqlserver -f || true
 docker run -d \
---name orm-mssql \
+--name vitorm-sqlserver \
 -p 1433:1433 \
 -e 'ACCEPT_EULA=Y' \
 -e 'SA_PASSWORD=Admin0123' \
@@ -49,11 +49,11 @@ echo '#build-bash__10.Test__#1.InitEnv.sh -> #8 wait for containers to init'
 
 
 echo '#build-bash__10.Test__#1.InitEnv.sh -> #8.1 wait for MySql to init' 
-docker run -t --rm --link orm-mysql mysql:8.0.26 timeout 120 sh -c 'until mysql -h orm-mysql -u root -p123456 -e "SELECT 1"; do echo waiting for mysql; sleep 2; done;  mysql -h orm-mysql --database=db_orm -u root -p123456 -e "create database if not exists db_orm2;create schema if not exists orm;";    '
+docker run -t --rm --link vitorm-mysql mysql:8.0.26 timeout 120 sh -c 'until mysql -h vitorm-mysql -u root -p123456 -e "SELECT 1"; do echo waiting for mysql; sleep 2; done;  mysql -h vitorm-mysql --database=db_orm -u root -p123456 -e "create database if not exists db_orm2;create schema if not exists orm;";    '
 
 
 echo '#build-bash__10.Test__#1.InitEnv.sh -> #8.2 wait for SqlServer to init' 
-docker run -t --rm --link orm-mssql mcr.microsoft.com/mssql/server:2019-CU12-ubuntu-20.04 timeout 120 sh -c 'until /opt/mssql-tools/bin/sqlcmd -S "orm-mssql" -U SA -P "Admin0123" -Q "SELECT 1"; do echo waiting for mysql; sleep 2; done;  /opt/mssql-tools/bin/sqlcmd -S "orm-mssql" -U SA -P "Admin0123" -Q "CREATE DATABASE db_orm";  /opt/mssql-tools/bin/sqlcmd -S "orm-mssql" -d "db_orm" -U SA -P "Admin0123" -Q "CREATE Schema orm";  /opt/mssql-tools/bin/sqlcmd -S "orm-mssql" -U SA -P "Admin0123" -Q "CREATE DATABASE db_orm2";    '
+docker run -t --rm --link vitorm-sqlserver mcr.microsoft.com/mssql/server:2019-CU12-ubuntu-20.04 timeout 120 sh -c 'until /opt/mssql-tools/bin/sqlcmd -S "vitorm-sqlserver" -U SA -P "Admin0123" -Q "SELECT 1"; do echo waiting for mysql; sleep 2; done;  /opt/mssql-tools/bin/sqlcmd -S "vitorm-sqlserver" -U SA -P "Admin0123" -Q "CREATE DATABASE db_orm";  /opt/mssql-tools/bin/sqlcmd -S "vitorm-sqlserver" -d "db_orm" -U SA -P "Admin0123" -Q "CREATE Schema orm";  /opt/mssql-tools/bin/sqlcmd -S "vitorm-sqlserver" -U SA -P "Admin0123" -Q "CREATE DATABASE db_orm2";    '
 
 
 #---------------------------------------------------------------------

+ 2 - 2
Publish/DevOps3/environment/build-bash__10.Test__#3.CleanEnv.sh

@@ -16,8 +16,8 @@ echo '#build-bash__10.Test_#3.CleanEnv.sh'
 
 
 echo '#build-bash__10.Test_#3.CleanEnv.sh -> #1 remove MySql'
-docker rm orm-mysql -f || true
+docker rm vitorm-mysql -f || true
 
 
 echo '#build-bash__10.Test_#3.CleanEnv.sh -> #2 remove SqlServer'
-docker rm orm-mssql -f || true
+docker rm vitorm-sqlserver -f || true

+ 1 - 0
doc/ReleaseLog.md

@@ -6,6 +6,7 @@
 - #4 support schema name of table for MySql
 - #5 single bool value in where condition
 - [Vitorm.Data] support name and multiple namespace
+- #6 [Vitorm.Data] auto load entityLoader from appsettings.json
 
 -----------------------
 # 2.0.5

+ 29 - 3
src/Vitorm.Data/Data.cs

@@ -9,6 +9,7 @@ using Vit.Core.Util.Reflection;
 using Vit.Linq;
 
 using Vitorm.DataProvider;
+using Vitorm.Entity;
 
 namespace Vitorm
 {
@@ -17,10 +18,35 @@ namespace Vitorm
 
         static Data()
         {
-            var dataSourceConfigs = Appsettings.json.GetByPath<List<Dictionary<string, object>>>("Vitorm.Data");
-            var dataProviders = dataSourceConfigs?.Select(CreateDataProvider).NotNull().ToList();
+            #region #1 load dataProvider
+            {
+                var dataSourceConfigs = Appsettings.json.GetByPath<List<Dictionary<string, object>>>("Vitorm.Data");
+                var dataProviders = dataSourceConfigs?.Select(CreateDataProvider).NotNull().ToList();
+
+                if (dataProviders?.Any() == true) providerCache.AddRange(dataProviders);
+            }
+            #endregion
 
-            if (dataProviders?.Any() == true) providerCache.AddRange(dataProviders);
+
+            #region #2 load entityLoader from appsettings.json
+            {
+                var configs = Appsettings.json.GetByPath<List<Dictionary<string, object>>>("Vitorm.EntityLoader");
+                configs?.ForEach(config =>
+                {
+                    object temp;
+                    string className = config.TryGetValue("className", out temp) ? temp as string : null;
+                    string assemblyFile = config.TryGetValue("assemblyFile", out temp) ? temp as string : null;
+                    string assemblyName = config.TryGetValue("assemblyName", out temp) ? temp as string : null;
+
+                    int index = config.TryGetValue("index", out temp) && temp is int i ? i : 0;
+
+                    var entityLoader = ObjectLoader.CreateInstance(className, assemblyFile: assemblyFile, assemblyName: assemblyName) as IEntityLoader;
+                    if (entityLoader == null) return;
+
+                    EntityLoaders.Instance.loaders.Insert(index, entityLoader);
+                });
+            }
+            #endregion
         }
 
         public static bool AddDataSource(Dictionary<string, object> dataSourceConfig)

+ 2 - 1
src/Vitorm.Sqlite/SqlTranslateService.cs

@@ -168,7 +168,8 @@ CREATE TABLE IF NOT EXISTS "User" (
             List<string> sqlFields = new();
 
             // #1 primary key
-            sqlFields.Add(GetColumnSql(entityDescriptor.key));
+            if (entityDescriptor.key != null)
+                sqlFields.Add(GetColumnSql(entityDescriptor.key));
 
             // #2 columns
             entityDescriptor.columns?.ForEach(column => sqlFields.Add(GetColumnSql(column)));

+ 1 - 3
src/Vitorm/DbContext.cs

@@ -64,9 +64,7 @@ namespace Vitorm
 
         #region EntityLoader
 
-        public static EntityLoaders defaultEntityLoader = new();
-
-        public IEntityLoader entityLoader = defaultEntityLoader;
+        public IEntityLoader entityLoader = EntityLoaders.Instance;
         public virtual IEntityDescriptor GetEntityDescriptor(Type entityType, bool tryFromCache = true)
         {
             if (tryFromCache && dbSetMap?.TryGetValue(entityType, out var dbSet) == true) return dbSet.entityDescriptor;

+ 3 - 0
src/Vitorm/Entity/EntityLoaders.cs

@@ -6,6 +6,9 @@ namespace Vitorm.Entity
 {
     public class EntityLoaders : IEntityLoader
     {
+        public readonly static EntityLoaders Instance = new();
+
+
         public List<IEntityLoader> loaders = new();
         public EntityLoaders()
         {

+ 1 - 1
src/Vitorm/Entity/Loader/DataAnnotations/EntityLoader.cs

@@ -98,7 +98,7 @@ namespace Vitorm.Entity.Loader.DataAnnotations
                         if (type == typeof(string)) isNullable = true;
                         else
                         {
-                            isNullable = (type.IsGenericType && typeof(Nullable<>) == type.GetGenericTypeDefinition());
+                            isNullable = TypeUtil.IsNullable(type);
                         }
                     }
 

+ 1 - 1
src/Vitorm/Sql/SqlExecutor.cs

@@ -6,7 +6,7 @@ namespace Vitorm.Sql
 {
     public partial class SqlExecutor
     {
-        public readonly static SqlExecutor Instance = new 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 - 1
test/Vitorm.Data.MsTest/DataAccess_Test.cs → test/Vitorm.Data.MsTest/CustomTest/DataProvider_Test.cs

@@ -7,7 +7,7 @@ namespace Vitorm.MsTest
 {
 
     [TestClass]
-    public partial class DataAccess_Test
+    public partial class DataProvider_Test
     {
 
         [TestMethod]

+ 105 - 0
test/Vitorm.Data.MsTest/CustomTest/EntityLoader_CustomLoader_Test.cs

@@ -0,0 +1,105 @@
+using System.Reflection;
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+using Vitorm.Entity;
+using Vitorm.Entity.Loader.DataAnnotations;
+using Vitorm.MsTest.CommonTest;
+using Vitorm.MsTest.Sqlite;
+
+namespace Vitorm.MsTest.Sqlite
+{
+    [MyTable(table = "CustomUser2")]
+    public class CustomUser
+    {
+        [MyKey]
+        public int id { get; set; }
+        public string name { get; set; }
+    }
+}
+
+
+namespace Vitorm.MsTest.CommonTest
+{
+    [TestClass]
+    public class EntityLoader_CustomLoader_Test
+    {
+
+        [TestMethod]
+        public void Test_EntityLoader()
+        {
+            {
+                var name = Guid.NewGuid().ToString();
+                Data.TryDropTable<CustomUser>();
+                Data.TryCreateTable<CustomUser>();
+                Data.Add(new CustomUser { id = 1, name = name });
+
+                Assert.AreEqual(name, Data.Get<CustomUser>(1).name);
+            }
+        }
+    }
+
+
+    #region Custom EntityLoader Attribute
+    public class MyTableAttribute : Attribute
+    {
+        public string table { get; set; }
+    }
+    public class MyKeyAttribute : Attribute { }
+
+
+    public class CustomEntityLoader : IEntityLoader
+    {
+        public void CleanCache() { }
+        public (bool success, IEntityDescriptor entityDescriptor) LoadDescriptor(Type entityType) => LoadFromType(entityType);
+        public (bool success, IEntityDescriptor entityDescriptor) LoadDescriptorWithoutCache(Type entityType) => LoadFromType(entityType);
+
+        public static bool GetTableName(Type entityType, out string tableName, out string schema)
+        {
+            tableName = entityType?.GetCustomAttribute<MyTableAttribute>()?.table;
+            schema = null;
+            return tableName != null;
+        }
+
+        public static (bool success, IEntityDescriptor EntityDescriptor) LoadFromType(Type entityType)
+        {
+            if (!GetTableName(entityType, out var tableName, out var schema)) return default;
+
+            IColumnDescriptor[] allColumns = entityType?.GetProperties(BindingFlags.Public | BindingFlags.Instance)
+                .Select(propertyInfo =>
+                {
+                    // #1 isKey
+                    bool isKey = propertyInfo?.GetCustomAttribute<MyKeyAttribute>() != null;
+
+                    // #2 column name and type
+                    string columnName = propertyInfo.Name;
+                    string columnDbType = null;
+                    int? columnOrder = null;
+
+                    // #3 isIdentity
+                    var isIdentity = false;
+
+                    // #4 isNullable
+                    bool isNullable;
+                    {
+                        var type = propertyInfo.PropertyType;
+                        if (type == typeof(string)) isNullable = true;
+                        else
+                        {
+                            isNullable = TypeUtil.IsNullable(type);
+                        }
+                    }
+
+                    return new ColumnDescriptor(
+                        propertyInfo, columnName: columnName,
+                        isKey: isKey, isIdentity: isIdentity, isNullable: isNullable,
+                        columnDbType: columnDbType,
+                        columnOrder: columnOrder
+                        );
+                }).Where(column => column != null).ToArray();
+
+            return (true, new EntityDescriptor(entityType, allColumns, tableName, schema));
+        }
+    }
+    #endregion
+}

+ 15 - 0
test/Vitorm.Data.MsTest/appsettings.json

@@ -45,6 +45,21 @@
         "connectionString": "data source=Sqlite4.db;"
       }
 
+    ],
+
+    "EntityLoader": [
+      {
+        /* [optional] EntityLoader index, same as priority, default value: 0 */
+        "//index": 0,
+        /* EntityLoader class name, must implement interface Vitorm.Entity.IEntityLoader */
+        "className": "Vitorm.MsTest.CommonTest.CustomEntityLoader",
+        /* [optional] from which assembly to load EntityLoader class */
+        "//assemblyName": "Vitorm.Data.MsTest",
+        /* [optional] from where to load EntityLoader class */
+        "//assemblyFile": "Vitorm.Data.MsTest.dll"
+      }
     ]
+
+
   }
 }

+ 3 - 3
test/Vitorm.Sqlite.MsTest/CommonTest/EntityLoader_CustomLoader_Test.cs

@@ -8,7 +8,7 @@ using Vitorm.Entity.Loader.DataAnnotations;
 namespace Vitorm.MsTest.CommonTest
 {
     [TestClass]
-    public class EntityLoader_CustomLoador_Test
+    public class EntityLoader_CustomLoader_Test
     {
         [TestMethod]
         public void Test_EntityDescriptor()
@@ -36,7 +36,7 @@ namespace Vitorm.MsTest.CommonTest
 
             // #2 defaultEntityLoader
             {
-                DbContext.defaultEntityLoader.loaders.Insert(0, new CustomEntityLoaderAttribute());
+                EntityLoaders.Instance.loaders.Insert(0, new CustomEntityLoaderAttribute());
 
                 var users = dbContext.Query<CustomUser>().Where(m => m.name == "u146").ToList();
                 Assert.AreEqual(1, users.Count());
@@ -149,7 +149,7 @@ namespace Vitorm.MsTest.CommonTest
                             if (type == typeof(string)) isNullable = true;
                             else
                             {
-                                isNullable = (type.IsGenericType && typeof(Nullable<>) == type.GetGenericTypeDefinition());
+                                isNullable = TypeUtil.IsNullable(type);
                             }
                         }
 

+ 88 - 0
test/Vitorm.Sqlite.MsTest/CommonTest/Property_Bool_Test.cs

@@ -0,0 +1,88 @@
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using System.Data;
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace Vitorm.MsTest.CommonTest
+{
+
+    [TestClass]
+    public class Property_Bool_Test
+    {
+        [TestMethod]
+        public void Test()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+
+            // #1 Init
+            var name = Guid.NewGuid().ToString();
+            var dbSet = dbContext.DbSet<MyUser>();
+            dbSet.TryDropTable();
+            dbSet.TryCreateTable();
+            dbSet.Add(new MyUser { id = 1, enable = true, isEven = null });
+            dbSet.Add(new MyUser { id = 2, enable = true, isEven = true });
+            dbSet.Add(new MyUser { id = 3, enable = false, isEven = false });
+
+            // #2 Assert
+            {
+                var user = dbSet.Query().Where(m => m.enable == true).OrderBy(m => m.id).First();
+                Assert.AreEqual(1, user.id);
+            }
+            {
+                var user = dbSet.Query().Where(m => m.enable == false).OrderBy(m => m.id).First();
+                Assert.AreEqual(3, user.id);
+            }
+            {
+                var user = dbSet.Query().Where(m => m.enable).OrderBy(m => m.id).First();
+                Assert.AreEqual(1, user.id);
+            }
+            {
+                var user = dbSet.Query().Where(m => !m.enable).OrderBy(m => m.id).First();
+                Assert.AreEqual(3, user.id);
+            }
+            {
+                var user = dbSet.Query().Where(m => m.isEven == null).OrderBy(m => m.id).First();
+                Assert.AreEqual(1, user.id);
+            }
+            {
+                var user = dbSet.Query().Where(m => m.isEven == true).OrderBy(m => m.id).First();
+                Assert.AreEqual(2, user.id);
+            }
+            {
+                var user = dbSet.Query().Where(m => m.isEven == false).OrderBy(m => m.id).First();
+                Assert.AreEqual(3, user.id);
+            }
+            {
+                var user = dbSet.Query().Where(m => m.isEven.Value).OrderBy(m => m.id).First();
+                Assert.AreEqual(2, user.id);
+            }
+            {
+                var user = dbSet.Query().Where(m => !m.isEven.Value).OrderBy(m => m.id).First();
+                Assert.AreEqual(3, user.id);
+            }
+            {
+                var query = dbSet.Query().Where(m => m.isEven.Value).OrderBy(m => m.isEven).Select(m => new { m.id, m.isEven });
+                var sql = query.ToExecuteString();
+                var users = query.ToList();
+                var user = users.First();
+                Assert.AreEqual(2, user.id);
+                Assert.AreEqual(true, user.isEven);
+            }
+        }
+
+
+
+        // Entity
+        [Table("MyUser")]
+        public class MyUser
+        {
+            [Key]
+            public int id { get; set; }
+            public bool? isEven { get; set; }
+            public bool enable { get; set; }
+        }
+
+
+    }
+}