-
Notifications
You must be signed in to change notification settings - Fork 75
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
Ideas/457 map table directly to constructor for stepargumenttransformers #488
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the concept... Just thinking about the naming. Here are my thoughts.
As far as I understand (from code and from your explanation), currently the implementation only passes table cells for the creation if there is a corresponding property. If a property is found and there is no default constructor, it invokes a constructor with a signature that corresponds to the properties, but the property is mandatory. (I.e. all constructor property has to have an associated property.)
As far as I understood your motivation, there are cases, where there is no explicit property for a value, but we would like to have it passed in through constructor.
The option UseStrictTableToConstructorBinding
is a bit too generic, because it does not really tell what "strict" means in this context. I'm thinking in something like BindToConstructorParametersWithoutProperty
or BindToConstructorParameters
? What do you think?
For the implementation. You anyway have the tests, so you could play:
- I think the current implementation (regardless of this PR) of the
GetConstructorMatchingToColumnNames
method is wrong as it only queries the properties although we can also set fields. I think the method should have used theGetMembersThatNeedToBeSet
method to get the set of "members" instead of calling theGetProperties
directly. - If that would be the case, maybe it would be enough to change (extend) only the
GetMembersThatNeedToBeSet
method to support your need: if the option is set, you should createMemberHandler
instances for all constructor parameters in addition that does not have a corresponding field or property. (In thisMemberHandler
, the "set" delegate can throw exception, because it is never going to be set directly, just through the ctor. For this, you need to pass theInstanceCreationOptions
to theGetMembersThatNeedToBeSet
method, but this is not a problem as far as I see.
Could you try this approach if it works?
The current mechanism is a bit worse even: not all constructor parameters need to have an associated property. It only looks like that way ;-) Only the projected properties are taken into account. So properties with matching table columns. Then the first constructor is used that leads to an empty set of names when striking each parameter name out of the list of projected property names. So in my example, only the
Yeah, my take on this is that when your model does not have a default constructor, the table should provide values for all constructor parameters. Simply because the constructor is in charge of the initialization. It is a different use-case than what you normally need. By default you should (in my opinion) just create a model with a default constructor and initialize it through properties/fields.
Naming is always difficult ;-)
I agree.
I see where you want to go. I deliberately separated the two approaches to ensure not changing the current mechanism and to not introduce any breaking changes. That, and I wanted to force a single method of initialization. But I can see the flexibility in your approach.
Sure! |
I'm curious: do we think that an ASP.NET-like "model binding" is what could be most useful/idiomatic here? Something which tries to take the input values provided by the table row and construct a type from them:
It's a similar problem space, particularly with how the HTTP payload and the table row are both text-based needing to be converted into strongly-typed models. It also opens the door to more advanced compositions in the future, such as including values from additional sources. |
Yes, that sounds useful, but that one is a more complex change. @AroglDarthu What do you think? |
I think with this option, this setting has no relevance (because you anyway need to bind all ctor params to call the ctor), so I would ignore it. |
That does make a lot of sense. I think the binding mechanism would feel more intuitive that way. Not sure if the current static helpers are the way to go, as it will probably cause breaking changes or at least different behavior. Perhaps it could fit in nicely with the DI route @ajeckmans is aiming for in #108. It could (as fas as I understood) be introduced as a new feature alltogether, while still supporting the current static helpers. The static helpers can then be marked as deprecated with a message hinting at the new feature. |
My last refactoring does pretty much what @Code-Grump suggested:
Without any breaking changes (at least that was my objective)...
|
🤔 What's changed?
A new option was added to change the behaviour of the table helper
CreateInstance
.WIth the option set to true, the table helper will try to find a constructor that exactly matches the table.
If a matching constructor cannot be found, the helper will throw a
MissingMethodException
.See https://github.com/orgs/reqnroll/discussions/457
⚡️ What's your motivation?
In normal circumstances you would not need this feature, because you can just give the support model the correct properties or fields to resemble the table. However, in my use-case I need the support model to be serialized into an exact JSON structure. For example:
Usage:
🏷️ What kind of change is this?
📋 Checklist:
This text was originally taken from the template of the Cucumber project, then edited by hand. You can modify the template here.