@@ -59,12 +59,13 @@ protected static void WriteTypeAccessorSource(SourceProductionContext context, E
5959 ? InitializationMode . ObjectInitializer
6060 : InitializationMode . Constructor ;
6161
62+ var classIgnored = GetClassIgnoredProperties ( typeAttributes ) ;
6263 var propertySymbols = GetProperties ( targetSymbol ) ;
6364
6465 if ( mode == InitializationMode . ObjectInitializer )
6566 {
6667 var propertyArray = propertySymbols
67- . Select ( p => CreateProperty ( p ) )
68+ . Select ( p => CreateProperty ( p , classIgnored : classIgnored ) )
6869 . ToArray ( ) ;
6970
7071 return new EntityClass (
@@ -80,8 +81,11 @@ protected static void WriteTypeAccessorSource(SourceProductionContext context, E
8081
8182 // constructor initialization
8283
83- // constructor with same number of parameters as properties
84- var constructor = targetSymbol . Constructors . FirstOrDefault ( c => c . Parameters . Length == propertySymbols . Count ) ;
84+ // constructor with same number of parameters as mappable properties
85+ var mappableCount = propertySymbols
86+ . Count ( p => ! classIgnored . Contains ( p . Name ) && ! HasIgnorePropertyAttribute ( p . GetAttributes ( ) ) ) ;
87+
88+ var constructor = targetSymbol . Constructors . FirstOrDefault ( c => c . Parameters . Length == mappableCount ) ;
8589 if ( constructor == null )
8690 return null ;
8791
@@ -96,7 +100,7 @@ protected static void WriteTypeAccessorSource(SourceProductionContext context, E
96100 if ( parameter == null )
97101 continue ;
98102
99- var property = CreateProperty ( propertySymbol , parameter . Name ) ;
103+ var property = CreateProperty ( propertySymbol , parameter . Name , classIgnored : classIgnored ) ;
100104 properties . Add ( property ) ;
101105 }
102106
@@ -133,16 +137,17 @@ protected static List<IPropertySymbol> GetProperties(INamedTypeSymbol targetSymb
133137 currentSymbol = currentSymbol . BaseType ;
134138 }
135139
136- return properties . Values . ToList ( ) ;
140+ return [ .. properties . Values ] ;
137141 }
138142
139- protected static EntityProperty CreateProperty ( IPropertySymbol propertySymbol , string ? parameterName = null )
143+ protected static EntityProperty CreateProperty ( IPropertySymbol propertySymbol , string ? parameterName = null , HashSet < string > ? classIgnored = null )
140144 {
141145 var propertyType = propertySymbol . Type . ToDisplayString ( ) ;
142146 var memberTypeName = propertySymbol . Type . WithNullableAnnotation ( NullableAnnotation . NotAnnotated ) . ToDisplayString ( ) ;
143147 var propertyName = propertySymbol . Name ;
144148 var hasGetter = propertySymbol . GetMethod != null ;
145- var hasSetter = propertySymbol . SetMethod != null && ! propertySymbol . SetMethod . IsInitOnly ;
149+ var hasSetter = propertySymbol . SetMethod ? . IsInitOnly == false ;
150+ var isNotMapped = ( classIgnored ? . Contains ( propertyName ) == true ) || ! IsSupportedType ( propertySymbol . Type ) ;
146151
147152 var attributes = propertySymbol . GetAttributes ( ) ;
148153 if ( attributes == default || attributes . Length == 0 )
@@ -153,6 +158,7 @@ protected static EntityProperty CreateProperty(IPropertySymbol propertySymbol, s
153158 PropertyType : propertyType ,
154159 MemberTypeName : memberTypeName ,
155160 ParameterName : parameterName ,
161+ IsNotMapped : isNotMapped ,
156162 HasGetter : hasGetter ,
157163 HasSetter : hasSetter
158164 ) ;
@@ -162,7 +168,11 @@ protected static EntityProperty CreateProperty(IPropertySymbol propertySymbol, s
162168 var converterName = GetConverterName ( attributes ) ;
163169
164170 var isKey = HasDataAnnotationAttribute ( attributes , "KeyAttribute" ) ;
165- var isNotMapped = IsNotMapped ( attributes ) ;
171+
172+ isNotMapped = isNotMapped
173+ || IsNotMapped ( attributes )
174+ || HasIgnorePropertyAttribute ( attributes ) ;
175+
166176 var isDatabaseGenerated = GetIsDatabaseGenerated ( attributes ) ;
167177 var isConcurrencyCheck = HasDataAnnotationAttribute ( attributes , "ConcurrencyCheckAttribute" ) ;
168178 var foreignKey = GetSchemaAttributeConstructorStringArg ( attributes , "ForeignKeyAttribute" ) ;
@@ -335,7 +345,51 @@ private static bool GetIsDatabaseGenerated(ImmutableArray<AttributeData> attribu
335345
336346 protected static bool IsIncluded ( IPropertySymbol propertySymbol )
337347 {
338- return ! propertySymbol . IsIndexer && ! propertySymbol . IsAbstract && propertySymbol . DeclaredAccessibility == Accessibility . Public ;
348+ return ! propertySymbol . IsIndexer
349+ && ! propertySymbol . IsAbstract
350+ && propertySymbol . DeclaredAccessibility == Accessibility . Public ;
351+ }
352+
353+ private static bool IsSupportedType ( ITypeSymbol type )
354+ {
355+ // handle nullable value types
356+ if ( type is INamedTypeSymbol { OriginalDefinition . SpecialType : SpecialType . System_Nullable_T } namedType )
357+ return IsSupportedType ( namedType . TypeArguments [ 0 ] ) ;
358+
359+ // enums are stored as their underlying integer type
360+ if ( type . TypeKind == TypeKind . Enum )
361+ return true ;
362+
363+ // primitives and string
364+ switch ( type . SpecialType )
365+ {
366+ case SpecialType . System_Boolean :
367+ case SpecialType . System_Byte :
368+ case SpecialType . System_Char :
369+ case SpecialType . System_Decimal :
370+ case SpecialType . System_Double :
371+ case SpecialType . System_Single :
372+ case SpecialType . System_Int16 :
373+ case SpecialType . System_Int32 :
374+ case SpecialType . System_Int64 :
375+ case SpecialType . System_String :
376+ return true ;
377+ }
378+
379+ // byte[]
380+ if ( type is IArrayTypeSymbol { ElementType . SpecialType : SpecialType . System_Byte } )
381+ return true ;
382+
383+ // well-known struct types and FluentCommand.ConcurrencyToken
384+ var fullName = type . ToDisplayString ( ) ;
385+ return fullName is
386+ "System.DateTime" or
387+ "System.DateTimeOffset" or
388+ "System.Guid" or
389+ "System.TimeSpan" or
390+ "System.DateOnly" or
391+ "System.TimeOnly" or
392+ "FluentCommand.ConcurrencyToken" ;
339393 }
340394
341395 private static bool IsNotMapped ( ImmutableArray < AttributeData > attributes )
@@ -359,4 +413,54 @@ private static bool IsNotMapped(ImmutableArray<AttributeData> attributes)
359413 }
360414 } ) ;
361415 }
416+
417+ private static bool HasIgnorePropertyAttribute ( ImmutableArray < AttributeData > attributes )
418+ {
419+ return attributes . Any ( a => a . AttributeClass is
420+ {
421+ Name : "IgnorePropertyAttribute" ,
422+ ContainingNamespace :
423+ {
424+ Name : "Attributes" ,
425+ ContainingNamespace . Name : "FluentCommand"
426+ }
427+ } ) ;
428+ }
429+
430+ private static HashSet < string > GetClassIgnoredProperties ( ImmutableArray < AttributeData > attributes )
431+ {
432+ var ignored = new HashSet < string > ( StringComparer . Ordinal ) ;
433+
434+ foreach ( var attr in attributes )
435+ {
436+ if ( attr . AttributeClass is not
437+ {
438+ Name : "IgnorePropertyAttribute" ,
439+ ContainingNamespace :
440+ {
441+ Name : "Attributes" ,
442+ ContainingNamespace . Name : "FluentCommand"
443+ }
444+ } )
445+ {
446+ continue ;
447+ }
448+
449+ // constructor argument: [IgnoreProperty("Name")] or [IgnoreProperty(nameof(T.Name))]
450+ if ( attr . ConstructorArguments . Length > 0 && attr . ConstructorArguments [ 0 ] . Value is string ctorName )
451+ {
452+ ignored . Add ( ctorName ) ;
453+ continue ;
454+ }
455+
456+ // named argument: [IgnoreProperty(PropertyName = "Name")]
457+ foreach ( var namedArg in attr . NamedArguments )
458+ {
459+ if ( namedArg . Key == "PropertyName" && namedArg . Value . Value is string namedValue )
460+ ignored . Add ( namedValue ) ;
461+ }
462+ }
463+
464+ return ignored ;
465+ }
362466}
0 commit comments