Skip to content

Fix Create Property Model Name for Complex Objects #62459

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

sami-daniel
Copy link

@sami-daniel sami-daniel commented Jun 25, 2025

Fix Create Property Model Name for Complex Objects

Description

Added an option to optimize model names where the model name is the same
as one of its properties when binding the model name. This should only
be used when this is the desired behavior. Tests have been added to
assert this behavior when passing parameters whose model name is the
same as the property to be bound.

Now the optimization option allows to treat the modelName and the propertyName with only one if
both are equal.

I opened a PR trying to solve this almost 1 year ago, but I didn't have enough knowledge and ended up abandoning ship. Halfway through, I also deleted my fork of the old repository, so I didn't update the old PR, but I decided to finish this work in another one after having learned a little more. I'm sorry about that and I apologize to everyone who was counting on this.

Fixes #57637

@sami-daniel sami-daniel requested review from tdykstra and a team as code owners June 25, 2025 04:37
@github-actions github-actions bot added the area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates label Jun 25, 2025
@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label Jun 25, 2025
Added an method to allow optimize model names where the model name is the same
as one of its properties when binding the model name. This should only
be used when this is the desired behavior. Tests have been added to
assert this behavior when passing parameters whose model name is the
same as the property to be bound.

This was introduced to allow circumventing the bug where the parameter
name is the same as the name of one of its properties, causing the
model to trick the PrefixContainer into considering that parameter
as prefixed (model.property), even when it is not.

```C#
class SomeClass
{
  public string Parameter { get; set; } = "";
}
```

and later we define the model name as parameter (e.g in a ActionMethod for example)

```C#
public IActionResult SomeEndpoint([FromQuery] SomeClass parameter) { /* elided */ }
```

internally, when we pass the query "?parameter=somecoolparametervalue", it will classify
it as prefixed (parameter.Parameter instead of only parameter), because of the internal
logic of PrefixContainer. However, it is not prefixed, so later on when it try to bind the
key (the keys from the query, in this case parameter) with the internal keys (wrong classified
as parameter.Parameter) the ModelBinder would not find an valid candidate for matching with the
key.

Now the optimization option allows to treat the modelName and the propertyName with only one if
both are equal.
@sami-daniel
Copy link
Author

Okay, let's clarify a few things. The first is that I wasn't exactly sure where to write the tests. Internally, I needed a place where I could test the entire flow of the ComplexObjectModelBinder, without Mocks and proxies in between. The place I found was. The perfect place where I could find this was in CollectionModelBinderIntegrationTest, but it doesn't seem like the right place for that. Anyway, if there is a more appropriate place I can move it somewhere else.

@sami-daniel
Copy link
Author

Okay, let's clarify a few things. The first is that I wasn't exactly sure where to write the tests. Internally, I needed a place where I could test the entire flow of the ComplexObjectModelBinder, without Mocks and proxies in between. The place I found was. The perfect place where I could find this was in CollectionModelBinderIntegrationTest, but it doesn't seem like the right place for that. Anyway, if there is a more appropriate place I can move it somewhere else.

The second problem is that during development I noticed that: If complex objects are organized in the same way as in the original issue, the problem occurs as well, but it is not so simple to resolve due to recursion.

Let's say we have the following scenario:

class City
{
    public Address Parameter { get; set; }
}

class Address
{
    public string Parameter { get; set; }
}

public IActionResult SomeEndpoint(City parameter) { /* elided */ }

The current behavior of the ModelBinder will incorrectly expect something like:

GET /api/cidades?parameter.parameter.parameter=BH
instead of
GET /api/cidades?parameter.parameter=BH, this second being the ideal behavior.

Unfortunately, the changes I made are not able to solve this problem, since the ComplexObjectModelBinder uses other binders internally to bind the properties with the values. In this case, it will use another ComplexObjectModelBinder to bind the property, but since the model was optimized earlier (it no longer has prefix.property.property and now only has prefix.property), it fails to match the key values to be bound, which is why the written test was marked as Skip. And that’s why I added this separate comment. I believe this could be the subject of a new issue in the future.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates community-contribution Indicates that the PR has been added by a community member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

HttpGet [FromQuery] Model binding fails in case of propertity with the same name
1 participant