@@ -2440,6 +2440,8 @@ public class TableMapping
24402440
24412441 public CreateFlags CreateFlags { get ; private set ; }
24422442
2443+ internal MapMethod Method { get ; private set ; } = MapMethod . ByName ;
2444+
24432445 readonly Column _autoPk ;
24442446 readonly Column [ ] _insertColumns ;
24452447 readonly Column [ ] _insertOrReplaceColumns ;
@@ -2463,33 +2465,13 @@ public TableMapping (Type type, CreateFlags createFlags = CreateFlags.None)
24632465 TableName = ( tableAttr != null && ! string . IsNullOrEmpty ( tableAttr . Name ) ) ? tableAttr . Name : MappedType . Name ;
24642466 WithoutRowId = tableAttr != null ? tableAttr . WithoutRowId : false ;
24652467
2466- var props = new List < PropertyInfo > ( ) ;
2467- var baseType = type ;
2468- var propNames = new HashSet < string > ( ) ;
2469- while ( baseType != typeof ( object ) ) {
2470- var ti = baseType . GetTypeInfo ( ) ;
2471- var newProps = (
2472- from p in ti . DeclaredProperties
2473- where
2474- ! propNames . Contains ( p . Name ) &&
2475- p . CanRead && p . CanWrite &&
2476- ( p . GetMethod != null ) && ( p . SetMethod != null ) &&
2477- ( p . GetMethod . IsPublic && p . SetMethod . IsPublic ) &&
2478- ( ! p . GetMethod . IsStatic ) && ( ! p . SetMethod . IsStatic )
2479- select p ) . ToList ( ) ;
2480- foreach ( var p in newProps ) {
2481- propNames . Add ( p . Name ) ;
2482- }
2483- props . AddRange ( newProps ) ;
2484- baseType = ti . BaseType ;
2485- }
2486-
2487- var cols = new List < Column > ( ) ;
2488- foreach ( var p in props ) {
2489- var ignore = p . IsDefined ( typeof ( IgnoreAttribute ) , true ) ;
2490- if ( ! ignore ) {
2491- cols . Add ( new Column ( p , createFlags ) ) ;
2492- }
2468+ var members = GetPublicMembers ( type ) ;
2469+ var cols = new List < Column > ( members . Count ) ;
2470+ foreach ( var m in members )
2471+ {
2472+ var ignore = m . IsDefined ( typeof ( IgnoreAttribute ) , true ) ;
2473+ if ( ! ignore )
2474+ cols . Add ( new Column ( m , createFlags ) ) ;
24932475 }
24942476 Columns = cols . ToArray ( ) ;
24952477 foreach ( var c in Columns ) {
@@ -2515,6 +2497,51 @@ from p in ti.DeclaredProperties
25152497 _insertOrReplaceColumns = Columns . ToArray ( ) ;
25162498 }
25172499
2500+ private IReadOnlyCollection < MemberInfo > GetPublicMembers ( Type type )
2501+ {
2502+ if ( type . Name . StartsWith ( "ValueTuple`" ) )
2503+ return GetFieldsFromValueTuple ( type ) ;
2504+
2505+ var members = new List < MemberInfo > ( ) ;
2506+ var memberNames = new HashSet < string > ( ) ;
2507+ var newMembers = new List < MemberInfo > ( ) ;
2508+ do
2509+ {
2510+ var ti = type . GetTypeInfo ( ) ;
2511+ newMembers . Clear ( ) ;
2512+
2513+ newMembers . AddRange (
2514+ from p in ti . DeclaredProperties
2515+ where ! memberNames . Contains ( p . Name ) &&
2516+ p . CanRead && p . CanWrite &&
2517+ p . GetMethod != null && p . SetMethod != null &&
2518+ p . GetMethod . IsPublic && p . SetMethod . IsPublic &&
2519+ ! p . GetMethod . IsStatic && ! p . SetMethod . IsStatic
2520+ select p ) ;
2521+
2522+ members . AddRange ( newMembers ) ;
2523+ foreach ( var m in newMembers )
2524+ memberNames . Add ( m . Name ) ;
2525+
2526+ type = ti . BaseType ;
2527+ }
2528+ while ( type != typeof ( object ) ) ;
2529+
2530+ return members ;
2531+ }
2532+
2533+ private IReadOnlyCollection < MemberInfo > GetFieldsFromValueTuple ( Type type )
2534+ {
2535+ Method = MapMethod . ByPosition ;
2536+ var fields = type . GetFields ( ) ;
2537+
2538+ // https://docs.microsoft.com/en-us/dotnet/api/system.valuetuple-8.rest
2539+ if ( fields . Length >= 8 )
2540+ throw new NotSupportedException ( "ValueTuple with more than 7 members not supported due to nesting; see https://docs.microsoft.com/en-us/dotnet/api/system.valuetuple-8.rest" ) ;
2541+
2542+ return fields ;
2543+ }
2544+
25182545 public bool HasAutoIncPK { get ; private set ; }
25192546
25202547 public void SetAutoIncPK ( object obj , long id )
@@ -2544,19 +2571,22 @@ public Column FindColumnWithPropertyName (string propertyName)
25442571
25452572 public Column FindColumn ( string columnName )
25462573 {
2574+ if ( Method != MapMethod . ByName )
2575+ throw new InvalidOperationException ( $ "This { nameof ( TableMapping ) } is not mapped by name, but { Method } .") ;
2576+
25472577 var exact = Columns . FirstOrDefault ( c => c . Name . ToLower ( ) == columnName . ToLower ( ) ) ;
25482578 return exact ;
25492579 }
25502580
25512581 public class Column
25522582 {
2553- PropertyInfo _prop ;
2583+ MemberInfo _member ;
25542584
25552585 public string Name { get ; private set ; }
25562586
2557- public PropertyInfo PropertyInfo => _prop ;
2587+ public PropertyInfo PropertyInfo => _member as PropertyInfo ;
25582588
2559- public string PropertyName { get { return _prop . Name ; } }
2589+ public string PropertyName { get { return _member . Name ; } }
25602590
25612591 public Type ColumnType { get ; private set ; }
25622592
@@ -2575,59 +2605,95 @@ public class Column
25752605
25762606 public bool StoreAsText { get ; private set ; }
25772607
2578- public Column ( PropertyInfo prop , CreateFlags createFlags = CreateFlags . None )
2608+ public Column ( MemberInfo member , CreateFlags createFlags = CreateFlags . None )
25792609 {
2580- var colAttr = prop . CustomAttributes . FirstOrDefault ( x => x . AttributeType == typeof ( ColumnAttribute ) ) ;
2610+ _member = member ;
2611+ var memberType = GetMemberType ( member ) ;
25812612
2582- _prop = prop ;
2613+ var colAttr = member . CustomAttributes . FirstOrDefault ( x => x . AttributeType == typeof ( ColumnAttribute ) ) ;
25832614#if ENABLE_IL2CPP
25842615 var ca = prop . GetCustomAttribute ( typeof ( ColumnAttribute ) ) as ColumnAttribute ;
25852616 Name = ca == null ? prop . Name : ca . Name ;
25862617#else
25872618 Name = ( colAttr != null && colAttr . ConstructorArguments . Count > 0 ) ?
25882619 colAttr . ConstructorArguments [ 0 ] . Value ? . ToString ( ) :
2589- prop . Name ;
2620+ member . Name ;
25902621#endif
25912622 //If this type is Nullable<T> then Nullable.GetUnderlyingType returns the T, otherwise it returns null, so get the actual type instead
2592- ColumnType = Nullable . GetUnderlyingType ( prop . PropertyType ) ?? prop . PropertyType ;
2593- Collation = Orm . Collation ( prop ) ;
2623+ ColumnType = Nullable . GetUnderlyingType ( memberType ) ?? memberType ;
2624+ Collation = Orm . Collation ( member ) ;
25942625
2595- IsPK = Orm . IsPK ( prop ) ||
2626+ IsPK = Orm . IsPK ( member ) ||
25962627 ( ( ( createFlags & CreateFlags . ImplicitPK ) == CreateFlags . ImplicitPK ) &&
2597- string . Compare ( prop . Name , Orm . ImplicitPkName , StringComparison . OrdinalIgnoreCase ) == 0 ) ;
2628+ string . Compare ( member . Name , Orm . ImplicitPkName , StringComparison . OrdinalIgnoreCase ) == 0 ) ;
25982629
2599- var isAuto = Orm . IsAutoInc ( prop ) || ( IsPK && ( ( createFlags & CreateFlags . AutoIncPK ) == CreateFlags . AutoIncPK ) ) ;
2630+ var isAuto = Orm . IsAutoInc ( member ) || ( IsPK && ( ( createFlags & CreateFlags . AutoIncPK ) == CreateFlags . AutoIncPK ) ) ;
26002631 IsAutoGuid = isAuto && ColumnType == typeof ( Guid ) ;
26012632 IsAutoInc = isAuto && ! IsAutoGuid ;
26022633
2603- Indices = Orm . GetIndices ( prop ) ;
2634+ Indices = Orm . GetIndices ( member ) ;
26042635 if ( ! Indices . Any ( )
26052636 && ! IsPK
26062637 && ( ( createFlags & CreateFlags . ImplicitIndex ) == CreateFlags . ImplicitIndex )
26072638 && Name . EndsWith ( Orm . ImplicitIndexSuffix , StringComparison . OrdinalIgnoreCase )
26082639 ) {
26092640 Indices = new IndexedAttribute [ ] { new IndexedAttribute ( ) } ;
26102641 }
2611- IsNullable = ! ( IsPK || Orm . IsMarkedNotNull ( prop ) ) ;
2612- MaxStringLength = Orm . MaxStringLength ( prop ) ;
2642+ IsNullable = ! ( IsPK || Orm . IsMarkedNotNull ( member ) ) ;
2643+ MaxStringLength = Orm . MaxStringLength ( member ) ;
26132644
2614- StoreAsText = prop . PropertyType . GetTypeInfo ( ) . CustomAttributes . Any ( x => x . AttributeType == typeof ( StoreAsTextAttribute ) ) ;
2645+ StoreAsText = memberType . GetTypeInfo ( ) . CustomAttributes . Any ( x => x . AttributeType == typeof ( StoreAsTextAttribute ) ) ;
26152646 }
26162647
2648+ public Column ( PropertyInfo member , CreateFlags createFlags = CreateFlags . None )
2649+ : this ( ( MemberInfo ) member , createFlags )
2650+ { }
2651+
26172652 public void SetValue ( object obj , object val )
26182653 {
2619- if ( val != null && ColumnType . GetTypeInfo ( ) . IsEnum ) {
2620- _prop . SetValue ( obj , Enum . ToObject ( ColumnType , val ) ) ;
2654+ if ( _member is PropertyInfo propy )
2655+ {
2656+ if ( val != null && ColumnType . GetTypeInfo ( ) . IsEnum )
2657+ propy . SetValue ( obj , Enum . ToObject ( ColumnType , val ) ) ;
2658+ else
2659+ propy . SetValue ( obj , val ) ;
26212660 }
2622- else {
2623- _prop . SetValue ( obj , val , null ) ;
2661+ else if ( _member is FieldInfo field )
2662+ {
2663+ if ( val != null && ColumnType . GetTypeInfo ( ) . IsEnum )
2664+ field . SetValue ( obj , Enum . ToObject ( ColumnType , val ) ) ;
2665+ else
2666+ field . SetValue ( obj , val ) ;
26242667 }
2668+ else
2669+ throw new InvalidProgramException ( "unreachable condition" ) ;
26252670 }
26262671
26272672 public object GetValue ( object obj )
26282673 {
2629- return _prop . GetValue ( obj , null ) ;
2674+ if ( _member is PropertyInfo propy )
2675+ return propy . GetValue ( obj ) ;
2676+ else if ( _member is FieldInfo field )
2677+ return field . GetValue ( obj ) ;
2678+ else
2679+ throw new InvalidProgramException ( "unreachable condition" ) ;
26302680 }
2681+
2682+ private static Type GetMemberType ( MemberInfo m )
2683+ {
2684+ switch ( m . MemberType )
2685+ {
2686+ case MemberTypes . Property : return ( ( PropertyInfo ) m ) . PropertyType ;
2687+ case MemberTypes . Field : return ( ( FieldInfo ) m ) . FieldType ;
2688+ default : throw new InvalidProgramException ( $ "{ nameof ( TableMapping ) } supports properties or fields only.") ;
2689+ }
2690+ }
2691+ }
2692+
2693+ internal enum MapMethod
2694+ {
2695+ ByName ,
2696+ ByPosition
26312697 }
26322698 }
26332699
@@ -2836,7 +2902,7 @@ public static IEnumerable<IndexedAttribute> GetIndices (MemberInfo p)
28362902#endif
28372903 }
28382904
2839- public static int ? MaxStringLength ( PropertyInfo p )
2905+ public static int ? MaxStringLength ( MemberInfo p )
28402906 {
28412907#if ENABLE_IL2CPP
28422908 return p . GetCustomAttribute < MaxLengthAttribute > ( ) ? . Value ;
@@ -2850,6 +2916,8 @@ public static IEnumerable<IndexedAttribute> GetIndices (MemberInfo p)
28502916#endif
28512917 }
28522918
2919+ public static int ? MaxStringLength ( PropertyInfo p ) => MaxStringLength ( ( MemberInfo ) p ) ;
2920+
28532921 public static bool IsMarkedNotNull ( MemberInfo p )
28542922 {
28552923 return p . CustomAttributes . Any ( x => x . AttributeType == typeof ( NotNullAttribute ) ) ;
@@ -2938,11 +3006,19 @@ public IEnumerable<T> ExecuteDeferredQuery<T> (TableMapping map)
29383006 var cols = new TableMapping . Column [ SQLite3 . ColumnCount ( stmt ) ] ;
29393007 var fastColumnSetters = new Action < T , Sqlite3Statement , int > [ SQLite3 . ColumnCount ( stmt ) ] ;
29403008
2941- for ( int i = 0 ; i < cols . Length ; i ++ ) {
2942- var name = SQLite3 . ColumnName16 ( stmt , i ) ;
2943- cols [ i ] = map . FindColumn ( name ) ;
2944- if ( cols [ i ] != null )
2945- fastColumnSetters [ i ] = FastColumnSetter . GetFastSetter < T > ( _conn , cols [ i ] ) ;
3009+ if ( map . Method == TableMapping . MapMethod . ByPosition )
3010+ {
3011+ Array . Copy ( map . Columns , cols , Math . Min ( cols . Length , map . Columns . Length ) ) ;
3012+ }
3013+ else if ( map . Method == TableMapping . MapMethod . ByName )
3014+ {
3015+ for ( int i = 0 ; i < cols . Length ; i ++ )
3016+ {
3017+ var name = SQLite3 . ColumnName16 ( stmt , i ) ;
3018+ cols [ i ] = map . FindColumn ( name ) ;
3019+ if ( cols [ i ] != null )
3020+ fastColumnSetters [ i ] = FastColumnSetter . GetFastSetter < T > ( _conn , cols [ i ] ) ;
3021+ }
29463022 }
29473023
29483024 while ( SQLite3 . Step ( stmt ) == SQLite3 . Result . Row ) {
0 commit comments