-
Notifications
You must be signed in to change notification settings - Fork 470
UseOptions / UseMappingSchema #4861
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
base: master
Are you sure you want to change the base?
Conversation
I don't like this API. UseOptions may change data provider on the fly and probably has a lot of other sideeffects. If you need some specific feature, we can discuss. |
I get the concern, but Maybe we need to mention it in the documentation to avoid confusion. |
I wouldn't introduce it this way. Instead, you can use the Builder pattern to restrict modifications to only the allowed options. While direct |
Introducing a separate Builder pattern just to restrict modifications would add unnecessary complexity. Users will need to learn yet another part of an already feature-rich API, making it harder to adopt. Instead, we can rely on clear documentation and enforce restrictions at runtime by throwing exceptions when unsupported options are modified. |
We can't ensure clear documentation when additional options are introduced. However, users might discover undocumented features that work and start relying on them. Later, if we make changes, everything undocumented could stop working. I want to prevent users from making mistakes due to a lack of documentation. Additionally, a strictly limited builder would be more precise than simply modifying the options class. Exceptions are useful, but why not prevent mistakes entirely by designing the builder in a way that makes them impossible? If something isn’t allowed, it simply won’t compile—this is far better than letting users try unsupported options and fail at runtime. |
Our API is already complex enough, and we shouldn’t make it even harder to use. The proposed We already have a Builder-based API - Merge API, and to be honest, I can't use it without constantly referring to the documentation, examples, or even the source code. All those "prevent mistakes entirely by designing the builder" arguments don’t help in practice. Complex APIs that builders tend to become are a bad idea. Reusing the existing, familiar API is a good one. As for user mistakes, in the latest update, I’ve added the necessary checks to ensure that invalid modifications throw an exception. I wouldn’t have even done that, but maybe this will give you more peace of mind :) |
I find that interesting, because I find the current version of the Merge API to be incredibly intuitive. The intellisense based on limited types means that the next available method(s) are immediately available and obvious to select which one should be used. |
Oh, that’s probably because I personally took part in refining the Merge API to make it as polished as it is! :) But that doesn’t mean it’s simple. Like any API built using the Builder pattern, it comes with an additional learning curve, is often inconsistent with existing APIs, and isn’t always intuitive. It also adds complexity to an already feature-rich library API. Sometimes, a Builder is necessary - Merge API is a good example. It’s complex, but I don’t see any alternative for it. However, making the entire library follow this pattern would be a huge mistake. In the case of |
/azp run test-all |
Azure Pipelines successfully started running 1 pipeline(s). |
Test baselines changed by this PR. Don't forget to merge/close baselines PR after this pr merged/closed. |
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.
part 1
/// <summary> | ||
/// Gets the default options. | ||
/// </summary> | ||
IOptionSet Default { get; } |
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've checked Default
implementation and use and see a lot of inconsistency around it.
It returns either private empty
, public empty
, public default
or options from Common.Configuration
: would be nice if we can rid of empty/default duplication here and have only public Default
(backed either by Common.Configuration
or _empty
value) including options init in DataOptions (lines 70-75, especially taking into account that some code there reference to Common.Configuration
which is implementation detail of specific option set and shouldn't be used here)
{ | ||
interface IReapplicable<T> | ||
{ | ||
Action? Apply(T obj, object? previousObject); |
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.
It looks like we can change type from object
to IOptionSet
if we inherit interface from IOptionSet
_sb.Value | ||
.Append('.') | ||
.Append(GetObjectID(data?.Method)) | ||
; |
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.
Add(GetObjectID(data?.Method))
_sb.Value | ||
.Append('.') | ||
.Append(GetObjectID(data)) | ||
; | ||
return this; |
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.
Add(GetObjectID(data))
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.
and maybe in other places where transform data? Like Add(object?)
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.
part 2
var action = a.Apply(obj, previousItem); | ||
if (action != null) | ||
actions += action; |
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.
could be simplified to
actions += a.Apply(obj, previousItem);
also below and DataOptions Reapply methods
A new method,
UseOptions
, has been introduced to allow temporary changes toIDataContext
options, with automatic restoration afterward.IDataContext
options are immutable, meaning they cannot be changed once set. With this feature, it is now possible to override options within a specific execution context while ensuring they revert to their original state when no longer needed.Several overloads are available to fit different use cases: