Skip to content

Implement RouteHandlerServices.Map and use in RequestDelegateGenerator #46180

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jan 23, 2023

Conversation

captainsafia
Copy link
Member

Addresses #46163

  • Adds a RouteHandlerServices.Map implementation per API review above
  • Remove SourceGeneratorEndpointDataSource implementation in RequestDelegateGenerator's output
  • Make IEndpointRouteBuilder.GetOrAddRouteEndpointDataSource internal to support invocation from RouteHandlerServices.Map
  • Update generated thunks to match signature expected by RouteHandlerServices.Map

@@ -174,239 +157,5 @@ internal static Task ExecuteObjectResult(object? obj, HttpContext httpContext)
}
}
}

{{GeneratedCodeAttribute}}
file class SourceGeneratedRouteEndpointDataSource : EndpointDataSource
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🥇

builder.Metadata.Add(new SourceKey{{StaticRouteHandlerModelEmitter.EmitSourceKey(endpoint)}});
if (options == null)
{
return new RequestDelegateMetadataResult { EndpointMetadata = new List<object>().AsReadOnly() };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:
Replace new List<object>().AsReadOnly() with ReadOnlyCollection<object>.Empty, at least 3 places can be changed

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL! Looks like this was added in 8.0? Very nice...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I just assumed it existed and saw that it did 😄
Didn't realize it's new!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made the same comment, but you can use Array.Empty<object>() instead.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ended up using ReadOnlyCollection<object>. In the interest of readability it does a better job of communicating the type passed to the initializer here and I don't think there's any memory implications with that singleton.

Copy link
Member

@eerhardt eerhardt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is awesome work, @captainsafia.

I feel like I'm better able to give a decent code review now that I can see the generated code.


};

%GENERATEDCODEATTRIBUTE%
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar comment as above. Do we need this attribute on these methods when the whole class is marked as generated?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noodled on our use of the GeneratedCode attribute here a little bit more. Since the primary goal of the attribute is to make it easier to inspect if a type is codegenned from metadata, I think it makes sense to only include it in types that could be referenced from user code. Specifically, this would be:

  • The SourceKey definition
  • The strongly-typed Map overloads

I refactored the code to apply it to these places. Note, I opted to remove it from the static class containing the strongly-typed Map overloads since it's unlikely that user code ends up resolving that directly.

Let me know if you think this philosophy makes sense

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we want the GeneratedCodeAttribute on all the classes we generate in this file.

The reason is so things like analyzers, code-style, and code coverage tools don't warn/report things from inside this code. The devs can't change this code, so they shouldn't see tools talking about.

Even for the internal static class GenerateRouteBuilderEndpoints class. The Regex generator puts it on the partial methods, and not the class, because the class is partial to the user's code. So it would be incorrect to put it on the whole class, since the user writes code in this class too.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Regex generator puts it on the partial methods, and not the class, because the class is partial to the user's code. So it would be incorrect to put it on the whole class, since the user writes code in this class too.

This insights makes sense to me.

Here's the modification I made:

  • Moved the GenerateRouteBuilderEndpoints to the code generated in the Microsoft.AspNetCore.Builder namespace to fix the global:: using issue
  • Added the GeneratedCode attribute to the GenerateRouteBuilderEndpoints class

So, all the classes we codegen inside that namespacce are marked.

Classes marked as file private are not marked, which I assume is fine.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Classes marked as file private are not marked, which I assume is fine.

I think they should be marked as well. That way they don't show up in code coverage reports, that devs can't do anything about.

// </auto-generated>
//------------------------------------------------------------------------------
#nullable enable
using global::Microsoft.AspNetCore.Http;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's this using left for? I assume it shouldn't be needed, since everything outside of the namespace Microsoft.AspNetCore.Http.Generated should be fully qualified.


};

%GENERATEDCODEATTRIBUTE%
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we want the GeneratedCodeAttribute on all the classes we generate in this file.

The reason is so things like analyzers, code-style, and code coverage tools don't warn/report things from inside this code. The devs can't change this code, so they shouldn't see tools talking about.

Even for the internal static class GenerateRouteBuilderEndpoints class. The Regex generator puts it on the partial methods, and not the class, because the class is partial to the user's code. So it would be incorrect to put it on the whole class, since the user writes code in this class too.

Copy link
Member

@eerhardt eerhardt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thanks for fixing all my nits. :) I think the code gen looks really clean now.

Comment on lines +157 to +165
private static void PopulateMetadata<T>(MethodInfo method, EndpointBuilder builder) where T : IEndpointMetadataProvider
{
T.PopulateMetadata(method, builder);
}

private static void PopulateMetadata<T>(ParameterInfo parameter, EndpointBuilder builder) where T : IEndpointParameterMetadataProvider
{
T.PopulateMetadata(parameter, builder);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see these methods used. I assume they will be used in the future. Not a huge deal, but in the future we might want to only generate them if they are actually being used.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes 🙊 ... we'll eventually use this when we add support for populating metadata for types that implement the IEndpointMetadataProvider interfaces. I can remove it in the next PR...

@captainsafia captainsafia enabled auto-merge (squash) January 23, 2023 19:44
@captainsafia captainsafia merged commit c5a59c4 into main Jan 23, 2023
@captainsafia captainsafia deleted the safia/map-rdsg branch January 23, 2023 20:27
@ghost ghost added this to the 8.0-preview1 milestone Jan 23, 2023
atossell91 pushed a commit to atossell91/aspnetcore that referenced this pull request Jan 23, 2023
dotnet#46180)

* Implement RouteHandlerServices.Map and use in RequestDelegateGenerator

* Tweak generated code

* Remove warnings from generated code

* Fix docstring for RouteHandlerServices.Map

* Add baseline tests and fix global:: issue

* Clean up generated code

* Add HTTP verb caching and fix usings

* Use FQN and remove GeneratedCode attribute
@amcasey amcasey added area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions and removed area-runtime labels Aug 25, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants