@@ -3032,6 +3032,167 @@ public void ShortName_on_compiler_generated_type3()
30323032 Assert . Equal ( "__AnonymousType01Child" , entityType . ShortName ( ) ) ;
30333033 }
30343034
3035+ [ ConditionalTheory ]
3036+ // file class MyEntity in Program.cs
3037+ [ InlineData ( "<Program>F1234ABCD__MyEntity" , "MyEntity" ) ]
3038+ // file class declared in a file whose name itself contains "__"
3039+ [ InlineData ( "<My__File>F1234ABCD__MyEntity" , "MyEntity" ) ]
3040+ // file class whose user-chosen name contains "__"
3041+ [ InlineData ( "<Program>F1234ABCD__Foo__Bar" , "Foo__Bar" ) ]
3042+ // file class with generic type parameters
3043+ [ InlineData ( "<Program>F1234ABCD__MyEntity<int>" , "MyEntity" ) ]
3044+ public void ShortName_on_file_scoped_type ( string clrName , string expectedShortName )
3045+ {
3046+ var model = CreateModel ( ) ;
3047+
3048+ var assemblyName = new AssemblyName ( "DynamicEntityClrTypeAssembly_FileScoped" ) ;
3049+ var assemblyBuilder = AssemblyBuilder . DefineDynamicAssembly ( assemblyName , AssemblyBuilderAccess . Run ) ;
3050+ var moduleBuilder = assemblyBuilder . DefineDynamicModule ( "MyModule" ) ;
3051+ var typeBuilder = moduleBuilder . DefineType ( clrName ) ;
3052+ var type = typeBuilder . CreateType ( ) ;
3053+
3054+ model . AddEntityType ( type ) ;
3055+
3056+ var entityType = model . FinalizeModel ( ) . FindEntityType ( clrName ) ;
3057+
3058+ Assert . Equal ( expectedShortName , entityType . ShortName ( ) ) ;
3059+ }
3060+
3061+ [ ConditionalTheory ]
3062+ // Regular type — no `<` prefix, no transformation
3063+ [ InlineData ( "Foo" , "Foo" ) ]
3064+ // Type whose user-chosen name contains "__" but is not file-scoped — no `<` prefix, no transformation
3065+ [ InlineData ( "Foo__Bar" , "Foo__Bar" ) ]
3066+ // Generic type — existing logic still strips generics from the tail
3067+ [ InlineData ( "MyType<int>" , "MyType" ) ]
3068+ // Generic type whose name contains "__"
3069+ [ InlineData ( "Foo__Bar<int>" , "Foo__Bar" ) ]
3070+ public void ShortName_unchanged_for_regular_types ( string clrName , string expectedShortName )
3071+ {
3072+ var model = CreateModel ( ) ;
3073+
3074+ var assemblyName = new AssemblyName ( "DynamicEntityClrTypeAssembly_Regular" ) ;
3075+ var assemblyBuilder = AssemblyBuilder . DefineDynamicAssembly ( assemblyName , AssemblyBuilderAccess . Run ) ;
3076+ var moduleBuilder = assemblyBuilder . DefineDynamicModule ( "MyModule" ) ;
3077+ var typeBuilder = moduleBuilder . DefineType ( clrName ) ;
3078+ var type = typeBuilder . CreateType ( ) ;
3079+
3080+ model . AddEntityType ( type ) ;
3081+
3082+ var entityType = model . FinalizeModel ( ) . FindEntityType ( clrName ) ;
3083+
3084+ Assert . Equal ( expectedShortName , entityType . ShortName ( ) ) ;
3085+ }
3086+
3087+ [ ConditionalTheory ]
3088+ // file class MyEntity in Program.cs
3089+ [ InlineData ( "<Program>F1234ABCD__MyEntity" , "MyEntity" ) ]
3090+ // file class declared in a file whose name itself contains "__"
3091+ [ InlineData ( "<My__File>F1234ABCD__MyEntity" , "MyEntity" ) ]
3092+ // file class whose user-chosen name contains "__"
3093+ [ InlineData ( "<Program>F1234ABCD__Foo__Bar" , "Foo__Bar" ) ]
3094+ // file class with single generic type parameter — DisplayName preserves generics (unlike ShortName)
3095+ [ InlineData ( "<Program>F1234ABCD__MyEntity<int>" , "MyEntity<int>" ) ]
3096+ // file class with nested generics
3097+ [ InlineData ( "<Program>F1234ABCD__Wrapper<List<int>>" , "Wrapper<List<int>>" ) ]
3098+ // file class used as a generic argument inside another type — sentinel must be stripped from the inner position too
3099+ [ InlineData ( "List<<Program>F1234ABCD__MyFileClass>" , "List<MyFileClass>" ) ]
3100+ // generic of generic, with a file-scoped type at the inner-inner position
3101+ [ InlineData ( "List<List<<Program>F1234ABCD__Inner>>" , "List<List<Inner>>" ) ]
3102+ // short hex digest (Roslyn varies digest length)
3103+ [ InlineData ( "<Program>F12__Foo" , "Foo" ) ]
3104+ // long hex digest
3105+ [ InlineData ( "<Program>FABCDEF1234567890__Foo" , "Foo" ) ]
3106+ // file class whose user-chosen name starts with an underscore
3107+ [ InlineData ( "<Program>F1234ABCD___Underscored" , "_Underscored" ) ]
3108+ public void DisplayName_on_file_scoped_type ( string clrName , string expectedDisplayName )
3109+ {
3110+ var model = CreateModel ( ) ;
3111+
3112+ var assemblyName = new AssemblyName ( "DynamicEntityClrTypeAssembly_FileScopedDisplay" ) ;
3113+ var assemblyBuilder = AssemblyBuilder . DefineDynamicAssembly ( assemblyName , AssemblyBuilderAccess . Run ) ;
3114+ var moduleBuilder = assemblyBuilder . DefineDynamicModule ( "MyModule" ) ;
3115+ var typeBuilder = moduleBuilder . DefineType ( clrName ) ;
3116+ var type = typeBuilder . CreateType ( ) ;
3117+
3118+ model . AddEntityType ( type ) ;
3119+
3120+ var entityType = model . FinalizeModel ( ) . FindEntityType ( clrName ) ;
3121+
3122+ Assert . Equal ( expectedDisplayName , entityType . DisplayName ( ) ) ;
3123+ }
3124+
3125+ [ ConditionalTheory ]
3126+ // Regular type — no `<` prefix, no transformation
3127+ [ InlineData ( "Foo" , "Foo" ) ]
3128+ // Type whose user-chosen name contains "__" but is not file-scoped
3129+ [ InlineData ( "Foo__Bar" , "Foo__Bar" ) ]
3130+ // Generic type — DisplayName preserves generics (unlike ShortName)
3131+ [ InlineData ( "MyType<int>" , "MyType<int>" ) ]
3132+ // Generic type whose name contains "__"
3133+ [ InlineData ( "Foo__Bar<int>" , "Foo__Bar<int>" ) ]
3134+ // Anonymous-style synthesized name (`<>`) — not file-scoped, untouched by file-scoped branch
3135+ [ InlineData ( "<>__AnonymousType01Child" , "<>__AnonymousType01Child" ) ]
3136+ // Closure display class — `<>c` prefix, not `>F`, untouched
3137+ [ InlineData ( "<>c__DisplayClass0_0" , "<>c__DisplayClass0_0" ) ]
3138+ // Async state machine — `>d` signature, not `>F`, untouched
3139+ [ InlineData ( "<MyMethod>d__0" , "<MyMethod>d__0" ) ]
3140+ // Local function — `>g` signature, not `>F`, untouched
3141+ [ InlineData ( "<MyMethod>g__Local|0_0" , "<MyMethod>g__Local|0_0" ) ]
3142+ // Lowercase `f` — Roslyn anonymous-type marker, not file-scoped (`F` is uppercase). Untouched.
3143+ [ InlineData ( "<Program>f1234__NotFileScoped" , "<Program>f1234__NotFileScoped" ) ]
3144+ public void DisplayName_unchanged_for_non_file_scoped_types ( string clrName , string expectedDisplayName )
3145+ {
3146+ var model = CreateModel ( ) ;
3147+
3148+ var assemblyName = new AssemblyName ( "DynamicEntityClrTypeAssembly_DisplayRegular" ) ;
3149+ var assemblyBuilder = AssemblyBuilder . DefineDynamicAssembly ( assemblyName , AssemblyBuilderAccess . Run ) ;
3150+ var moduleBuilder = assemblyBuilder . DefineDynamicModule ( "MyModule" ) ;
3151+ var typeBuilder = moduleBuilder . DefineType ( clrName ) ;
3152+ var type = typeBuilder . CreateType ( ) ;
3153+
3154+ model . AddEntityType ( type ) ;
3155+
3156+ var entityType = model . FinalizeModel ( ) . FindEntityType ( clrName ) ;
3157+
3158+ Assert . Equal ( expectedDisplayName , entityType . DisplayName ( ) ) ;
3159+ }
3160+
3161+ [ ConditionalTheory ]
3162+ // Has `>F` signature but no `__` separator — file-scoped sentinel is incomplete, leave alone
3163+ [ InlineData ( "<Program>F1234ABCD" , "<Program>F1234ABCD" ) ]
3164+ // `<` but no closing `>` — incomplete sentinel, leave alone
3165+ [ InlineData ( "<NoClose" , "<NoClose" ) ]
3166+ // Empty user portion after `__` — malformed Roslyn output, but ensure we don't crash;
3167+ // current behavior strips to empty (matches ShortName behavior; harmless edge case)
3168+ [ InlineData ( "<Program>F1234ABCD__" , "" ) ]
3169+ // Just `<>` — bounds check `closeAngle + 1 < name.Length` rejects this; no char follows the `>`.
3170+ [ InlineData ( "<>" , "<>" ) ]
3171+ public void DisplayName_handles_malformed_or_incomplete_file_scoped_inputs_safely ( string clrName , string expectedDisplayName )
3172+ {
3173+ var model = CreateModel ( ) ;
3174+
3175+ var assemblyName = new AssemblyName ( "DynamicEntityClrTypeAssembly_DisplayMalformed" ) ;
3176+ var assemblyBuilder = AssemblyBuilder . DefineDynamicAssembly ( assemblyName , AssemblyBuilderAccess . Run ) ;
3177+ var moduleBuilder = assemblyBuilder . DefineDynamicModule ( "MyModule" ) ;
3178+ var typeBuilder = moduleBuilder . DefineType ( clrName ) ;
3179+ var type = typeBuilder . CreateType ( ) ;
3180+
3181+ model . AddEntityType ( type ) ;
3182+
3183+ var entityType = model . FinalizeModel ( ) . FindEntityType ( clrName ) ;
3184+
3185+ Assert . Equal ( expectedDisplayName , entityType . DisplayName ( ) ) ;
3186+ }
3187+
3188+ [ ConditionalFact ]
3189+ public void DisplayName_unchanged_for_well_known_types ( )
3190+ {
3191+ Assert . Equal ( "EntityTypeTest" , CreateModel ( ) . AddEntityType ( typeof ( EntityTypeTest ) ) . DisplayName ( ) ) ;
3192+ Assert . Equal ( "Customer" , CreateModel ( ) . AddEntityType ( typeof ( Customer ) ) . DisplayName ( ) ) ;
3193+ Assert . Equal ( "List<Customer>" , CreateModel ( ) . AddEntityType ( typeof ( List < Customer > ) ) . DisplayName ( ) ) ;
3194+ }
3195+
30353196 private readonly IMutableModel _model = BuildModel ( ) ;
30363197
30373198 private IMutableEntityType DependentType
0 commit comments