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

Base classes like ScoringFunction should be abstract #9686

Closed
brjohnstmsft opened this issue Jan 30, 2020 · 18 comments · Fixed by #12250
Closed

Base classes like ScoringFunction should be abstract #9686

brjohnstmsft opened this issue Jan 30, 2020 · 18 comments · Fixed by #12250
Assignees
Labels
blocking-release Blocks release Client This issue points to a problem in the data-plane of the library. Search
Milestone

Comments

@brjohnstmsft
Copy link
Member

Library or service name.
Azure.Search

Is your feature request related to a problem? Please describe.
In the current (Track 1) .NET client library for Azure Cognitive Search, the generated model classes that represent base classes (for example, ScoringFunction) are not abstract, but they should be. This can lead to confusion when customers try to serialize and deserialize the model classes themselves, as in this issue.

We should ensure this is corrected in the Track 2 .NET SDK.

FYI @AlexGhiondea

@triage-new-issues triage-new-issues bot added the needs-triage This is a new issue that needs to be triaged to the appropriate team. label Jan 30, 2020
@triage-new-issues triage-new-issues bot removed the needs-triage This is a new issue that needs to be triaged to the appropriate team. label Jan 30, 2020
@maririos
Copy link
Member

FYI @tg-msft

@jsquire jsquire added the Client This issue points to a problem in the data-plane of the library. label Jan 30, 2020
@AlexGhiondea AlexGhiondea assigned tg-msft and unassigned AlexGhiondea Feb 11, 2020
@AlexGhiondea AlexGhiondea added this to the [2020] March milestone Feb 18, 2020
@tg-msft tg-msft modified the milestones: [2020] March, [2020] April Mar 12, 2020
@tg-msft tg-msft modified the milestones: [2020] April, [2020] May Apr 6, 2020
@tg-msft tg-msft assigned heaths and unassigned tg-msft Apr 6, 2020
@heaths
Copy link
Member

heaths commented Apr 6, 2020

I'll discuss with @pakrym but this may be something we have to do manually with the customized model classes. I imagine, in general, it's not easy to tell whether generated base models can be instantiated or only extended. I imagine the latter is more common, but wouldn't expect the former to never occur.

@heaths
Copy link
Member

heaths commented Apr 14, 2020

/cc @Yahnoosh

One concern that came up in internal discussions was that if the server returns a different discriminated type, what then? It can't deserialize to anything including the base class because it's abstract. That's actually why @pakrym doesn't mark the classes abstract.

However, I pointed out that IIF the services introduce new types in new api-versions, this shouldn't be a concern. Client libraries, according to guidelines, should use service version enums that are not directly tied to an api-version and don't accept user-input. .NET, for example, uses monotonically increasing version numbers to represent supported api-versions. Combined, no dev should expect an unsupported type from the server in an older version of the library because the type would never be returned. @johanste has pointed out, however, that services often return new types in older api-versions because they don't always bind response models to api-version.

Thus, we may need to leave these models as non-abstract and, instead, on the base models, implement Azure/autorest.csharp#650 to support round tripping unknown types.

@brjohnstmsft
Copy link
Member Author

@heaths Is there some way we can address both the deserialization-of-unknown-types issue and the usability issue created by having non-abstract base classes? For example, instead of deserializing unknown properties to a dictionary on the base class, perhaps each hierarchy gets its own UnknownResponse derived type or something similar?

@heaths
Copy link
Member

heaths commented Apr 20, 2020

That's a pretty good idea. @pakrym, that could be done generically, right? Maybe an Unknown{Type} model gets generated for each would-be abstract model, and that would have a property bag?

@johanste
Copy link
Member

Incidentally, this is one of the recommended patterns for service teams that knew that they had a polymorphic type that they intended to add new child types to.

@pakrym
Copy link
Contributor

pakrym commented Apr 20, 2020

It's possible but not very clean. We would need to generate an Unknown{Type} for every polymorphic type that being returned by model or client method.

Would these types be public?

@heaths
Copy link
Member

heaths commented Apr 20, 2020

We'd only need one for the base class that you'd otherwise mark abstract. And, yes, I imagine they should be publish. People could dig into the property bag at least. They wouldn't have to be, though. I've seen in some implementations the property bag for unhandled properties are often private just so one can roundtrip.

@pakrym
Copy link
Contributor

pakrym commented Apr 20, 2020

We'd only need one for the base class that you'd otherwise mark abstract.

Not really, imagine you have A -> B -> C hierarchy.

And 3 client methods:

A GetA();
B GetB();
C GetC()

You can't just generate an UknownA class and use it as a return value in GetB and GetC you also need UnknownB and UnknownC. Same things with models, if any subclass is directly returned it would need and Unknown{Type}

@heaths
Copy link
Member

heaths commented Apr 21, 2020

Fair enough, but how many times is there more than one base class? We have to do something. We're told not to ship "empty classes" and it's confusing for customers if they try to instantiate the base class and pass it along. Have a "few" extra Unknown{Type} classes seems a better option.

/cc @KrzysztofCwalina

@pakrym
Copy link
Contributor

pakrym commented Apr 21, 2020

Fair enough, but how many times is there more than one base class?

This is something we would have to figure out. There are at least 3 in storage management.

I agree that we need to figure it out. I think internal classes might be a reasonable solution here.

@heaths
Copy link
Member

heaths commented Apr 21, 2020

I think internal classes might be a reasonable solution here.

Could you clarify what you mean? You're agreeing (as an option) that the generator should emit all those classes as needed, but make those classes internal? So we'd just return the base class for roundtripping purposes only.

If that's what you meant, I think that's fine. It won't overload the namespace with a bunch of new types.

@AlexGhiondea AlexGhiondea added customer-reported Issues that are reported by GitHub users external to the Azure organization. feature-request This issue requires a new behavior in the product in order be resolved. and removed customer-reported Issues that are reported by GitHub users external to the Azure organization. feature-request This issue requires a new behavior in the product in order be resolved. labels Apr 21, 2020
@AlexGhiondea AlexGhiondea changed the title [FEATURE REQ] [Search] Base classes like ScoringFunction should be abstract Base classes like ScoringFunction should be abstract Apr 21, 2020
@tg-msft
Copy link
Member

tg-msft commented Apr 22, 2020

I'd personally like an interface IExtraProperties { Dictionary<string, object> Properties { get; } } that everything implements explicitly per the discussion in Azure/autorest.csharp#650.

@heaths
Copy link
Member

heaths commented Apr 30, 2020

Here's a list of the classes that should be abstract:

  • CharFilter
  • CognitiveServicesAccount
  • DataChangeDetectionPolicy
  • DataDeletionDetectionPolicy
  • LexicalAnalyzer
  • LexicalTokenizer
  • ScoringFunction
  • SearchIndexerSkill
  • Similarity
  • TokenFilter

@heaths
Copy link
Member

heaths commented Apr 30, 2020

The generator doesn't handle abstract classes correctly when I added customized models as abstract. Thus, this is blocked on Azure/autorest.csharp#650.

@heaths heaths added the blocking-release Blocks release label Apr 30, 2020
@heaths heaths modified the milestones: [2020] May, [2020] June Apr 30, 2020
@heaths
Copy link
Member

heaths commented Apr 30, 2020

For preview 3 I'll make the constructors private protected, at least.

  • CharFilter
  • CognitiveServicesAccount
  • DataChangeDetectionPolicy
  • DataDeletionDetectionPolicy
  • LexicalAnalyzer
  • LexicalTokenizer
  • ScoringFunction
  • SearchIndexerSkill
  • Similarity
  • TokenFilter

@pakrym
Copy link
Contributor

pakrym commented Apr 30, 2020

Are all of these classes input-output?

heaths added a commit to heaths/azure-sdk-for-net that referenced this issue Apr 30, 2020
@heaths
Copy link
Member

heaths commented Apr 30, 2020

Yes, all input/output.

heaths added a commit that referenced this issue May 1, 2020
* Prevent base classes from being instantiated

Works around #9686

* Update public APIs
heaths added a commit to heaths/azure-sdk-for-net that referenced this issue May 21, 2020
Resolves Azure#9686 without making the classes abstract, given that a fix for Azure/autorest.csharp#650 is further out.
heaths added a commit that referenced this issue May 22, 2020
Resolves #9686 without making the classes abstract, given that a fix for Azure/autorest.csharp#650 is further out.
@github-actions github-actions bot locked and limited conversation to collaborators Mar 28, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
blocking-release Blocks release Client This issue points to a problem in the data-plane of the library. Search
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants