asyncapi / saunter

Saunter is a code-first AsyncAPI documentation generator for dotnet.
https://www.asyncapi.com/
MIT License
199 stars 56 forks source link

MissingMethodException .ctor at System.RuntimeMethodHandle.IsCAVisibleFromDecoratedType #112

Closed rasert closed 2 years ago

rasert commented 3 years ago

When getting the "/asyncapi/asyncapi.json" endpoint, I am getting an error. It seems some kind of conflict with Swashbuckle that is installed too.

Saunter v0.7.1 .Net 5 Swashbuckle v6.1.4

This is the response:

{"error":".ctor","stack":" at System.RuntimeMethodHandle.IsCAVisibleFromDecoratedType(QCallTypeHandle attrTypeHandle, RuntimeMethodHandleInternal attrCtor, QCallTypeHandle sourceTypeHandle, QCallModule sourceModule)\r\n at System.Reflection.CustomAttribute.FilterCustomAttributeRecord(MetadataToken caCtorToken, MetadataImport& scope, RuntimeModule decoratedModule, MetadataToken decoratedToken, RuntimeType attributeFilterType, Boolean mustBeInheritable, ListBuilder1& derivedAttributes, RuntimeType& attributeType, IRuntimeMethodInfo& ctorWithParameters, Boolean& isVarArg)\r\n at System.Reflection.CustomAttribute.AddCustomAttributes(ListBuilder1& attributes, RuntimeModule decoratedModule, Int32 decoratedMetadataToken, RuntimeType attributeFilterType, Boolean mustBeInheritable, ListBuilder1 derivedAttributes)\r\n at System.Reflection.CustomAttribute.GetCustomAttributes(RuntimeType type, RuntimeType caType, Boolean inherit)\r\n at System.Attribute.GetCustomAttributes(MemberInfo element, Type type, Boolean inherit)\r\n at System.Attribute.GetCustomAttribute(MemberInfo element, Type attributeType, Boolean inherit)\r\n at System.Reflection.CustomAttributeExtensions.GetCustomAttribute[T](MemberInfo element)\r\n at Saunter.Generation.AsyncApiDocumentProvider.<>c__DisplayClass3_0.<GetAsyncApiTypes>b__2(TypeInfo t)\r\n at System.Linq.Enumerable.WhereArrayIterator1.MoveNext()\r\n at System.Collections.Generic.LargeArrayBuilder1.AddRange(IEnumerable1 items)\r\n at System.Collections.Generic.SparseArrayBuilder1.ReserveOrAdd(IEnumerable1 items)\r\n at System.Linq.Enumerable.SelectManySingleSelectorIterator`2.ToArray()\r\n at Saunter.Generation.AsyncApiDocumentProvider.GetAsyncApiTypes(AsyncApiOptions options, AsyncApiDocument prototype)\r\n at Saunter.Generation.AsyncApiDocumentProvider.GetDocument(AsyncApiOptions options, AsyncApiDocument prototype)\r\n at Saunter.AsyncApiMiddleware.Invoke(HttpContext context)\r\n at Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)\r\n at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)\r\n at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)\r\n at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)\r\n at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)\r\n at Microsoft.AspNetCore.Localization.RequestLocalizationMiddleware.Invoke(HttpContext context)\r\n at Integration.ProductsPrice.Middlewares.ExceptionHandlerMiddleware.Invoke(HttpContext context, IWebHostEnvironment environment) in C:\Users\jmourao\workspace\MotorPromocao\poc-eks-motor\src\Integration.ProductsPrice\Middlewares\ExceptionHandlerMiddleware.cs:line 56","innerError":null,"innerException":null}

m-wild commented 3 years ago

Based on the stack trace, I think the swagger thing is a red herring.

Are you able to remove/comment-out the swagger middleware and confirm the issue still happens? This will help me to diagnose the issue. Thanks :)

rasert commented 3 years ago

Maybe it is not Swashbuckle. I removed it and I am still getting errors:

{"error":".ctor","stack":" at System.RuntimeMethodHandle.IsCAVisibleFromDecoratedType(QCallTypeHandle attrTypeHandle, RuntimeMethodHandleInternal attrCtor, QCallTypeHandle sourceTypeHandle, QCallModule sourceModule)\r\n at System.Reflection.CustomAttribute.FilterCustomAttributeRecord(MetadataToken caCtorToken, MetadataImport& scope, RuntimeModule decoratedModule, MetadataToken decoratedToken, RuntimeType attributeFilterType, Boolean mustBeInheritable, ListBuilder1& derivedAttributes, RuntimeType& attributeType, IRuntimeMethodInfo& ctorWithParameters, Boolean& isVarArg)\r\n at System.Reflection.CustomAttribute.AddCustomAttributes(ListBuilder1& attributes, RuntimeModule decoratedModule, Int32 decoratedMetadataToken, RuntimeType attributeFilterType, Boolean mustBeInheritable, ListBuilder1 derivedAttributes)\r\n at System.Reflection.CustomAttribute.GetCustomAttributes(RuntimeType type, RuntimeType caType, Boolean inherit)\r\n at System.Attribute.GetCustomAttributes(MemberInfo element, Type type, Boolean inherit)\r\n at System.Attribute.GetCustomAttribute(MemberInfo element, Type attributeType, Boolean inherit)\r\n at System.Reflection.CustomAttributeExtensions.GetCustomAttribute[T](MemberInfo element)\r\n at Saunter.Generation.AsyncApiDocumentProvider.<>c__DisplayClass3_0.<GetAsyncApiTypes>b__2(TypeInfo t)\r\n at System.Linq.Enumerable.WhereArrayIterator1.MoveNext()\r\n at System.Collections.Generic.LargeArrayBuilder1.AddRange(IEnumerable1 items)\r\n at System.Collections.Generic.SparseArrayBuilder1.ReserveOrAdd(IEnumerable1 items)\r\n at System.Linq.Enumerable.SelectManySingleSelectorIterator`2.ToArray()\r\n at Saunter.Generation.AsyncApiDocumentProvider.GetAsyncApiTypes(AsyncApiOptions options, AsyncApiDocument prototype)\r\n at Saunter.Generation.AsyncApiDocumentProvider.GetDocument(AsyncApiOptions options, AsyncApiDocument prototype)\r\n at Saunter.AsyncApiMiddleware.Invoke(HttpContext context)\r\n at Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)\r\n at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)\r\n at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)\r\n at Microsoft.AspNetCore.Localization.RequestLocalizationMiddleware.Invoke(HttpContext context)\r\n at Integration.ProductsPrice.Middlewares.ExceptionHandlerMiddleware.Invoke(HttpContext context, IWebHostEnvironment environment) in C:\Users\jmourao\workspace\MotorPromocao\poc-eks-motor\src\Integration.ProductsPrice\Middlewares\ExceptionHandlerMiddleware.cs:line 56","innerError":null,"innerException":null}

rasert commented 3 years ago

My message-bus class, that has the Attributes (AsyncApi, Channel, PublishOperation), is in a different assembly that is referenced by the asp.net web api. Is it a problem?

m-wild commented 3 years ago

Nope not a problem, you need to specify one of the types in The options.AssemblyMarkerTypes so the assembly is loaded though. What do you have in your startup.cs?

rasert commented 3 years ago

                /// <summary>
        /// This method gets called by the runtime. Use this method to add services to the container.
        /// </summary>
        /// <param name="services"></param>
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddRouting()
                .AddLocalization()
                .AddCors();
            services.AddControllers()
                .AddControllersAsServices()
                .AddFluentValidation(options =>
                {
                    options.RegisterValidatorsFromAssemblyContaining<DeltaValidator>();
                    options.RegisterValidatorsFromAssemblyContaining<NewPriceEventValidator>();
                    options.ImplicitlyValidateChildProperties = true;
                })
                .AddDapr(); // Register DaprClient on DI
            services.ConfigureApplicationServices(Configuration);
            services
                .AddHealthChecks()
                .AddCheck<GcamApiHealthCheck>("gcam", HealthStatus.Unhealthy)
                .AddCheck<LinxDbHealthCheck>("linxdb", HealthStatus.Unhealthy);

            services.Configure<ForwardedHeadersOptions>(options =>
            {
                options.ForwardLimit = 2;
                options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
            });

            services.AddSwaggerGen(options =>
            {
                options.SwaggerDoc("v1", new OpenApiInfo
                {
                    Title = "PriceBible",
                    Version = "v1"
                });
            });

            // Add Saunter to the application services. 
            services.AddAsyncApiSchemaGeneration(options =>
            {
                // Specify example type(s) from assemblies to scan.
                options.AssemblyMarkerTypes = new[] { typeof(NewPriceMessageBus) };

                // Build as much (or as little) of the AsyncApi document as you like.
                // Saunter will generate Channels, Operations, Messages, etc, but you
                // may want to specify Info here.
                options.AsyncApi = new AsyncApiDocument
                {
                    Info = new Info("NewPrice API", "1.0.0")
                    {
                        Description = "NewPrice API - Tells when products received new prices.",
                        License = new License("Apache 2.0")
                        {
                            Url = "https://www.apache.org/licenses/LICENSE-2.0"
                        }
                    },
                    Servers =
                    {
                        { "my-rabbit-server", new Server("rabbitmq.local.com", "amqp") }
                    }
                };
            });
        }

        /// <summary>
        /// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 
        /// </summary>
        /// <param name="builder"></param>
        public void Configure(IApplicationBuilder builder)
        {
            builder.UseForwardedHeaders();
            builder.UseMiddleware<ExceptionHandlerMiddleware>();
            builder.UseRouting();
            builder.UseCloudEvents(); // For Dapr Pub/Sub
            builder.UseRequestLocalization();
            builder.UseCors();
            builder.UseAuthentication();
            builder.UseAuthorization();
            builder.UseSwagger();
            builder.UseSwaggerUI(options =>
            {
                options.SwaggerEndpoint("v1/swagger.json", "PriceBible");
            });

            builder.UseEndpoints(endpoint =>
            {
                endpoint.MapHealthChecks("api/health", new HealthCheckOptions
                {
                    ResponseWriter = WriteHealthCheckResponse
                });
                endpoint.MapControllers();
                endpoint.MapSubscribeHandler(); // For Dapr Pub/Sub
                endpoint.MapAsyncApiDocuments();
                                endpoint.MapAsyncApiUi();
            });
        }
m-wild commented 3 years ago

That looks fine. Still trying to understand what has happened...

The code that is failing from Saunter's side is this

private static TypeInfo[] GetAsyncApiTypes(AsyncApiOptions options, AsyncApiDocument prototype)
{
    var assembliesToScan = options.AssemblyMarkerTypes.Select(t => t.Assembly);

    var asyncApiTypes = assembliesToScan
        .SelectMany(a => a.DefinedTypes.Where(t => t.GetCustomAttribute<AsyncApiAttribute>()?.DocumentName == prototype.DocumentName))
        .ToArray();

    return asyncApiTypes;
}

Which is really not doing anything special... We're listing all the types in that Assembly, then calling Type.GetCustomAttribute<AsyncApiAttribute>() which I can't see why that would fail. That's why I'm guessing there is something strange about that assembly.

m-wild commented 3 years ago

Hi just checking in if you managed to resolve your issue, or if there is anything else I can do to help? Thanks

m-wild commented 2 years ago

Closing due to inactivity. Feel free to re-open this issue if required.