Skip to content
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

Get EmptyHttpResult in RDF via reflection #45878

Merged
merged 5 commits into from Jan 12, 2023
Merged

Conversation

captainsafia
Copy link
Member

Addresses #45063

@captainsafia captainsafia marked this pull request as ready for review January 6, 2023 02:16
@captainsafia captainsafia requested a review from a team January 6, 2023 02:16
// Due to https://github.com/dotnet/aspnetcore/issues/41330 we cannot reference the EmptyHttpResult type
// but users still need to assert on it as in https://github.com/dotnet/aspnetcore/issues/45063
// so we temporarily work around this here by using reflection to get the actual type.
private static readonly NewExpression EmptyHttpResultValueTaskExpr = Type.GetType("Microsoft.AspNetCore.Http.Results, Microsoft.AspNetCore.Http.Results") is {} resultsType
Copy link
Member Author

Choose a reason for hiding this comment

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

This conditional primarily exists to support scenarios where RDF is instantiated in scenarios where the Results assembly isn't referenced (SignalR tests). This shouldn't impact scenarios where users are running this from the framework.

Copy link
Member

Choose a reason for hiding this comment

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

Instead of using Expression.New(typeof(ValueTask<object>)) which I think would result in a null being serialized to the response, can we make this throw lazily if we ever actually try building an expression tree with this and we cannot find the resultsType? Or maybe just continue using the private version of EmptyHttpResult?

I know this should only affect tests, but it's probably better to either make it throw or make it work to make debugging our tests easier in the future. I think throwing is probably the best option. We can add dependencies to any tests that need to use this NewExpression which will make them more real-world anyway.

@@ -2154,32 +2159,34 @@ static async Task ExecuteAwaited(ValueTask task)

private static ValueTask<object?> ExecuteTaskWithEmptyResult(Task task)
{
Debug.Assert(EmptyHttpResultInstance is not null);
Copy link
Member

Choose a reason for hiding this comment

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

Is not possible to it be null in the SignalR scenario? Or, maybe the SignalR scenario is edge case that should never happen in real world?

Copy link
Member

Choose a reason for hiding this comment

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

I assume this would be null in our SignalR tests currently since they don't reference the Results assembly, but I don't think the signalr tests are hitting this code path.

That said, we run our tests in release on the CI, so it might be better to reference a property that's basically just => EmptyHttpResultInstance ?? throw new UnreachableException("The EmptyHttpResult type could not be found."). instead of using EmptyHttpResultInstance directly.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yep, the problem in the SignalR tests was the resolution that was happening for the assembly in the static properties. I cleaned this up to account for Stephen's feedback and reused the same EmptyHttpResult.Instance variable when constructing the property.

// Due to https://github.com/dotnet/aspnetcore/issues/41330 we cannot reference the EmptyHttpResult type
// but users still need to assert on it as in https://github.com/dotnet/aspnetcore/issues/45063
// so we temporarily work around this here by using reflection to get the actual type.
private static readonly NewExpression EmptyHttpResultValueTaskExpr = Type.GetType("Microsoft.AspNetCore.Http.Results, Microsoft.AspNetCore.Http.Results") is {} resultsType
Copy link
Member

Choose a reason for hiding this comment

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

Instead of using Expression.New(typeof(ValueTask<object>)) which I think would result in a null being serialized to the response, can we make this throw lazily if we ever actually try building an expression tree with this and we cannot find the resultsType? Or maybe just continue using the private version of EmptyHttpResult?

I know this should only affect tests, but it's probably better to either make it throw or make it work to make debugging our tests easier in the future. I think throwing is probably the best option. We can add dependencies to any tests that need to use this NewExpression which will make them more real-world anyway.

@@ -2154,32 +2159,34 @@ static async Task ExecuteAwaited(ValueTask task)

private static ValueTask<object?> ExecuteTaskWithEmptyResult(Task task)
{
Debug.Assert(EmptyHttpResultInstance is not null);
Copy link
Member

Choose a reason for hiding this comment

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

I assume this would be null in our SignalR tests currently since they don't reference the Results assembly, but I don't think the signalr tests are hitting this code path.

That said, we run our tests in release on the CI, so it might be better to reference a property that's basically just => EmptyHttpResultInstance ?? throw new UnreachableException("The EmptyHttpResult type could not be found."). instead of using EmptyHttpResultInstance directly.

Copy link
Member

@halter73 halter73 left a comment

Choose a reason for hiding this comment

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

I would still prefer if this threw in release builds if EmptyHttpResultInstance is null.

@captainsafia
Copy link
Member Author

I would still prefer if this threw in release builds if EmptyHttpResultInstance is null.

Ah, I misunderstood what you're previous feedback was. I thought you meant throw exceptions in all builds (which would impact our local test scenario). I can add an if-def to throw on RELEASE builds.

@captainsafia captainsafia merged commit fdd35b0 into main Jan 12, 2023
@captainsafia captainsafia deleted the safia/empty-result branch January 12, 2023 02:14
@ghost ghost added this to the 8.0-preview1 milestone Jan 12, 2023
@captainsafia
Copy link
Member Author

/backport to release/7.0

@github-actions
Copy link
Contributor

Started backporting to release/7.0: https://github.com/dotnet/aspnetcore/actions/runs/3898500659

@github-actions
Copy link
Contributor

@captainsafia backporting to release/7.0 failed, the patch most likely resulted in conflicts:

$ git am --3way --ignore-whitespace --keep-non-patch changes.patch

Applying: Get EmptyHttpResult in RDF via reflection
Using index info to reconstruct a base tree...
M	src/Http/Http.Extensions/src/RequestDelegateFactory.cs
M	src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs
Falling back to patching base and 3-way merge...
Auto-merging src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs
CONFLICT (content): Merge conflict in src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs
Auto-merging src/Http/Http.Extensions/src/RequestDelegateFactory.cs
CONFLICT (content): Merge conflict in src/Http/Http.Extensions/src/RequestDelegateFactory.cs
error: Failed to merge in the changes.
hint: Use 'git am --show-current-patch=diff' to see the failed patch
Patch failed at 0001 Get EmptyHttpResult in RDF via reflection
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".
Error: The process '/usr/bin/git' failed with exit code 128

Please backport manually!

@github-actions
Copy link
Contributor

@captainsafia an error occurred while backporting to release/7.0, please check the run log for details!

Error: git am failed, most likely due to a merge conflict.

captainsafia added a commit that referenced this pull request Jan 12, 2023
* Get EmptyHttpResult in RDF via reflection

* Always use instance property for checks

* Favor Debug.Assert instead of early exception

* Throw exception in RELEASE builds

* Fix ifdef
dougbu pushed a commit that referenced this pull request Jan 12, 2023
* Get EmptyHttpResult in RDF via reflection (#45878)
* Get EmptyHttpResult in RDF via reflection
* Always use instance property for checks
* Favor Debug.Assert instead of early exception
* Throw exception in RELEASE builds
* Fix ifdef
* Add HttpResults using
@amcasey amcasey added the area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions label 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.

None yet

4 participants