Browse Source

- [Vitorm] support nested entity and rename Column to Property

Lith 6 tháng trước cách đây
mục cha
commit
e742819c41

+ 1 - 1
src/Versions.props

@@ -1,6 +1,6 @@
 <Project>
     <PropertyGroup>
-        <Version>2.2.2</Version>
+        <Version>2.3.0-develop</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.properties?.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.properties);
                 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.allProperties);
                 string sql = $@"insert into {DelimitTableName(entityDescriptor)}({string.Join(",", columnNames)}) values({string.Join(",", sqlColumnParams)});";
                 return (sql, GetSqlParams);
             }

+ 5 - 5
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.properties?.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;
 
@@ -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.properties);
                 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.allProperties);
                 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.properties?.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.properties);
                 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.allProperties);
                 string sql = $@"insert into {DelimitTableName(entityDescriptor)}({string.Join(",", columnNames)}) values({string.Join(",", sqlColumnParams)});";
                 return (sql, GetSqlParams);
             }

+ 3 - 3
src/Vitorm/Entity/EntityDescriptorWithAlias.cs

@@ -24,15 +24,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[] properties => originEntityDescriptor?.properties;
 
 
-        public IColumnDescriptor[] allColumns => originEntityDescriptor?.allColumns;
+        public IPropertyDescriptor[] allProperties => originEntityDescriptor?.allProperties;
 
 
     }

+ 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?.allProperties.FirstOrDefault(m => m.propertyName == propertyName)?.columnName;
         }
     }
 }

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

@@ -11,16 +11,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[] properties { get; }
 
         /// <summary>
         /// columns including primary key
         /// </summary>
-        public IColumnDescriptor[] allColumns { get; }
+        public IPropertyDescriptor[] allProperties { 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.properties = allProperties.Where(m => !m.isKey).OrderBy(col => col.columnOrder ?? int.MaxValue).ToArray();
+            this.allProperties = 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[] properties { get; protected set; }
 
 
-        public IColumnDescriptor[] allColumns { get; protected set; }
+        public IPropertyDescriptor[] allProperties { 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);
+
+            }
+
+            // 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.allProperties)
             {
                 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.allProperties)
             {
                 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.allProperties)
                 {
                     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.allProperties)
                 {
                     var value = TypeUtil.ConvertToType(reader[column.columnName], column.type);
                     if (value != null)

+ 5 - 5
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);
 
 
@@ -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.allProperties);
                 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.allProperties)
                 {
                     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.properties)
             {
                 columnName = column.columnName;
                 columnsToUpdate.Add($"{DelimitIdentifier(columnName)}={GenerateParameterName(columnName)}");

+ 8 - 0
src/Vitorm/TypeUtil.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Collections;
 using System.Reflection;
 
 namespace Vitorm
@@ -28,6 +29,13 @@ namespace Vitorm
         }
 
 
+        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)
         {

+ 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