Forráskód Böngészése

Merge pull request #19 from LithWang/master

2.3.0
Lith 5 hónapja
szülő
commit
eaafa2660a
28 módosított fájl, 414 hozzáadás és 97 törlés
  1. 6 0
      doc/ReleaseNotes.md
  2. 1 1
      doc/TODO.md
  3. 1 1
      src/Versions.props
  4. 5 5
      src/Vitorm.MySql/SqlTranslateService.cs
  5. 6 6
      src/Vitorm.SqlServer/SqlTranslateService.cs
  6. 5 5
      src/Vitorm.Sqlite/SqlTranslateService.cs
  7. 45 15
      src/Vitorm/DbContext.Event.cs
  8. 6 4
      src/Vitorm/Entity/EntityDescriptorWithAlias.cs
  9. 1 1
      src/Vitorm/Entity/Extensions/IEntityDescriptor_Extensions.Column.cs
  10. 6 3
      src/Vitorm/Entity/IEntityDescriptor.cs
  11. 15 3
      src/Vitorm/Entity/IPropertyDescriptor.cs
  12. 14 9
      src/Vitorm/Entity/Loader/DataAnnotations/EntityDescriptor.cs
  13. 50 10
      src/Vitorm/Entity/Loader/DataAnnotations/EntityLoader.cs
  14. 10 5
      src/Vitorm/Entity/Loader/DataAnnotations/PropertyDescriptor.cs
  15. 12 0
      src/Vitorm/Entity/PropertyType/ETypeMode.cs
  16. 43 0
      src/Vitorm/Entity/PropertyType/IPropertyType.cs
  17. 85 0
      src/Vitorm/Entity/PropertyType/PropertyType.cs
  18. 2 2
      src/Vitorm/Sql/DataReader/EntityReader/CompiledLambda/EntityPropertyReader.cs
  19. 2 2
      src/Vitorm/Sql/DataReader/EntityReader/CompiledLambda/ModelReader.cs
  20. 2 2
      src/Vitorm/Sql/DataReader/EntityReader/EntityConstructor/EntityReader_.cs
  21. 2 2
      src/Vitorm/Sql/DataReader/SqlColumns.cs
  22. 1 1
      src/Vitorm/Sql/SqlDbSet.Async.cs
  23. 1 1
      src/Vitorm/Sql/SqlDbSet.cs
  24. 8 8
      src/Vitorm/Sql/SqlTranslate/SqlTranslateService.cs
  25. 21 1
      src/Vitorm/TypeUtil.cs
  26. 10 5
      test/Vitorm.Data.MsTest/CustomTest/EntityLoader_CustomLoader_Test.cs
  27. 10 5
      test/Vitorm.Sqlite.MsTest/CommonTest/EntityLoader_CustomLoader_Test.cs
  28. 44 0
      test/Vitorm.Sqlite.MsTest/CommonTest/Query_Where_In_Test.cs

+ 6 - 0
doc/ReleaseNotes.md

@@ -1,5 +1,11 @@
 # Vitorm ReleaseNotes
 
+-----------------------
+# 2.3.0
+
+- [Vitorm] support nested entity and rename Column to Property
+- [Vitorm] IEntityDescriptor, rename properties/allProperties to propertiesWithoutKey/properties
+- [Vitorm] DbContext, change method Event_OnExecuting to virtual instance from static extension, and support Laze<executeString>
 
 -----------------------
 # 2.2.0

+ 1 - 1
doc/TODO.md

@@ -2,7 +2,7 @@
 
 
 # group then orderBy aggregate column
-> [QueryTranslator] not suported MethodCall: Sum
+> [QueryTranslator] not supported MethodCall: Sum
 ``` csharp
 using var dbContext = DataSource.CreateDbContext();
 var userQuery = dbContext.Query<User>();

+ 1 - 1
src/Versions.props

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

+ 5 - 5
src/Vitorm.MySql/SqlTranslateService.cs

@@ -189,14 +189,14 @@ CREATE TABLE IF NOT EXISTS `User` (
                 sqlFields.Add(GetColumnSql(entityDescriptor.key));
 
             // #2 columns
-            entityDescriptor.columns?.ForEach(column => sqlFields.Add(GetColumnSql(column)));
+            entityDescriptor.propertiesWithoutKey?.ForEach(column => sqlFields.Add(GetColumnSql(column)));
 
             return $@"
 CREATE TABLE IF NOT EXISTS {DelimitTableName(entityDescriptor)} (
   {string.Join(",\r\n  ", sqlFields)}
 )";
 
-            string GetColumnSql(IColumnDescriptor column)
+            string GetColumnSql(IPropertyDescriptor column)
             {
                 var isNullable = !column.isKey && column.isNullable;
                 var columnDbType = column.columnDbType ?? GetColumnDbType(column);
@@ -236,7 +236,7 @@ CREATE TABLE IF NOT EXISTS {DelimitTableName(entityDescriptor)} (
             [typeof(Guid)] = "binary(16)",
 
         };
-        protected override string GetColumnDbType(IColumnDescriptor column)
+        protected override string GetColumnDbType(IPropertyDescriptor column)
         {
             Type type = column.type;
 
@@ -280,7 +280,7 @@ CREATE TABLE IF NOT EXISTS {DelimitTableName(entityDescriptor)} (
                 // insert into user(name,fatherId,motherId) values('',0,0); select last_insert_id();
 
                 var entityDescriptor = arg.entityDescriptor;
-                var (columnNames, sqlColumnParams, GetSqlParams) = PrepareAdd_Columns(arg, entityDescriptor.columns);
+                var (columnNames, sqlColumnParams, GetSqlParams) = PrepareAdd_Columns(arg, entityDescriptor.propertiesWithoutKey);
                 string sql = $@"insert into {DelimitTableName(entityDescriptor)}({string.Join(",", columnNames)}) values({string.Join(",", sqlColumnParams)});";
 
                 // get generated id
@@ -292,7 +292,7 @@ CREATE TABLE IF NOT EXISTS {DelimitTableName(entityDescriptor)} (
                 // insert into user(name,fatherId,motherId) values('',0,0);
 
                 var entityDescriptor = arg.entityDescriptor;
-                var (columnNames, sqlColumnParams, GetSqlParams) = PrepareAdd_Columns(arg, entityDescriptor.allColumns);
+                var (columnNames, sqlColumnParams, GetSqlParams) = PrepareAdd_Columns(arg, entityDescriptor.properties);
                 string sql = $@"insert into {DelimitTableName(entityDescriptor)}({string.Join(",", columnNames)}) values({string.Join(",", sqlColumnParams)});";
                 return (sql, GetSqlParams);
             }

+ 6 - 6
src/Vitorm.SqlServer/SqlTranslateService.cs

@@ -229,7 +229,7 @@ if object_id(N'[dbo].[User]', N'U') is null
                 sqlFields.Add(GetColumnSql(entityDescriptor.key));
 
             // #2 columns
-            entityDescriptor.columns?.ForEach(column => sqlFields.Add(GetColumnSql(column)));
+            entityDescriptor.propertiesWithoutKey?.ForEach(column => sqlFields.Add(GetColumnSql(column)));
 
             return $@"
 if object_id(N'{DelimitTableName(entityDescriptor)}', N'U') is null
@@ -238,7 +238,7 @@ create table {DelimitTableName(entityDescriptor)} (
 )";
 
 
-            string GetColumnSql(IColumnDescriptor column)
+            string GetColumnSql(IPropertyDescriptor column)
             {
                 var isNullable = !column.isKey && column.isNullable;
                 var columnDbType = column.columnDbType ?? GetColumnDbType(column);
@@ -280,7 +280,7 @@ create table {DelimitTableName(entityDescriptor)} (
             [typeof(Guid)] = "uniqueIdentifier",
 
         };
-        protected override string GetColumnDbType(IColumnDescriptor column)
+        protected override string GetColumnDbType(IPropertyDescriptor column)
         {
             Type type = column.type;
 
@@ -320,7 +320,7 @@ create table {DelimitTableName(entityDescriptor)} (
             if (key == null) return EAddType.noKeyColumn;
 
             var keyValue = key.GetValue(entity);
-            var keyIsEmpty = keyValue is null || keyValue.Equals(TypeUtil.DefaultValue(arg.entityDescriptor.key.type));
+            var keyIsEmpty = keyValue is null || keyValue.Equals(TypeUtil.GetDefaultValue(arg.entityDescriptor.key.type));
 
             if (key.isIdentity)
             {
@@ -338,7 +338,7 @@ create table {DelimitTableName(entityDescriptor)} (
                 // insert into UserInfo(name) output inserted.guid values('dd');
 
                 var entityDescriptor = arg.entityDescriptor;
-                var (columnNames, sqlColumnParams, GetSqlParams) = PrepareAdd_Columns(arg, entityDescriptor.columns);
+                var (columnNames, sqlColumnParams, GetSqlParams) = PrepareAdd_Columns(arg, entityDescriptor.propertiesWithoutKey);
                 var sqlOutput = "output inserted." + DelimitIdentifier(entityDescriptor.key.columnName);
 
                 string sql = $@"insert into {DelimitTableName(entityDescriptor)}({string.Join(",", columnNames)}) {sqlOutput} values({string.Join(",", sqlColumnParams)});";
@@ -349,7 +349,7 @@ create table {DelimitTableName(entityDescriptor)} (
                 // insert into user(name,fatherId,motherId) values('',0,0);
 
                 var entityDescriptor = arg.entityDescriptor;
-                var (columnNames, sqlColumnParams, GetSqlParams) = PrepareAdd_Columns(arg, entityDescriptor.allColumns);
+                var (columnNames, sqlColumnParams, GetSqlParams) = PrepareAdd_Columns(arg, entityDescriptor.properties);
                 string sql = $@"insert into {DelimitTableName(entityDescriptor)}({string.Join(",", columnNames)}) values({string.Join(",", sqlColumnParams)});";
                 return (sql, GetSqlParams);
             }

+ 5 - 5
src/Vitorm.Sqlite/SqlTranslateService.cs

@@ -172,14 +172,14 @@ CREATE TABLE IF NOT EXISTS "User" (
                 sqlFields.Add(GetColumnSql(entityDescriptor.key));
 
             // #2 columns
-            entityDescriptor.columns?.ForEach(column => sqlFields.Add(GetColumnSql(column)));
+            entityDescriptor.propertiesWithoutKey?.ForEach(column => sqlFields.Add(GetColumnSql(column)));
 
             return $@"
 CREATE TABLE IF NOT EXISTS {DelimitTableName(entityDescriptor)} (
   {string.Join(",\r\n  ", sqlFields)}
 )";
 
-            string GetColumnSql(IColumnDescriptor column)
+            string GetColumnSql(IPropertyDescriptor column)
             {
                 var isNullable = !column.isKey && column.isNullable;
                 var columnDbType = column.columnDbType ?? GetColumnDbType(column);
@@ -221,7 +221,7 @@ CREATE TABLE IF NOT EXISTS {DelimitTableName(entityDescriptor)} (
         };
 
 
-        protected override string GetColumnDbType(IColumnDescriptor column)
+        protected override string GetColumnDbType(IPropertyDescriptor column)
         {
             Type type = column.type;
 
@@ -262,7 +262,7 @@ CREATE TABLE IF NOT EXISTS {DelimitTableName(entityDescriptor)} (
                 // insert into "user"(name,fatherId,motherId) values('lith',1,1); select last_insert_rowid();
 
                 var entityDescriptor = arg.entityDescriptor;
-                var (columnNames, sqlColumnParams, GetSqlParams) = PrepareAdd_Columns(arg, entityDescriptor.columns);
+                var (columnNames, sqlColumnParams, GetSqlParams) = PrepareAdd_Columns(arg, entityDescriptor.propertiesWithoutKey);
                 string sql = $@"insert into {DelimitTableName(entityDescriptor)}({string.Join(",", columnNames)}) values({string.Join(",", sqlColumnParams)});";
 
                 // get generated id
@@ -274,7 +274,7 @@ CREATE TABLE IF NOT EXISTS {DelimitTableName(entityDescriptor)} (
                 // insert into "user"(name,fatherId,motherId) values('lith',1,1);
 
                 var entityDescriptor = arg.entityDescriptor;
-                var (columnNames, sqlColumnParams, GetSqlParams) = PrepareAdd_Columns(arg, entityDescriptor.allColumns);
+                var (columnNames, sqlColumnParams, GetSqlParams) = PrepareAdd_Columns(arg, entityDescriptor.properties);
                 string sql = $@"insert into {DelimitTableName(entityDescriptor)}({string.Join(",", columnNames)}) values({string.Join(",", sqlColumnParams)});";
                 return (sql, GetSqlParams);
             }

+ 45 - 15
src/Vitorm/DbContext.Event.cs

@@ -7,19 +7,20 @@ namespace Vitorm
     {
         public static Action<ExecuteEventArgument> event_DefaultOnExecuting;
         public Action<ExecuteEventArgument> event_OnExecuting = event_DefaultOnExecuting;
-    }
 
-    public static class DbContext_Extensions_Event
-    {
-        public static void Event_OnExecuting(this DbContext dbContext, string executeString = null, object param = null)
+        public virtual void Event_OnExecuting(string executeString = null, object param = null)
         {
-            if (dbContext.event_OnExecuting != null)
-                dbContext.event_OnExecuting(new(dbContext, executeString, param));
+            event_OnExecuting?.Invoke(new(this, executeString, param));
         }
-        public static void Event_OnExecuting(this DbContext dbContext, ExecuteEventArgument arg)
+
+        public virtual void Event_OnExecuting(ExecuteEventArgument arg)
         {
-            if (dbContext.event_OnExecuting != null)
-                dbContext.event_OnExecuting(arg);
+            event_OnExecuting?.Invoke(arg);
+        }
+
+        public virtual void Event_OnExecuting(Lazy<ExecuteEventArgument> arg)
+        {
+            event_OnExecuting?.Invoke(new ExecuteEventArgument_Lazy(arg));
         }
     }
 
@@ -31,14 +32,17 @@ namespace Vitorm
             this.dbContext = dbContext;
             this.executeString = executeString;
             this.param = param;
-            this.extraParam = extraParam;
+            this._extraParam = extraParam;
         }
 
-        public DbContext dbContext;
-        public string executeString;
-        public object param;
-        public Dictionary<string, object> extraParam;
-        public object GetExtraParam(string key) => extraParam?.TryGetValue(key, out var value) == true ? value : null;
+
+        public virtual DbContext dbContext { get; protected set; }
+        public virtual string executeString { get; protected set; }
+        public virtual object param { get; protected set; }
+
+        protected Dictionary<string, object> _extraParam;
+        public virtual Dictionary<string, object> extraParam { get => _extraParam; set => _extraParam = value; }
+        public virtual object GetExtraParam(string key) => extraParam?.TryGetValue(key, out var value) == true ? value : null;
 
         public ExecuteEventArgument SetExtraParam(string key, object param)
         {
@@ -48,4 +52,30 @@ namespace Vitorm
         }
     }
 
+    public class ExecuteEventArgument_Lazy : ExecuteEventArgument
+    {
+        protected Lazy<ExecuteEventArgument> _lazyArg;
+
+        public ExecuteEventArgument_Lazy(Lazy<ExecuteEventArgument> lazyArg)
+        {
+            this._lazyArg = lazyArg;
+        }
+
+        public override DbContext dbContext => _lazyArg.Value?.dbContext;
+        public override string executeString => _lazyArg.Value?.executeString;
+        public override object param => _lazyArg.Value?.dbContext;
+        public override Dictionary<string, object> extraParam
+        {
+            get
+            {
+                return _lazyArg.Value?.extraParam;
+            }
+            set
+            {
+                _lazyArg.Value.extraParam = value;
+            }
+        }
+
+    }
+
 }

+ 6 - 4
src/Vitorm/Entity/EntityDescriptorWithAlias.cs

@@ -1,5 +1,7 @@
 using System;
 
+using Vitorm.Entity.PropertyType;
+
 namespace Vitorm.Entity
 {
     public partial class EntityDescriptorWithAlias : IEntityDescriptor
@@ -11,7 +13,7 @@ namespace Vitorm.Entity
             this.tableName = tableName;
         }
 
-
+        public IPropertyObjectType propertyType => originEntityDescriptor.propertyType;
         public Type entityType => originEntityDescriptor?.entityType;
         public string tableName { get; protected set; }
         public string schema => originEntityDescriptor?.schema;
@@ -24,15 +26,15 @@ namespace Vitorm.Entity
         /// <summary>
         /// primary key
         /// </summary>
-        public IColumnDescriptor key => originEntityDescriptor?.key;
+        public IPropertyDescriptor key => originEntityDescriptor?.key;
 
         /// <summary>
         /// not include primary key
         /// </summary>
-        public IColumnDescriptor[] columns => originEntityDescriptor?.columns;
+        public IPropertyDescriptor[] propertiesWithoutKey => originEntityDescriptor?.propertiesWithoutKey;
 
 
-        public IColumnDescriptor[] allColumns => originEntityDescriptor?.allColumns;
+        public IPropertyDescriptor[] properties => originEntityDescriptor?.properties;
 
 
     }

+ 1 - 1
src/Vitorm/Entity/Extensions/IEntityDescriptor_Extensions.Column.cs

@@ -14,7 +14,7 @@ namespace Vitorm
         /// <returns></returns>
         public static string GetColumnNameByPropertyName(this IEntityDescriptor data, string propertyName)
         {
-            return data?.allColumns.FirstOrDefault(m => m.propertyName == propertyName)?.columnName;
+            return data?.properties.FirstOrDefault(m => m.propertyName == propertyName)?.columnName;
         }
     }
 }

+ 6 - 3
src/Vitorm/Entity/IEntityDescriptor.cs

@@ -1,9 +1,12 @@
 using System;
 
+using Vitorm.Entity.PropertyType;
+
 namespace Vitorm.Entity
 {
     public interface IEntityDescriptor
     {
+        IPropertyObjectType propertyType { get; }
         Type entityType { get; }
         string schema { get; }
         string tableName { get; }
@@ -11,16 +14,16 @@ namespace Vitorm.Entity
         /// <summary>
         /// primary key
         /// </summary>
-        public IColumnDescriptor key { get; }
+        public IPropertyDescriptor key { get; }
 
         /// <summary>
         /// columns except primary key
         /// </summary>
-        public IColumnDescriptor[] columns { get; }
+        public IPropertyDescriptor[] propertiesWithoutKey { get; }
 
         /// <summary>
         /// columns including primary key
         /// </summary>
-        public IColumnDescriptor[] allColumns { get; }
+        public IPropertyDescriptor[] properties { get; }
     }
 }

+ 15 - 3
src/Vitorm/Entity/IColumnDescriptor.cs → src/Vitorm/Entity/IPropertyDescriptor.cs

@@ -1,18 +1,31 @@
 using System;
 
+using Vitorm.Entity.PropertyType;
+
 namespace Vitorm.Entity
 {
-    public interface IColumnDescriptor
+    public interface IPropertyDescriptor
     {
         Type type { get; }
         /// <summary>
         /// property name in Entity Type
         /// </summary>
         string propertyName { get; }
+
+        IPropertyType propertyType { get; }
+
+
+        void SetValue(object entity, object value);
+        object GetValue(object entity);
+
+
+        #region datasource properties
+
         /// <summary>
         /// column name in database
         /// </summary>
         string columnName { get; }
+
         bool isKey { get; }
 
         /// <summary>
@@ -36,9 +49,8 @@ namespace Vitorm.Entity
         /// </summary>
         int? columnLength { get; }
 
+        #endregion
 
-        void SetValue(object entity, object value);
-        object GetValue(object entity);
 
 
     }

+ 14 - 9
src/Vitorm/Entity/Loader/DataAnnotations/EntityDescriptor.cs

@@ -1,23 +1,28 @@
 using System;
 using System.Linq;
 
+using Vitorm.Entity.PropertyType;
+
 namespace Vitorm.Entity.Loader.DataAnnotations
 {
     public partial class EntityDescriptor : IEntityDescriptor
     {
-        public EntityDescriptor(Type entityType, IColumnDescriptor[] allColumns, string tableName, string schema = null)
+        public EntityDescriptor(IPropertyObjectType propertyType, string tableName, string schema = null)
         {
-            this.entityType = entityType;
+            this.propertyType = propertyType;
             this.tableName = tableName;
             this.schema = schema;
 
-            this.key = allColumns.FirstOrDefault(m => m.isKey);
-            this.columns = allColumns.Where(m => !m.isKey).OrderBy(col => col.columnOrder ?? int.MaxValue).ToArray();
-            this.allColumns = allColumns.OrderBy(col => col.columnOrder ?? int.MaxValue).ToArray();
+            var allProperties = propertyType.properties;
+
+            this.key = allProperties.FirstOrDefault(m => m.isKey);
+            this.propertiesWithoutKey = allProperties.Where(m => !m.isKey).OrderBy(col => col.columnOrder ?? int.MaxValue).ToArray();
+            this.properties = allProperties.OrderBy(col => col.columnOrder ?? int.MaxValue).ToArray();
         }
 
+        public IPropertyObjectType propertyType { get; protected set; }
 
-        public Type entityType { get; protected set; }
+        public Type entityType => propertyType?.type;
         public string tableName { get; protected set; }
         public string schema { get; protected set; }
 
@@ -29,15 +34,15 @@ namespace Vitorm.Entity.Loader.DataAnnotations
         /// <summary>
         /// primary key
         /// </summary>
-        public IColumnDescriptor key { get; protected set; }
+        public IPropertyDescriptor key { get; protected set; }
 
         /// <summary>
         /// not include primary key
         /// </summary>
-        public IColumnDescriptor[] columns { get; protected set; }
+        public IPropertyDescriptor[] propertiesWithoutKey { get; protected set; }
 
 
-        public IColumnDescriptor[] allColumns { get; protected set; }
+        public IPropertyDescriptor[] properties { get; protected set; }
 
 
     }

+ 50 - 10
src/Vitorm/Entity/Loader/DataAnnotations/EntityLoader.cs

@@ -4,6 +4,10 @@ using System.ComponentModel.DataAnnotations.Schema;
 using System.Linq;
 using System.Reflection;
 
+using Vitorm.Entity.PropertyType;
+
+using ValueType = Vitorm.Entity.PropertyType.PropertyValueType;
+
 namespace Vitorm.Entity.Loader.DataAnnotations
 {
     public class EntityLoader : IEntityLoader
@@ -52,25 +56,57 @@ namespace Vitorm.Entity.Loader.DataAnnotations
                 tableName = entityType.Name;
             }
 
-            var columns = LoadColumns(entityType);
+            var propertyType = ConvertToObjectType(entityType, new());
 
             // key
-            if (!strictMode && !columns.Any(col => col.isKey))
+            if (!strictMode && !propertyType.properties.Any(col => col.isKey))
             {
                 var keyNames = new[] { "id", tableName + "id" };
-                var keyColumn = columns.FirstOrDefault(col => keyNames.Contains(col.columnName, StringComparer.OrdinalIgnoreCase));
+                var keyColumn = (PropertyDescriptor)propertyType.properties.FirstOrDefault(col => keyNames.Contains(col.columnName, StringComparer.OrdinalIgnoreCase));
                 if (keyColumn != null) keyColumn.isKey = true;
             }
 
-            IColumnDescriptor[] allColumns = columns.Select(m => (IColumnDescriptor)m).ToArray();
+            return (true, new EntityDescriptor(propertyType, tableName, schema));
+        }
+
+
+        public static IPropertyType ConvertToPropertyType(Type propertyClrType, Dictionary<Type, IPropertyType> typeCache)
+        {
+            // value
+            if (TypeUtil.IsValueType(propertyClrType))
+            {
+                return new ValueType(propertyClrType);
+            }
+
+            // try get for cache
+            {
+                if (typeCache?.TryGetValue(propertyClrType, out IPropertyType propertyType) == true) return propertyType;
+            }
+
+
+            // array
+            var arrayElementType = TypeUtil.GetElementTypeFromArray(propertyClrType);
+            if (arrayElementType != null)
+            {
+                var propertyType = new PropertyArrayType(propertyClrType);
+                typeCache[propertyClrType] = propertyType;
+
+                propertyType.elementPropertyType = ConvertToPropertyType(arrayElementType, typeCache);
+                return propertyType;
+            }
+
+            // object
+            return ConvertToObjectType(propertyClrType, typeCache);
 
-            return (true, new EntityDescriptor(entityType, allColumns, tableName, schema));
         }
 
 
-        public static List<ColumnDescriptor> LoadColumns(Type entityType)
+        public static PropertyObjectType ConvertToObjectType(Type propertyClrType, Dictionary<Type, IPropertyType> typeCache)
         {
-            return entityType?.GetProperties(BindingFlags.Public | BindingFlags.Instance)
+            var propertyType = new PropertyObjectType(propertyClrType);
+            typeCache[propertyClrType] = propertyType;
+
+            var propertyDescriptors = propertyClrType?.GetProperties(BindingFlags.Public | BindingFlags.Instance)
                 .Select(propertyInfo =>
                 {
                     if (propertyInfo.GetCustomAttribute<System.ComponentModel.DataAnnotations.Schema.NotMappedAttribute>(inherit: true) != null) return null;
@@ -102,13 +138,17 @@ namespace Vitorm.Entity.Loader.DataAnnotations
                         }
                     }
 
-                    return new ColumnDescriptor(
-                        propertyInfo, columnName: columnName,
+                    return new PropertyDescriptor(
+                        propertyInfo, propertyType: ConvertToPropertyType(propertyInfo.PropertyType, typeCache),
+                        columnName: columnName,
                         isKey: isKey, isIdentity: isIdentity, isNullable: isNullable,
                         columnDbType: columnDbType, columnLength: columnLength,
                         columnOrder: columnOrder
                         );
-                }).Where(column => column != null).ToList();
+                }).Where(column => column != null);
+
+            propertyType.properties = propertyDescriptors.Select(m => (IPropertyDescriptor)m).ToArray();
+            return propertyType;
         }
 
 

+ 10 - 5
src/Vitorm/Entity/Loader/DataAnnotations/ColumnDescriptor.cs → src/Vitorm/Entity/Loader/DataAnnotations/PropertyDescriptor.cs

@@ -1,19 +1,22 @@
 using System;
 using System.Reflection;
 
+using Vitorm.Entity.PropertyType;
+
 namespace Vitorm.Entity.Loader.DataAnnotations
 {
-    public class ColumnDescriptor : IColumnDescriptor
+    public class PropertyDescriptor : IPropertyDescriptor
     {
-        public ColumnDescriptor(
-            PropertyInfo propertyInfo, string columnName,
+        public PropertyDescriptor(
+            PropertyInfo propertyInfo, IPropertyType propertyType,
+            string columnName,
             bool isKey, bool isIdentity, bool isNullable,
             string columnDbType, int? columnLength = null,
             int? columnOrder = null, bool? isIndex = null
             )
         {
             this.propertyInfo = propertyInfo;
-            type = propertyInfo.PropertyType;
+            this.propertyType = propertyType;
 
             this.columnName = columnName;
 
@@ -29,7 +32,9 @@ namespace Vitorm.Entity.Loader.DataAnnotations
         }
 
         readonly PropertyInfo propertyInfo;
-        public Type type { get; private set; }
+        public Type type => propertyType?.type;
+
+        public IPropertyType propertyType { get; private set; }
 
         /// <summary>
         /// property name in Entity Type

+ 12 - 0
src/Vitorm/Entity/PropertyType/ETypeMode.cs

@@ -0,0 +1,12 @@
+namespace Vitorm.Entity.PropertyType
+{
+    public enum ETypeMode
+    {
+        value,
+        @object,
+        array,
+        dictionary,
+
+    }
+
+}

+ 43 - 0
src/Vitorm/Entity/PropertyType/IPropertyType.cs

@@ -0,0 +1,43 @@
+using System;
+using System.Collections;
+
+namespace Vitorm.Entity.PropertyType
+{
+
+    public interface IPropertyType
+    {
+        ETypeMode mode { get; }
+
+        Type type { get; }
+    }
+
+
+    public interface IPropertyValueType : IPropertyType
+    {
+    }
+
+
+    public interface IPropertyObjectType : IPropertyType
+    {
+        IPropertyDescriptor[] properties { get; }
+    }
+
+
+    public interface IPropertyArrayType : IPropertyType
+    {
+        IPropertyType elementPropertyType { get; }
+        object CreateArray(IEnumerable elements);
+    }
+
+
+    public interface IPropertyDictionaryType : IPropertyType
+    {
+        Type keyType { get; }
+        IPropertyType valueType { get; }
+    }
+
+
+
+
+
+}

+ 85 - 0
src/Vitorm/Entity/PropertyType/PropertyType.cs

@@ -0,0 +1,85 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+
+namespace Vitorm.Entity.PropertyType
+{
+
+    public class PropertyValueType : IPropertyValueType
+    {
+        public PropertyValueType(Type type)
+        {
+            this.type = type;
+        }
+
+        public ETypeMode mode => ETypeMode.value;
+
+        public Type type { get; set; }
+    }
+
+    public class PropertyObjectType : IPropertyObjectType
+    {
+        public PropertyObjectType(Type type, IPropertyDescriptor[] properties = null)
+        {
+            this.type = type;
+            this.properties = properties;
+        }
+
+        public ETypeMode mode => ETypeMode.@object;
+        public Type type { get; set; }
+        public IPropertyDescriptor[] properties { get; set; }
+    }
+
+    public class PropertyArrayType : IPropertyArrayType
+    {
+        public PropertyArrayType(Type type, IPropertyType elementPropertyType = null)
+        {
+            this.type = type;
+            this.elementPropertyType = elementPropertyType;
+        }
+
+        public ETypeMode mode => ETypeMode.array;
+        public Type type { get; set; }
+        public IPropertyType elementPropertyType { get; set; }
+        public virtual object CreateArray(IEnumerable elements)
+        {
+            return GetMethod_CreateArray(elementPropertyType.type).Invoke(this, new object[] { type, elements });
+        }
+
+
+        static MethodInfo method_CreateArray = null;
+        static MethodInfo GetMethod_CreateArray(Type elementType)
+        {
+            method_CreateArray ??= new Func<Type, IEnumerable, object>(CreateArray<object>).Method.GetGenericMethodDefinition();
+
+            return method_CreateArray.MakeGenericMethod(elementType);
+        }
+
+        public static object CreateArray<Element>(Type arrayType, IEnumerable elements)
+        {
+            var items = elements as IEnumerable<Element> ?? elements.Cast<Element>();
+
+            if (arrayType.IsArray) return items.ToArray();
+
+            var array = Activator.CreateInstance(arrayType);
+            if (array is IList list)
+            {
+                foreach (var item in items) list.Add(item);
+                return array;
+            }
+
+            if (array is ISet<Element> set)
+            {
+                foreach (var item in items) set.Add(item);
+                return array;
+            }
+            throw new InvalidOperationException($"Can not convert to Array, element type not match. Array type: {arrayType.FullName} . Element type: {typeof(Element).FullName}");
+        }
+    }
+
+
+
+
+}

+ 2 - 2
src/Vitorm/Sql/DataReader/EntityReader/CompiledLambda/EntityPropertyReader.cs

@@ -7,9 +7,9 @@ namespace Vitorm.Sql.DataReader.EntityConstructor.CompiledLambda
 {
     class EntityPropertyReader : SqlFieldReader
     {
-        public IColumnDescriptor column { get; protected set; }
+        public IPropertyDescriptor column { get; protected set; }
 
-        public EntityPropertyReader(IColumnDescriptor column, int sqlColumnIndex) : base(column.type, sqlColumnIndex)
+        public EntityPropertyReader(IPropertyDescriptor column, int sqlColumnIndex) : base(column.type, sqlColumnIndex)
         {
             this.column = column;
         }

+ 2 - 2
src/Vitorm/Sql/DataReader/EntityReader/CompiledLambda/ModelReader.cs

@@ -14,7 +14,7 @@ namespace Vitorm.Sql.DataReader.EntityReader.CompiledLambda
         public string argUniqueKey { get; set; }
         public Type entityType { get; }
 
-        List<(IColumnDescriptor columnDescriptor, SqlFieldReader sqlFieldReader)> properties = new();
+        List<(IPropertyDescriptor columnDescriptor, SqlFieldReader sqlFieldReader)> properties = new();
 
 
         public ModelReader(SqlColumns sqlColumns, ISqlTranslateService sqlTranslateService, string tableName, string argUniqueKey, string argName, IEntityDescriptor entityDescriptor)
@@ -24,7 +24,7 @@ namespace Vitorm.Sql.DataReader.EntityReader.CompiledLambda
 
             this.entityType = entityDescriptor.entityType;
 
-            foreach (var column in entityDescriptor.allColumns)
+            foreach (var column in entityDescriptor.properties)
             {
                 var sqlColumnIndex = sqlColumns.AddSqlColumnAndGetIndex(sqlTranslateService, tableName, columnDescriptor: column);
 

+ 2 - 2
src/Vitorm/Sql/DataReader/EntityReader/EntityConstructor/EntityReader_.cs

@@ -10,13 +10,13 @@ namespace Vitorm.Sql.DataReader.EntityReader.EntityConstructor
     public class EntityReader_ : IValueReader
     {
         protected Type entityType;
-        protected List<(IColumnDescriptor columnDescriptor, SqlFieldReader sqlFieldReader)> properties = new();
+        protected List<(IPropertyDescriptor columnDescriptor, SqlFieldReader sqlFieldReader)> properties = new();
 
         public EntityReader_(EntityReaderConfig config, string tableName, Type entityType, IEntityDescriptor entityDescriptor)
         {
             this.entityType = entityDescriptor.entityType;
 
-            foreach (var column in entityDescriptor.allColumns)
+            foreach (var column in entityDescriptor.properties)
             {
                 var sqlColumnIndex = config.sqlColumns.AddSqlColumnAndGetIndex(config.sqlTranslateService, tableName, columnDescriptor: column);
 

+ 2 - 2
src/Vitorm/Sql/DataReader/SqlColumns.cs

@@ -21,7 +21,7 @@ namespace Vitorm.Sql.DataReader
         /// <param name="tableName"></param>
         /// <param name="columnDescriptor"></param>
         /// <returns></returns>
-        public int AddSqlColumnAndGetIndex(ISqlTranslateService sqlTranslateService, string tableName, IColumnDescriptor columnDescriptor)
+        public int AddSqlColumnAndGetIndex(ISqlTranslateService sqlTranslateService, string tableName, IPropertyDescriptor columnDescriptor)
         {
             var sqlColumnName = sqlTranslateService.GetSqlField(tableName, columnDescriptor.columnName);
 
@@ -97,7 +97,7 @@ namespace Vitorm.Sql.DataReader
         {
             // or table alias
             public string tableName;
-            public IColumnDescriptor columnDescriptor;
+            public IPropertyDescriptor columnDescriptor;
             public ExpressionNode_Member member;
 
             public string sqlColumnName;

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

@@ -127,7 +127,7 @@ namespace Vitorm.Sql
             if (reader is DbDataReader dataReader ? await dataReader.ReadAsync() : reader.Read())
             {
                 var entity = (Entity)Activator.CreateInstance(entityDescriptor.entityType);
-                foreach (var column in entityDescriptor.allColumns)
+                foreach (var column in entityDescriptor.properties)
                 {
                     var value = TypeUtil.ConvertToType(reader[column.columnName], column.type);
                     if (value != null)

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

@@ -163,7 +163,7 @@ namespace Vitorm.Sql
             if (reader.Read())
             {
                 var entity = (Entity)Activator.CreateInstance(entityDescriptor.entityType);
-                foreach (var column in entityDescriptor.allColumns)
+                foreach (var column in entityDescriptor.properties)
                 {
                     var value = TypeUtil.ConvertToType(reader[column.columnName], column.type);
                     if (value != null)

+ 8 - 8
src/Vitorm/Sql/SqlTranslate/SqlTranslateService.cs

@@ -105,7 +105,7 @@ namespace Vitorm.Sql.SqlTranslate
             return GetSqlField(member.objectValue?.parameterName ?? member.parameterName, memberName);
         }
 
-        protected virtual string GetColumnDbType(IColumnDescriptor column) => GetColumnDbType(column.type);
+        protected virtual string GetColumnDbType(IPropertyDescriptor column) => GetColumnDbType(column.type);
         protected abstract string GetColumnDbType(Type type);
 
 
@@ -314,7 +314,7 @@ namespace Vitorm.Sql.SqlTranslate
                                 }
 
                         }
-                        throw new NotSupportedException("[QueryTranslator] not suported MethodCall: " + methodCall.methodName);
+                        throw new NotSupportedException("[QueryTranslator] not supported MethodCall: " + methodCall.methodName);
                     }
 
                 #region Read Value
@@ -361,7 +361,7 @@ namespace Vitorm.Sql.SqlTranslate
                     }
                     #endregion
             }
-            throw new NotSupportedException("[QueryTranslator] not suported nodeType: " + node.nodeType);
+            throw new NotSupportedException("[QueryTranslator] not supported nodeType: " + node.nodeType);
         }
 
 
@@ -400,7 +400,7 @@ namespace Vitorm.Sql.SqlTranslate
         public static bool Entity_HasKeyValue(SqlTranslateArgument arg, object entity)
         {
             var keyValue = arg.entityDescriptor?.key.GetValue(entity);
-            return keyValue is not null && !keyValue.Equals(TypeUtil.DefaultValue(arg.entityDescriptor.key.type));
+            return keyValue is not null && !keyValue.Equals(TypeUtil.GetDefaultValue(arg.entityDescriptor.key.type));
         }
         public virtual bool HasKeyValue(SqlTranslateArgument arg, object entity) => hasKeyValue?.Invoke(arg, entity) == true;
         public virtual EAddType Entity_GetAddType(SqlTranslateArgument arg, object entity)
@@ -416,7 +416,7 @@ namespace Vitorm.Sql.SqlTranslate
             //return EAddType.unexpectedEmptyKey;
         }
 
-        protected virtual (List<string> columnNames, List<string> sqlColumnParams, Func<object, Dictionary<string, object>> GetSqlParams) PrepareAdd_Columns(SqlTranslateArgument arg, IColumnDescriptor[] columns)
+        protected virtual (List<string> columnNames, List<string> sqlColumnParams, Func<object, Dictionary<string, object>> GetSqlParams) PrepareAdd_Columns(SqlTranslateArgument arg, IPropertyDescriptor[] columns)
         {
             // #1 GetSqlParams 
             Dictionary<string, object> GetSqlParams(object entity)
@@ -452,7 +452,7 @@ namespace Vitorm.Sql.SqlTranslate
                 // insert into user(name,fatherId,motherId) values('',0,0);
 
                 var entityDescriptor = arg.entityDescriptor;
-                var (columnNames, sqlColumnParams, GetSqlParams) = PrepareAdd_Columns(arg, entityDescriptor.allColumns);
+                var (columnNames, sqlColumnParams, GetSqlParams) = PrepareAdd_Columns(arg, entityDescriptor.properties);
                 string sql = $@"insert into {DelimitTableName(entityDescriptor)}({string.Join(",", columnNames)}) values({string.Join(",", sqlColumnParams)});";
                 return (sql, GetSqlParams);
             }
@@ -501,7 +501,7 @@ namespace Vitorm.Sql.SqlTranslate
             Dictionary<string, object> GetSqlParams(object entity)
             {
                 var sqlParam = new Dictionary<string, object>();
-                foreach (var column in entityDescriptor.allColumns)
+                foreach (var column in entityDescriptor.properties)
                 {
                     var columnName = column.columnName;
                     var value = column.GetValue(entity);
@@ -515,7 +515,7 @@ namespace Vitorm.Sql.SqlTranslate
             // #2 columns
             List<string> columnsToUpdate = new List<string>();
             string columnName;
-            foreach (var column in entityDescriptor.columns)
+            foreach (var column in entityDescriptor.propertiesWithoutKey)
             {
                 columnName = column.columnName;
                 columnsToUpdate.Add($"{DelimitIdentifier(columnName)}={GenerateParameterName(columnName)}");

+ 21 - 1
src/Vitorm/TypeUtil.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Collections;
 using System.Reflection;
 
 namespace Vitorm
@@ -28,6 +29,25 @@ namespace Vitorm
         }
 
 
+        /// <summary>
+        /// is Array or List(SortedSet...)
+        /// </summary>
+        /// <param name="type"></param>
+        /// <returns></returns>
+        public static bool IsArrayType(Type type)
+        {
+            if (type.IsArray) return true;
+            if (type.IsGenericType && typeof(ICollection).IsAssignableFrom(type)) return true;
+            return false;
+        }
+
+        public static Type GetElementTypeFromArray(Type type)
+        {
+            if (type.IsArray) return type.GetElementType();
+            if (type.IsGenericType && typeof(ICollection).IsAssignableFrom(type)) return type.GetGenericArguments()[0];
+            return null;
+        }
+
 
         public static object ConvertToType(object value, Type type)
         {
@@ -48,7 +68,7 @@ namespace Vitorm
         }
 
 
-        public static object DefaultValue(Type type)
+        public static object GetDefaultValue(Type type)
         {
             if (null == type || !type.IsValueType) return null;
             return Activator.CreateInstance(type);

+ 10 - 5
test/Vitorm.Data.MsTest/CustomTest/EntityLoader_CustomLoader_Test.cs

@@ -4,6 +4,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
 
 using Vitorm.Entity;
 using Vitorm.Entity.Loader.DataAnnotations;
+using Vitorm.Entity.PropertyType;
 using Vitorm.MsTest.CustomTest;
 using Vitorm.MsTest.Sqlite;
 
@@ -65,7 +66,7 @@ namespace Vitorm.MsTest.CustomTest
         {
             if (!GetTableName(entityType, out var tableName, out var schema)) return default;
 
-            IColumnDescriptor[] allColumns = entityType?.GetProperties(BindingFlags.Public | BindingFlags.Instance)
+            var propertyDescriptors = entityType?.GetProperties(BindingFlags.Public | BindingFlags.Instance)
                 .Select(propertyInfo =>
                 {
                     // #1 isKey
@@ -90,15 +91,19 @@ namespace Vitorm.MsTest.CustomTest
                         }
                     }
 
-                    return new ColumnDescriptor(
-                        propertyInfo, columnName: columnName,
+                    return new PropertyDescriptor(
+                        propertyInfo, propertyType: new PropertyValueType(propertyInfo.PropertyType),
+                        columnName: columnName,
                         isKey: isKey, isIdentity: isIdentity, isNullable: isNullable,
                         columnDbType: columnDbType,
                         columnOrder: columnOrder
                         );
-                }).Where(column => column != null).ToArray();
+                }).Where(column => column != null);
 
-            return (true, new EntityDescriptor(entityType, allColumns, tableName, schema));
+            var propertyType = new PropertyObjectType(entityType);
+            propertyType.properties = propertyDescriptors.Select(m => (IPropertyDescriptor)m).ToArray();
+
+            return (true, new EntityDescriptor(propertyType, tableName, schema));
         }
     }
     #endregion

+ 10 - 5
test/Vitorm.Sqlite.MsTest/CommonTest/EntityLoader_CustomLoader_Test.cs

@@ -4,6 +4,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
 
 using Vitorm.Entity;
 using Vitorm.Entity.Loader.DataAnnotations;
+using Vitorm.Entity.PropertyType;
 
 namespace Vitorm.MsTest.CommonTest
 {
@@ -120,7 +121,7 @@ namespace Vitorm.MsTest.CommonTest
             {
                 if (!GetTableName(entityType, out var tableName, out var schema)) return default;
 
-                IColumnDescriptor[] allColumns = entityType?.GetProperties(BindingFlags.Public | BindingFlags.Instance)
+                var propertyDescriptors = entityType?.GetProperties(BindingFlags.Public | BindingFlags.Instance)
                     .Select(propertyInfo =>
                     {
                         var labels = propertyInfo?.GetCustomAttributes<LabelAttribute>();
@@ -152,15 +153,19 @@ namespace Vitorm.MsTest.CommonTest
                             }
                         }
 
-                        return new ColumnDescriptor(
-                            propertyInfo, columnName: columnName,
+                        return new PropertyDescriptor(
+                            propertyInfo, propertyType: new PropertyValueType(propertyInfo.PropertyType),
+                            columnName: columnName,
                             isKey: isKey, isIdentity: isIdentity, isNullable: isNullable,
                             columnDbType: columnDbType,
                             columnOrder: columnOrder
                             );
-                    }).Where(column => column != null).ToArray();
+                    }).Where(column => column != null);
 
-                return (true, new EntityDescriptor(entityType, allColumns, tableName, schema));
+                var propertyType = new PropertyObjectType(entityType);
+                propertyType.properties = propertyDescriptors.Select(m => (IPropertyDescriptor)m).ToArray();
+
+                return (true, new EntityDescriptor(propertyType, tableName, schema));
             }
         }
         #endregion

+ 44 - 0
test/Vitorm.Sqlite.MsTest/CommonTest/Query_Where_In_Test.cs

@@ -0,0 +1,44 @@
+using System.Data;
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+using Vit.Linq;
+
+namespace Vitorm.MsTest.CommonTest
+{
+
+    [TestClass]
+    public class Query_Where_In_Test
+    {
+
+        [TestMethod]
+        public void Test_In()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            {
+                var ids = new[] { 1, 2 };
+                var userList = userQuery.Where(m => ids.Contains(m.id)).OrderBy(m => m.id).ToList();
+
+                var strIds = String.Join(',', userList.Select(m => m.id));
+                Assert.AreEqual(2, userList.Count);
+                Assert.AreEqual("1,2", strIds);
+            }
+
+
+            {
+                var ids = new List<int> { 1, 2 };
+                var userList = userQuery.Where(m => ids.Contains(m.id)).OrderBy(m => m.id).ToList();
+
+                var strIds = String.Join(',', userList.Select(m => m.id));
+                Assert.AreEqual(2, userList.Count);
+                Assert.AreEqual("1,2", strIds);
+            }
+
+        }
+
+
+
+    }
+}