jburzynski / TypeGen

Single-class-per-file C# to TypeScript generator
MIT License
194 stars 55 forks source link

System.TypeLoadException with generic attribute on C# model (dotnet-typegen 5.0.1) #202

Open DBalashov opened 3 months ago

DBalashov commented 3 months ago

I use generic attribute on models for FluentValidation.Auto package with automatic model validation

Model:

[ExportTsInterface]
[Validator<UserWalletValidator>]
public sealed record JUserWalletEditable(int ID, string Name, string Address);
sealed class UserWalletValidator : AbstractValidator<JUserWalletEditable>
{
    static readonly Regex addressValidator = new("^T[A-Za-z1-9]{33,35}$");

    public UserWalletValidator()
    {
        RuleFor(f => f.Name).NotEmpty().MinimumLength(4).MaximumLength(64);
        RuleFor(f => f.Address).NotEmpty()
                               .MaximumLength(36)
                               .Custom((s, ctx) =>
                                       {
                                           if (!addressValidator.IsMatch(s))
                                               ctx.AddFailure("Invalid TRC20 address");
                                       });
    }
}

Run dotnet-typegen generate -c tgconfig.json and exception occured:

Generating files for project "."...
Unhandled exception: System.TypeLoadException: GenericArguments[0], 'User.Services.Validators.UserWalletValidator', on 'FluentValidation.Auto.ValidatorAttribute`1[T]' violates the constraint of type parameter 'T'.
   at System.ModuleHandle.ResolveType(QCallModule module, Int32 typeToken, IntPtr* typeInstArgs, Int32 typeInstCount, IntPtr* methodInstArgs, Int32 methodInstCount, ObjectHandleOnStack type)
   at System.ModuleHandle.ResolveTypeHandle(Int32 typeToken, RuntimeTypeHandle[] typeInstantiationContext, RuntimeTypeHandle[] methodInstantiationContext)
   at System.Reflection.RuntimeModule.ResolveType(Int32 metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments)
   at System.Reflection.CustomAttribute.FilterCustomAttributeRecord(MetadataToken caCtorToken, MetadataImport& scope, RuntimeModule decoratedModule, MetadataToken decoratedToken, RuntimeType attributeFilterType, Boolean mustBeInheritable, ListBuilder`1& derivedAttributes, RuntimeType& attributeType, IRuntimeMethodInfo& ctorWithParameters, Boolean& isVarArg)
   at System.Reflection.CustomAttribute.AddCustomAttributes(ListBuilder`1& attributes, RuntimeModule decoratedModule, Int32 decoratedMetadataToken, RuntimeType attributeFilterType, Boolean mustBeInheritable, ListBuilder`1 derivedAttributes)
   at System.Reflection.CustomAttribute.GetCustomAttributes(RuntimeType type, RuntimeType caType, Boolean inherit)
   at System.Attribute.GetCustomAttributes(MemberInfo element, Type attributeType, Boolean inherit)
   at System.Attribute.GetCustomAttribute(MemberInfo element, Type attributeType, Boolean inherit)
   at System.Reflection.CustomAttributeExtensions.GetCustomAttribute[T](MemberInfo element)
   at TypeGen.Core.Metadata.AttributeMetadataReader.GetAttribute[TAttribute](Type type) in C:\projects\typegen\src\TypeGen\TypeGen.Core\Metadata\AttributeMetadataReader.cs:line 13
   at TypeGen.Core.Extensions.TypeExtensions.HasExportAttribute(Type type, IMetadataReader reader) in C:\projects\typegen\src\TypeGen\TypeGen.Core\Extensions\TypeExtensions.cs:line 27
   at TypeGen.Core.Extensions.TypeExtensions.<>c__DisplayClass1_0.<GetExportMarkedTypes>b__0(Type t) in C:\projects\typegen\src\TypeGen\TypeGen.Core\Extensions\TypeExtensions.cs:line 43
   at System.Linq.Enumerable.WhereEnumerableIterator`1.MoveNext()
   at TypeGen.Core.Generator.Services.GenerationSpecProvider.GetGenerationSpec(IEnumerable`1 assemblies) in C:\projects\typegen\src\TypeGen\TypeGen.Core\Generator\Services\GenerationSpecProvider.cs:line 28
   at TypeGen.Core.Generator.Generator.Generate(IEnumerable`1 assemblies) in C:\projects\typegen\src\TypeGen\TypeGen.Core\Generator\Generator.cs:line 220
   at TypeGen.Cli.Ui.Presenter.GenerateSingle(String projectFolder, String configPath, String outputFolder) in C:\projects\typegen\src\TypeGen\TypeGen.Cli\Ui\Presenter.cs:line 108
   at TypeGen.Cli.Ui.Presenter.GeneratePrivate(Boolean verbose, IReadOnlyCollection`1 projectFolderPaths, IReadOnlyCollection`1 configPaths, String outputFolder) in C:\projects\typegen\src\TypeGen\TypeGen.Cli\Ui\Presenter.cs:line 78
   at TypeGen.Cli.Ui.Presenter.<>c__DisplayClass9_0.<Generate>b__0() in C:\projects\typegen\src\TypeGen\TypeGen.Cli\Ui\Presenter.cs:line 61
   at TypeGen.Cli.Ui.Validation.ValidationResult.Match(Action onSuccess, Action`1 onFailure) in C:\projects\typegen\src\TypeGen\TypeGen.Cli\Ui\Validation\ValidationResult.cs:line 32
   at TypeGen.Cli.Ui.Presenter.Generate(Boolean verbose, IReadOnlyCollection`1 projectFolderPaths, IReadOnlyCollection`1 configPaths, String outputFolder) in C:\projects\typegen\src\TypeGen\TypeGen.Cli\Ui\Presenter.cs:line 59
   at TypeGen.Cli.Application.<BuildCommandLine>b__5_1(Boolean v, List`1 p, List`1 c, String o) in C:\projects\typegen\src\TypeGen\TypeGen.Cli\Application.cs:line 98
   at System.CommandLine.Handler.<>c__DisplayClass5_0`4.<SetHandler>b__0(InvocationContext context)
   at System.CommandLine.Invocation.AnonymousCommandHandler.Invoke(InvocationContext context)
   at System.CommandLine.Invocation.AnonymousCommandHandler.InvokeAsync(InvocationContext context)
   at System.CommandLine.Invocation.InvocationPipeline.<>c__DisplayClass4_0.<<BuildInvocationChain>b__0>d.MoveNext()
--- End of stack trace from previous location ---
   at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c__DisplayClass17_0.<<UseParseErrorReporting>b__0>d.MoveNext()
--- End of stack trace from previous location ---
   at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c__DisplayClass12_0.<<UseHelp>b__0>d.MoveNext()
--- End of stack trace from previous location ---
   at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c__DisplayClass22_0.<<UseVersionOption>b__0>d.MoveNext()
--- End of stack trace from previous location ---
   at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c__DisplayClass19_0.<<UseTypoCorrections>b__0>d.MoveNext()
--- End of stack trace from previous location ---
   at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c.<<UseSuggestDirective>b__18_0>d.MoveNext()
--- End of stack trace from previous location ---
   at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c__DisplayClass16_0.<<UseParseDirective>b__0>d.MoveNext()
--- End of stack trace from previous location ---
   at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c.<<RegisterWithDotnetSuggest>b__5_0>d.MoveNext()
--- End of stack trace from previous location ---
   at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c__DisplayClass8_0.<<UseExceptionHandler>b__0>d.MoveNext()                                                                                                                                                                                                                                  

Config file:

{
    "assemblies": [
        "../Authentication.Services/bin/Debug/net8.0/Authentication.Services.dll",
        "../Common.Services/bin/Debug/net8.0/Common.Services.dll",
        "../DataLayer/bin/Debug/net8.0/DataLayer.dll",
        "../DealIn.Services/bin/Debug/net8.0/DealIn.Services.dll",
        "../Device.Services/bin/Debug/net8.0/Device.Services.dll",
        "../Deal.Services/bin/Debug/net8.0/Deal.Services.dll",
        "../User.Services/bin/Debug/net8.0/User.Services.dll",
        "../Admin.Services/bin/Debug/net8.0/Admin.Services.dll",
        "../Finance.Services/bin/Debug/net8.0/Finance.Services.dll",
        "../Messages/bin/Debug/net8.0/Messages.dll",
        "../Shared/bin/Debug/net8.0/Shared.dll",
        "../WebHost/bin/Debug/net8.0/WebHost.dll"
    ],
    "propertyNameConverters": [
        "../TypeGenHelpers/bin/Debug/net8.0/TypeGenHelpers.dll:TypeGenHelpers.TypeGenPropertyNameConverter"
    ],
    "typeNameConverters": [
        "../TypeGenHelpers/bin/Debug/net8.0/TypeGenHelpers.dll:TypeGenHelpers.TypeGenTypeNameConverter"
    ],
    "exportTypesAsInterfacesByDefault": true,
    "typeScriptFileExtension": "ts",
    "explicitPublicAccessor": true,
    "useTabCharacter": true,
    "outputPath": "../../frontend/src/helpers/client-models",
    "clearOutputDirectory": true,
    "customTypeMappings": {
        "System.Guid": "string",
        "System.TimeSpan": "string"
    },
    "typeUnionsForTypes": {
    }
}
petero-dk commented 5 days ago

I am pretty sure you could workaround this by making an intermediary type

Class ValidatorUserWalletValidator : Validator<UserWalletValidator> {}