1825 строки
51 KiB
Markdown
1825 строки
51 KiB
Markdown
#### Default `autorest --csharp` generator change
|
||
___
|
||
|
||
:warning: We have updated the default generator used by `autorest --csharp` to the new version `V3`, which uses `@autorest/csharp` package and it will have few side effects:
|
||
- It will generate code based on the [.NET SDK guidelines](https://azure.github.io/azure-sdk/dotnet_introduction.html), which will be totally different than the code generated by `V2` version.
|
||
- The dotnet 8.0 or above is required.
|
||
- If you still want to generate code based on `V2` version, you can add `--legacy` flag to the command line to get the previous behavior.
|
||
___
|
||
|
||
|
||
# C# code generator for AutoRest V3
|
||
|
||
- [C# code generator for AutoRest V3](#c-code-generator-for-autorest-v3)
|
||
- [Prerequisites](#prerequisites)
|
||
- [Build](#build)
|
||
- [Test](#test)
|
||
- [Testing Details](#testing-details)
|
||
- [PR Merge Process](#pr-merge-process)
|
||
- [Preview changes in azure-sdk-for-net](#preview-changes-in-azure-sdk-for-net)
|
||
- [Make sure changes in azure-sdk-for-net is expected before merging your autorest.csharp PR](#make-sure-changes-in-azure-sdk-for-net-is-expected-before-merging-your-autorestcsharp-pr)
|
||
- [Merge azure-sdk-for-net PR](#merge-azure-sdk-for-net-pr)
|
||
- [Use in `azure-sdk-for-net` repo](#use-in-azure-sdk-for-net-repo)
|
||
- [PR Integration with Azure SDK Repository](#pr-integration-with-azure-sdk-repository)
|
||
- [Use outside of the `azure-sdk-for-net` repo](#use-outside-of-the-azure-sdk-for-net-repo)
|
||
- [Debugging](#debugging)
|
||
- [Debugging transforms](#debugging-transforms)
|
||
- [Customizing the generated code](#customizing-the-generated-code)
|
||
- [Make a model internal](#make-a-model-internal)
|
||
- [Rename a model class](#rename-a-model-class)
|
||
- [Change a model or client namespace](#change-a-model-or-client-namespace)
|
||
- [Make model property internal](#make-model-property-internal)
|
||
- [Rename a model property](#rename-a-model-property)
|
||
- [Change a model property type](#change-a-model-property-type)
|
||
- [Preserve raw Json value of a property](#preserve-raw-json-value-of-a-property)
|
||
- [Changing member doc comment](#changing-member-doc-comment)
|
||
- [Customize serialization/deserialization methods](#customize-serializationdeserialization-methods)
|
||
- [Change the serialized name of a property](#change-the-serialized-name-of-a-property)
|
||
- [Change the hierarchy of a property in the serialized JSON](#change-the-hierarchy-of-a-property-in-the-serialized-json)
|
||
- [Change the implementation of serialization/deserialization method of one particular property](#change-the-implementation-of-serializationdeserialization-method-of-one-particular-property)
|
||
- [Add a new property to the model with serialization/deserialization](#add-a-new-property-to-the-model-with-serializationdeserialization)
|
||
- [Replace the entire serialization/deserialization method](#replace-the-entire-serializationdeserialization-method)
|
||
- [Renaming an enum](#renaming-an-enum)
|
||
- [Renaming an enum member](#renaming-an-enum-member)
|
||
- [Changing an enum to an extensible enum](#changing-an-enum-to-an-extensible-enum)
|
||
- [Make a client internal](#make-a-client-internal)
|
||
- [Rename a client](#rename-a-client)
|
||
- [Replace any generated member](#replace-any-generated-member)
|
||
- [Remove any generated member](#remove-any-generated-member)
|
||
- [Change model namespace or accessibility in bulk](#change-model-namespace-or-accessibility-in-bulk)
|
||
- [Change operation accessibility in bulk](#change-operation-accessibility-in-bulk)
|
||
- [Exclude models from namespace](#exclude-models-from-namespace)
|
||
- [Extending a model with additional constructors](#extending-a-model-with-additional-constructors)
|
||
- [Change the file license header](#change-the-file-license-header)
|
||
- [Management plane concepts and configurations](#management-plane-concepts-and-configurations)
|
||
|
||
<!-- /TOC -->
|
||
|
||
## Prerequisites
|
||
|
||
- [NodeJS (18.x.x)](https://nodejs.org/en/)
|
||
- [.NET Core SDK (8.0.303 or above)](https://dotnet.microsoft.com/download/dotnet/8.0)
|
||
- [PowerShell Core 7](https://github.com/PowerShell/PowerShell/releases/latest)
|
||
- `npm install` (at root)
|
||
|
||
## Build
|
||
|
||
- `dotnet build` (at root)
|
||
|
||
## Test
|
||
|
||
**`./eng/Generate.ps1` (at root in PowerShell Core)**
|
||
|
||
This command tests your change across many swagger definitions and samples.
|
||
|
||
These arguments change the behavior:
|
||
- `-fast` option skips Swagger -> YAML IL step. Much faster when only making codegen changes
|
||
- `-fast SWAGGER_NAME` (where SWAGGER_NAME is replaced with the name of the swagger) to run only one case
|
||
|
||
You could run this command on local dev machine or ask github workflow run it against your branch. The steps to run workflow is:
|
||
|
||
1. Go to github actions from your fork repo.
|
||
2. Click workflow "Regenerate all code".
|
||
3. Choose the branch you are working on, and click "Run workflow".
|
||
4. After the workflow success, there will be a new commit to your branch
|
||
|
||
![Regen workflow example image](./docs/images/regenWorkflow.png)
|
||
|
||
**`dotnet test` (at root)**
|
||
|
||
### Testing Details
|
||
|
||
**autorest.testserver**
|
||
|
||
[autorest.testserver](http://github.com/Azure/autorest.testserver/) provides a platform for automated testing of the code generators.
|
||
|
||
It packages a bunch of test swagger files, along with a “mock” nodejs server.
|
||
|
||
The swagger files are compiled, and then run, which pings the mock server (to verify behavior). This tests both the Modeler 4 and language specific codegen.
|
||
|
||
This document contains some additional [technical details](https://github.com/Azure/autorest.csharp/blob/main/test/README.md).
|
||
|
||
**cadl-ranch**
|
||
|
||
[cadl-ranch](https://github.com/Azure/cadl-ranch) is similar to autorest.testserver, which provides a platform for automated testing too. The difference is the testing target of cadl-ranch is typespec generated SDK.
|
||
|
||
Sometimes when we are adding new tests in cadl-ranch, we might want to make sure the tests and corresponding mock APIs work well by applying these newly added/modified tests to autorest.csharp. To realize this, run `.\eng\ApplyCadlRanch.ps1 {cadl-ranch path}` or just run `.\eng\ApplyCadlRanch.ps1` if cadl-ranch folder is at the same folder of autorest.csharp.
|
||
|
||
## PR Merge Process
|
||
|
||
When the automatic PR is created for azure-sdk-for-net if there are any issues found all other changes to autorest.csharp are blocked until those issues are resolved. This means we should be certain that the changes we are making create the expected result in azure-sdk-for-net prior to merging our PR.
|
||
|
||
### Preview changes in azure-sdk-for-net
|
||
|
||
This step could be done [manually](./docs/manually_regen_sdk.md) or automatically with below steps:
|
||
|
||
Go to [Autorest Regen Preview](https://dev.azure.com/azure-sdk/internal/_build?definitionId=5990&_a=summary) pipeline, and click "Run Pipeline". In "Branch/tag", type in your PR ref, i.e. refs/pull/3215/head, or refs/pull/3215/merge. Click "Run". After the build finishes, there will be a PR starting with "Autorest Regen Preview" in azure-sdk-for-net.
|
||
|
||
### Make sure changes in azure-sdk-for-net is expected before merging your autorest.csharp PR
|
||
Once this is done we want to ensure this regen PR is discoverable as well as the key stakeholders who would need to sign off mark this PR as either approved or request changes.
|
||
- In the autorest.csharp PR description please add a line at the very top indicating which azure-sdk-for-net PR is the regeneration for this PR. An example would look like this
|
||
![PR example image](./docs/images/regenPRLink.png)
|
||
- Next have any stakeholders that would need to review do so in this PR and mark it as approved or request changes.
|
||
- Once all stakeholders review and approve of the PR we are ready for the next step of merging the autorest.csharp PR.
|
||
|
||
### Merge azure-sdk-for-net PR
|
||
|
||
When the autorest.csharp PR gets merged there will be an automatic azure-sdk-for-net PR created under the [azure-sdk fork](https://github.com/azure-sdk/azure-sdk-for-net) but this PR will not contain any of the Export-API changes nor will it contain any custom changes to fix test cases / snippet references. It is therefore recommended that we merge this PR into our custom PR we made to validate there are no additional changes we didn't expect.
|
||
|
||
- Undo the changes to the nuget config and Packages.Data.props if you have not already done so.
|
||
- Add a note in the autogenerated PR that conflicts are being resolved in PR you created above so that people know where to follow the conversation.
|
||
![resolved conflicts example image](./docs/images/conflictsResolved.png)
|
||
- If you haven't done so add azure-sdk as an upstream and pull the branch that the automatic PR uses `auto-update-autorest` into your local azure-sdk-for-net branch.
|
||
- `git remote add azure-sdk https://github.com/azure-sdk/azure-sdk-for-net`
|
||
- `git fetch azure-sdk`
|
||
- `git pull azure-sdk auto-update-autorest`
|
||
- This should have zero conflicts and if it does something wasn't done correctly in the PR that was created to demonstrate the regeneration.
|
||
- If there are conflicts our only option here is to resolve them, but will most require another review from the stakeholders most likely.
|
||
- If there were no conflicts or after they are resolved push the changes to your branch and you can then merge the PR that was previous approved or re-approved in the case of conflicts.
|
||
- The autogenerated PR can now simply be closed
|
||
|
||
## Use in `azure-sdk-for-net` repo
|
||
|
||
Run `dotnet build /t:GenerateCode` in the directory that contains your `.csproj` file.
|
||
|
||
This executes [these targets](https://github.com/Azure/autorest.csharp/blob/main/src/AutoRest.CSharp/build/CodeGeneration.targets).
|
||
|
||
Refer also to [azure-sdk-for-net/CONTRIBUTING.md](https://github.com/Azure/azure-sdk-for-net/blob/master/CONTRIBUTING.md#on-boarding-new-generated-code-library) for more details.
|
||
|
||
### PR Integration with Azure SDK Repository
|
||
|
||
Merging a change in autorest.csharp will open a PR against azure-sdk-for-net with every project’s generated code staged for review.
|
||
|
||
Along with this, it also bumps the generator to the new version.
|
||
|
||
This bump is done [here](https://github.com/Azure/azure-sdk-for-net/blob/master/eng/Packages.Data.props).
|
||
|
||
The generator is shipped as a NuGet package.
|
||
|
||
This way, every binding stays in lockstep with the current generator
|
||
|
||
|
||
## Use outside of the `azure-sdk-for-net` repo
|
||
|
||
Use below command to generate code:
|
||
|
||
```
|
||
autorest --use:@autorest/csharp@3.0.0-beta.20210210.4 --input-file:FILENAME --clear-output-folder:true --output-folder:DIRECTORY
|
||
```
|
||
|
||
Note:
|
||
1. Use @autorest/csharp version v3.0.0-beta.20210210.4 or later.
|
||
2. If you don't want to override the `.csproj` after the first generation, you can pass `--skip-csproj` flag with the autorest command.
|
||
|
||
For more details please refer [these](https://github.com/Azure/autorest/tree/master/docs/generate) docs to generate code from your OpenAPI definition using AutoRest.
|
||
|
||
## Debugging
|
||
|
||
The generator consists of two parts, first part consumes the input (swagger or TypeSpec) and produce an intermediate result which the second part consumes them to produce the generated code.
|
||
|
||
When the input is swagger files, the generator leverages the processes and pipelines from [autorest](https://github.com/Azure/autorest), and its first part is the autorest plugin `autorest.modelerfour` which produces the file `CodeModel.yaml` and `Configuration.json`.
|
||
|
||
When the input is TypeSpec files, the first part is the autorest plugin `@azure-tools/typespec-csharp` which produces the file `tspCodeModel.json` and `Configuration.json`.
|
||
|
||
The second part of the generator is written in `C#` and consumes `Configuration.json` and `CodeModel.yaml`/`tspCodeModel.json` to produce the generated code.
|
||
|
||
This section shows how to debug the second part of the generator which takes `Configuration.json` and `CodeModel.yaml`/`tspCodeModel.json` as input.
|
||
|
||
### Debugging the projects inside the `autorest.csharp` repo
|
||
|
||
The `autorest.csharp` repo contains quite a few sample projects and test projects to verify various of features of the generator. To debug these projects, you could just simply open the solution file `AutoRest.CSharp.sln` in Visual Studio and Visual Studio already has a corresponding debugging profile for each project which you could start debugging with.
|
||
|
||
### Debugging the projects outside the `autorest.csharp` repo
|
||
|
||
To debug a project outside the `autorest.csharp` repo, in addition to the prerequisites you must do to let your project be able to use the generator, you also need to do the following depending on the input type of your project.
|
||
|
||
If your project is using the swagger files as input, and you use a markdown file (like `readme.md` or `autorest.md`) as the configuration of your autorest command, you need to add the following to your configuration `md` file
|
||
|
||
```yml
|
||
csharpgen:
|
||
attach: true
|
||
```
|
||
|
||
This will attach the debugger to either an existing Visual Studio instance or opening a new Visual Studio instance to debug the generator.
|
||
|
||
If your project is using the swagger files as input, and you use the autorest command line to pass in the configurations, you need to add the following options to your autorest command:
|
||
|
||
```bash
|
||
--csharpgen.attach=true
|
||
```
|
||
to attach a debugger to the plugin process.
|
||
|
||
If your project is using the TypeSpec files as input, you need to add the following options to your command line
|
||
|
||
```
|
||
--option @azure-tools/typespec-csharp.debug=true
|
||
```
|
||
to attach a debugger to the plugin process.
|
||
|
||
If you need to use a locally built generator, you could use the following options to specify the path to the generator dll file:
|
||
|
||
```
|
||
--option @azure-tools/typespec-csharp.csharpGeneratorPath=/absolute/path/to/AutoRest.CSharp.dll
|
||
```
|
||
|
||
Usually you should find the `AutoRest.CSharp.dll` file here: `[Root of autorest.csharp repo]/artifacts/bin/AutoRest.CSharp/Debug/net8.0/AutoRest.CSharp.dll`.
|
||
|
||
### Debugging the projects in the `azure-sdk-for-net` repo
|
||
|
||
There is a target defined in `azure-sdk-for-net` repo to generate the library code:
|
||
```powershell
|
||
dotnet build /t:GenerateCode
|
||
```
|
||
|
||
In `azure-sdk-for-net` repo, to debug a project that uses swagger files as input, you need to add the following to your `autorest.md` file
|
||
|
||
```yml
|
||
csharpgen:
|
||
attach: true
|
||
```
|
||
|
||
and then run `dotnet build /t:GenerateCode` and it will attach a debugger to the plugin process.
|
||
|
||
To debug a TypeSpec project inside `azure-sdk-for-net` repo, you could run
|
||
```powershell
|
||
dotnet build /t:GenerateCode /p:TypeSpecAdditionalOptions="debug=true"
|
||
```
|
||
to attach a debugger to the plugin process.
|
||
|
||
If you are trying to use a locally built generator `dll` and debug it, you should run
|
||
```powershell
|
||
dotnet build /t:GenerateCode /p:TypeSpecAdditionalOptions="csharpGeneratorPath=/absolute/path/to/AutoRest.CSharp.dll%3Bdebug=true"
|
||
```
|
||
where you must use `%3B` as escape of the semicolon to append multiple options.
|
||
|
||
You could also use the raw `tsp-client` command to debug the project. Run the following command in the same directory of the project you want to debug:
|
||
```powershell
|
||
npx tsp-client update --output-dir . --emitter-options "csharpGeneratorPath=/absolute/path/to/AutoRest.CSharp.dll;debug=true"
|
||
```
|
||
|
||
## Debugging transforms
|
||
|
||
Many customizations can be done as a transform in readme.md, however getting it right can be tricky.
|
||
|
||
One useful trick is to use `$lib.log` to output the state of the object either before of after transform:
|
||
|
||
```
|
||
transform: >
|
||
$['x-accessibility'] = "internal";
|
||
$lib.log($);
|
||
```
|
||
|
||
## Customizing the generated code
|
||
|
||
### Make a model internal
|
||
|
||
Define a class with the same namespace and name as generated model and use the desired accessibility.
|
||
|
||
<details>
|
||
|
||
**Generated code before (Generated/Models/Model.cs):**
|
||
|
||
``` C#
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Model { }
|
||
}
|
||
```
|
||
|
||
**Add customized model (Model.cs)**
|
||
|
||
``` C#
|
||
namespace Azure.Service.Models
|
||
{
|
||
internal partial class Model { }
|
||
}
|
||
```
|
||
|
||
**Generated code after (Generated/Models/Model.cs):**
|
||
|
||
``` diff
|
||
namespace Azure.Service.Models
|
||
{
|
||
- public partial class Model { }
|
||
+ internal partial class Model { }
|
||
}
|
||
```
|
||
|
||
</details>
|
||
|
||
### Rename a model class
|
||
|
||
Define a class with a desired name and mark it with `[CodeGenModel("OriginalName")]`
|
||
|
||
<details>
|
||
|
||
**Generated code before (Generated/Models/Model.cs):**
|
||
|
||
``` C#
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Model { }
|
||
}
|
||
```
|
||
|
||
**Add customized model (NewModelClassName.cs)**
|
||
|
||
``` C#
|
||
namespace Azure.Service.Models
|
||
{
|
||
[CodeGenModel("Model")]
|
||
public partial class NewModelClassName { }
|
||
}
|
||
```
|
||
|
||
**Generated code after (Generated/Models/NewModelClassName.cs):**
|
||
|
||
``` diff
|
||
namespace Azure.Service.Models
|
||
{
|
||
- public partial class Model { }
|
||
+ public partial class NewModelClassName { }
|
||
}
|
||
```
|
||
|
||
</details>
|
||
|
||
### Change a model or client namespace
|
||
|
||
Define a class with a desired namespace and mark it with `[CodeGenModel("OriginalName")]`.
|
||
|
||
The same works for a client, if marked with `[CodeGenClient("ClientName")]`.
|
||
|
||
<details>
|
||
|
||
**Generated code before (Generated/Models/Model.cs):**
|
||
|
||
``` C#
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Model { }
|
||
}
|
||
```
|
||
|
||
**Add customized model (NewModelClassName.cs)**
|
||
|
||
``` C#
|
||
namespace Azure.Service
|
||
{
|
||
[CodeGenModel("Model")]
|
||
public partial class Model { }
|
||
}
|
||
```
|
||
|
||
**Generated code after (Generated/Models/NewModelClassName.cs):**
|
||
|
||
``` diff
|
||
- namespace Azure.Service.Models
|
||
+ namespace Azure.Service
|
||
{
|
||
public partial class Model { }
|
||
}
|
||
```
|
||
|
||
</details>
|
||
|
||
### Make model property internal
|
||
|
||
Define a class with a property matching a generated property name but with desired accessibility.
|
||
|
||
<details>
|
||
|
||
**Generated code before (Generated/Models/Model.cs):**
|
||
|
||
``` C#
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Model
|
||
{
|
||
public string Property { get; }
|
||
}
|
||
}
|
||
```
|
||
|
||
**Add customized model (Model.cs)**
|
||
|
||
``` C#
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Model
|
||
{
|
||
internal string Property { get; }
|
||
}
|
||
}
|
||
```
|
||
|
||
**Generated code after (Generated/Models/Model.cs):**
|
||
|
||
``` diff
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Model
|
||
{
|
||
- public string Property { get; }
|
||
}
|
||
}
|
||
```
|
||
|
||
</details>
|
||
|
||
### Rename a model property
|
||
|
||
Define a partial class with a new property name and mark it with `[CodeGenMember("OriginalName")]` attribute.
|
||
|
||
**NOTE:** you can also change a property to a field using this mapping.
|
||
|
||
<details>
|
||
|
||
**Generated code before (Generated/Models/Model.cs):**
|
||
|
||
``` C#
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Model
|
||
{
|
||
public string Property { get; }
|
||
}
|
||
}
|
||
```
|
||
|
||
**Add customized model (Model.cs)**
|
||
|
||
``` C#
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Model
|
||
{
|
||
[CodeGenMember("Property")]
|
||
public string RenamedProperty { get; }
|
||
}
|
||
}
|
||
```
|
||
|
||
**Generated code after (Generated/Models/Model.cs):**
|
||
|
||
``` diff
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Model
|
||
{
|
||
- public string Property { get; }
|
||
+ // All original Property usages would reference a RenamedProperty
|
||
}
|
||
}
|
||
```
|
||
|
||
</details>
|
||
|
||
### Change a model property type
|
||
|
||
:warning:
|
||
|
||
**NOTE: This is supported for a narrow set of cases where the underlying serialized type doesn't change**
|
||
|
||
Scenarios that would work:
|
||
|
||
1. String <-> TimeSpan (both represented as string in JSON)
|
||
2. Float <-> Int (both are numbers)
|
||
3. String <-> Enums (both strings)
|
||
4. String -> Uri
|
||
|
||
Won't work:
|
||
|
||
1. String <-> Bool (different json type)
|
||
2. Changing model kinds
|
||
|
||
If you think you have a valid re-mapping scenario that's not supported file an issue.
|
||
|
||
:warning:
|
||
|
||
Define a property with different type than the generated one.
|
||
|
||
<details>
|
||
|
||
**Generated code before (Generated/Models/Model.cs):**
|
||
|
||
``` C#
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Model
|
||
{
|
||
public string Property { get; }
|
||
}
|
||
}
|
||
```
|
||
|
||
**Add customized model (Model.cs)**
|
||
|
||
``` C#
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Model
|
||
{
|
||
public DateTime Property { get; }
|
||
}
|
||
}
|
||
```
|
||
|
||
**Generated code after (Generated/Models/Model.Serializer.cs):**
|
||
|
||
``` diff
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Model
|
||
{
|
||
- public string Property { get; }
|
||
+ // Serialization code now reads and writes DateTime value instead of string
|
||
}
|
||
}
|
||
```
|
||
|
||
</details>
|
||
|
||
### Preserve raw Json value of a property
|
||
|
||
Use the [Change a model property type](#Change-a-model-property-type) approach to change property type to `JsonElement`.
|
||
|
||
<details>
|
||
|
||
**Generated code before (Generated/Models/Model.cs):**
|
||
|
||
``` C#
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Model
|
||
{
|
||
public string Property { get; }
|
||
}
|
||
}
|
||
```
|
||
|
||
**Add customized model (Model.cs)**
|
||
|
||
``` C#
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Model
|
||
{
|
||
public JsonElement Property { get; }
|
||
}
|
||
}
|
||
```
|
||
|
||
**Generated code after (Generated/Models/Model.Serializer.cs):**
|
||
|
||
``` diff
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Model
|
||
{
|
||
- public string Property { get; }
|
||
+ // Serialization code now reads and writes JsonElement value instead of string
|
||
}
|
||
}
|
||
```
|
||
|
||
</details>
|
||
|
||
### Changing member doc comment
|
||
|
||
Redefine a member in partial class with a new doc comment.
|
||
|
||
<details>
|
||
|
||
**Generated code before (Generated/Models/Model.cs):**
|
||
|
||
``` C#
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Model
|
||
{
|
||
/// Subpar doc comment
|
||
public string Property { get; }
|
||
}
|
||
}
|
||
```
|
||
|
||
**Add customized model (Model.cs)**
|
||
|
||
``` C#
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Model
|
||
{
|
||
/// Great doc comment
|
||
public string Property { get; }
|
||
}
|
||
}
|
||
```
|
||
|
||
**Generated code after (Generated/Models/Model.cs):**
|
||
|
||
``` diff
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Model
|
||
{
|
||
- /// Subpar doc comment
|
||
- public string Property { get; }
|
||
}
|
||
}
|
||
```
|
||
|
||
</details>
|
||
|
||
### Customize serialization/deserialization methods
|
||
|
||
Changing how a property serializes or deserializes is done by `CodeGenSerialization` attribute. This attribute can be applied to a class or struct with a property name to change the serialization/deserialization method of the property.
|
||
|
||
#### Change the serialized name of a property
|
||
|
||
If you want to change the property name that serializes into the JSON or deserializes from the JSON, you could define your own partial class with the `CodeGenMemberSerialization` attribute.
|
||
|
||
<details>
|
||
|
||
For instance, we have a model class `Cat` with property `Name` and `Color`:
|
||
|
||
**Generated code before:**
|
||
|
||
``` C#
|
||
// Generated/Models/Cat.cs
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Cat
|
||
{
|
||
/* omit the ctors for brevity */
|
||
public string Name { get; set; }
|
||
public string Color { get; set; }
|
||
}
|
||
}
|
||
```
|
||
|
||
**Add customized model:**
|
||
|
||
``` C#
|
||
// Cat.cs
|
||
namespace Azure.Service.Models
|
||
{
|
||
[CodeGenSerialization(nameof(Name), "catName")] // add the property name, and the new serialized name
|
||
public partial class Cat
|
||
{
|
||
}
|
||
}
|
||
```
|
||
|
||
**Generated code after:**
|
||
|
||
``` diff
|
||
// Generated/Models/Cat.cs - no change
|
||
|
||
// Generated/Models/Cat.Serialization.cs
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Cat : IUtf8JsonSerializable
|
||
{
|
||
void IUtf8JsonSerializable.Write(Utf8JsonWriter writer)
|
||
{
|
||
writer.WriteStartObject();
|
||
- writer.WritePropertyName("name"u8);
|
||
+ writer.WritePropertyName("catName"u8);
|
||
writer.WriteStringValue(Name);
|
||
writer.WritePropertyName("color"u8);
|
||
writer.WriteStringValue(Color);
|
||
writer.WriteEndObject();
|
||
}
|
||
|
||
internal static Cat DeserializeCat(JsonElement element)
|
||
{
|
||
if (element.ValueKind == JsonValueKind.Null)
|
||
{
|
||
return null;
|
||
}
|
||
string name = default;
|
||
Optional<string> color = default;
|
||
foreach (var property in element.EnumerateObject())
|
||
{
|
||
- if (property.NameEquals("name"u8))
|
||
+ if (property.NameEquals("catName"u8))
|
||
{
|
||
name = property.Value.GetString();
|
||
continue;
|
||
}
|
||
if (property.NameEquals("color"u8))
|
||
{
|
||
color = property.Value.GetString();
|
||
continue;
|
||
}
|
||
}
|
||
return new Cat(name, color);
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
#### Change the hierarchy of a property in the serialized JSON
|
||
|
||
If you want to change the layer of the property in the json, you can add all the elements in the json path of your property to the attribute using an array, the generator will generate the property into the JSON in the correct hierarchy.
|
||
|
||
**NOTE: Introducing extra layers in serialized JSON only works for MPG and HLC models, does not work for DPG models.**
|
||
|
||
For instance, we want to move `Name` property in the model `Cat` to make it serialized under property `properties` and rename to `catName`.
|
||
|
||
**Generated code before:**
|
||
|
||
``` C#
|
||
// Generated/Models/Cat.cs
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Cat
|
||
{
|
||
/* omit the ctors for brevity */
|
||
public string Name { get; set; }
|
||
public string Color { get; set; }
|
||
}
|
||
}
|
||
|
||
// Generated/Models/Cat.Serialization.cs
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Cat : IUtf8JsonSerializable
|
||
{
|
||
void IUtf8JsonSerializable.Write(Utf8JsonWriter writer)
|
||
{
|
||
writer.WriteStartObject();
|
||
writer.WritePropertyName("name"u8);
|
||
writer.WriteStringValue(Name);
|
||
writer.WritePropertyName("color"u8);
|
||
writer.WriteStringValue(Color);
|
||
writer.WriteEndObject();
|
||
}
|
||
|
||
internal static Cat DeserializeCat(JsonElement element)
|
||
{
|
||
if (element.ValueKind == JsonValueKind.Null)
|
||
{
|
||
return null;
|
||
}
|
||
string name = default;
|
||
Optional<string> color = default;
|
||
foreach (var property in element.EnumerateObject())
|
||
{
|
||
if (property.NameEquals("name"u8))
|
||
{
|
||
name = property.Value.GetString();
|
||
continue;
|
||
}
|
||
if (property.NameEquals("color"u8))
|
||
{
|
||
color = property.Value.GetString();
|
||
continue;
|
||
}
|
||
}
|
||
return new Cat(name, color);
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**Add customized model:**
|
||
|
||
``` C#
|
||
// Cat.cs
|
||
namespace Azure.Service.Models
|
||
{
|
||
[CodeGenSerialization(nameof(Name), new string[] { "properties", "catName" })]
|
||
public partial class Cat
|
||
{
|
||
}
|
||
}
|
||
```
|
||
|
||
**Generated code after:**
|
||
|
||
``` diff
|
||
// Generated/Models/Cat.cs - no change
|
||
|
||
// Generated/Models/Model.Serialization.cs
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Cat : IUtf8JsonSerializable
|
||
{
|
||
void IUtf8JsonSerializable.Write(Utf8JsonWriter writer)
|
||
{
|
||
writer.WriteStartObject();
|
||
- writer.WritePropertyName("name"u8);
|
||
+ writer.WritePropertyName("properties"u8);
|
||
+ writer.WriteStartObject();
|
||
+ writer.WritePropertyName("catName"u8);
|
||
writer.WriteStringValue(Name);
|
||
+ writer.WriteEndObject();
|
||
writer.WritePropertyName("color"u8);
|
||
writer.WriteStringValue(Color);
|
||
writer.WriteEndObject();
|
||
}
|
||
|
||
internal static Cat DeserializeCat(JsonElement element)
|
||
{
|
||
if (element.ValueKind == JsonValueKind.Null)
|
||
{
|
||
return null;
|
||
}
|
||
string name = default;
|
||
foreach (var property in element.EnumerateObject())
|
||
{
|
||
- if (property.NameEquals("name"u8))
|
||
+ if (property.NameEquals("properties"u8))
|
||
+ {
|
||
+ foreach (var property in element.EnumerateObject())
|
||
+ {
|
||
+ if (property.NameEquals("catName"u8))
|
||
{
|
||
meow = property.Value.GetString();
|
||
continue;
|
||
}
|
||
+ }
|
||
+ continue;
|
||
+ }
|
||
if (property.NameEquals("color"u8))
|
||
{
|
||
color = property.Value.GetString();
|
||
continue;
|
||
}
|
||
}
|
||
return new Cat(name, color);
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
</details>
|
||
|
||
#### Change the implementation of serialization/deserialization method of a property
|
||
|
||
If you want to change the implementation of serialization/deserialization method of a property, you could define your own hook methods and assign them to the `CodeGenSerialization` attribute.
|
||
|
||
The custom serialization method for this property is assigned by the `SerializationValueHook` property of the `CodeGenSerialization` attribute, and the custom deserialization method for this property is assigned by the `DeserializationValueHook` property of the `CodeGenSerialization` attribute.
|
||
|
||
The `SerializationValueHook` and `DeserializationValueHook` here are hook method names, and these methods should have the signature as below:
|
||
|
||
``` C#
|
||
// serialization hook and serialization value hook
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
internal void SerializationMethodHook(Utf8JsonWriter writer)
|
||
{
|
||
// write your own serialization logic here
|
||
}
|
||
|
||
// deserialization hook for required property
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
internal static void DeserializeSizeProperty(JsonProperty property, ref TypeOfTheProperty name)
|
||
{
|
||
// write your own deserialization logic here
|
||
}
|
||
// deserialization hook for optional property
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
internal static void DeserializeSizeProperty(JsonProperty property, ref Optional<TypeOfTheProperty> name)
|
||
{
|
||
// write your own deserialization logic here
|
||
}
|
||
```
|
||
|
||
Please use the `nameof` expression to avoid typo in the attribute. Also you could leave both the serialization value hook unassigned if you do not want to change the serialization logic, similar you could leave deserialization hook unassigned if you do not want to change the deserialization logic.
|
||
|
||
The `[MethodImpl(MethodImplOptions.AggressiveInlining)]` attribute is recommended for your hook methods to get optimized performance.
|
||
|
||
Please note that the generator will not check the signature of the hook methods you assigned to the attribute, therefore if the signature is not compatible, the generated library might not compile.
|
||
|
||
<details>
|
||
|
||
For instance, we have a model class `Cat` with property `Name` and `Color`, and we would like to change the way how `Name` property is serialized and deserialized.
|
||
|
||
**Generated code before:**
|
||
|
||
``` C#
|
||
// Generated/Models/Cat.cs
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Cat
|
||
{
|
||
/* omit the ctors for brevity */
|
||
public string Name { get; set; }
|
||
public string Color { get; set; }
|
||
}
|
||
}
|
||
|
||
// Generated/Models/Cat.Serialization.cs
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Cat : IUtf8JsonSerializable
|
||
{
|
||
void IUtf8JsonSerializable.Write(Utf8JsonWriter writer)
|
||
{
|
||
writer.WriteStartObject();
|
||
writer.WritePropertyName("name"u8);
|
||
writer.WriteStringValue(Name);
|
||
writer.WritePropertyName("color"u8);
|
||
writer.WriteStringValue(Color);
|
||
writer.WriteEndObject();
|
||
}
|
||
|
||
internal static Cat DeserializeCat(JsonElement element)
|
||
{
|
||
if (element.ValueKind == JsonValueKind.Null)
|
||
{
|
||
return null;
|
||
}
|
||
string name = default;
|
||
Optional<string> color = default;
|
||
foreach (var property in element.EnumerateObject())
|
||
{
|
||
if (property.NameEquals("name"u8))
|
||
{
|
||
name = property.Value.GetString();
|
||
continue;
|
||
}
|
||
if (property.NameEquals("color"u8))
|
||
{
|
||
color = property.Value.GetString();
|
||
continue;
|
||
}
|
||
}
|
||
return new Cat(name, color);
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**Add customized model:**
|
||
|
||
``` C#
|
||
// Cat.cs
|
||
namespace Azure.Service.Models
|
||
{
|
||
[CodeGenSerialization(nameof(Name), SerializationValueHook = nameof(SerializeNameValue), DeserializationValue = nameof(DeserializeNameValue))]
|
||
public partial class Cat
|
||
{
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
private void SerializeNameValue(Utf8JsonWriter writer)
|
||
{
|
||
// this is the logic we would like to have for the value serialization
|
||
writer.WriteStringValue(Name.ToUpper());
|
||
}
|
||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
private static void DeserializeNameValue(JsonProperty property, ref string name) // the type here is string since name is required
|
||
{
|
||
// this is the logic we would like to have for the value deserialization
|
||
name = property.Value.GetString().ToLower();
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**Generated code after:**
|
||
|
||
``` diff
|
||
// Generated/Models/Cat.cs - no change
|
||
|
||
// Generated/Models/Cat.Serialization.cs
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Cat : IUtf8JsonSerializable
|
||
{
|
||
void IUtf8JsonSerializable.Write(Utf8JsonWriter writer)
|
||
{
|
||
writer.WriteStartObject();
|
||
writer.WritePropertyName("name"u8);
|
||
- writer.WriteStringValue(Name);
|
||
+ SerializeNameValue(writer);
|
||
if (Optional.IsDefined(Color))
|
||
{
|
||
writer.WritePropertyName("color"u8);
|
||
writer.WriteStringValue(Color);
|
||
}
|
||
writer.WriteEndObject();
|
||
}
|
||
|
||
internal static Cat DeserializeCat(JsonElement element)
|
||
{
|
||
if (element.ValueKind == JsonValueKind.Null)
|
||
{
|
||
return null;
|
||
}
|
||
string name = default;
|
||
Optional<string> color = default;
|
||
foreach (var property in element.EnumerateObject())
|
||
{
|
||
if (property.NameEquals("name"u8))
|
||
{
|
||
- meow = property.Value.GetString();
|
||
+ DeserializeNameValue(property, ref name);
|
||
continue;
|
||
}
|
||
if (property.NameEquals("color"u8))
|
||
{
|
||
color = property.Value.GetString();
|
||
continue;
|
||
}
|
||
}
|
||
return new Cat(name, color, size);
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
</details>
|
||
|
||
#### Add a new property to the model with serialization/deserialization
|
||
|
||
If you want to add a new property to the model and also add the property into the serialization/deserialization methods, you could also use the `CodeGenSerialization` attribute to change its default serialized name, and serialization/deserialization methods.
|
||
|
||
<details>
|
||
|
||
**Generated code before:**
|
||
|
||
``` C#
|
||
// Generated/Models/Cat.cs
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Cat
|
||
{
|
||
/* omit the ctors for brevity */
|
||
public string Name { get; set; }
|
||
public string Color { get; set; }
|
||
}
|
||
}
|
||
|
||
// Generated/Models/Cat.Serialization.cs
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Cat : IUtf8JsonSerializable
|
||
{
|
||
void IUtf8JsonSerializable.Write(Utf8JsonWriter writer)
|
||
{
|
||
writer.WriteStartObject();
|
||
writer.WritePropertyName("name"u8);
|
||
writer.WriteStringValue(Name);
|
||
writer.WritePropertyName("color"u8);
|
||
writer.WriteStringValue(Color);
|
||
writer.WriteEndObject();
|
||
}
|
||
|
||
internal static Cat DeserializeCat(JsonElement element)
|
||
{
|
||
if (element.ValueKind == JsonValueKind.Null)
|
||
{
|
||
return null;
|
||
}
|
||
string name = default;
|
||
Optional<string> color = default;
|
||
foreach (var property in element.EnumerateObject())
|
||
{
|
||
if (property.NameEquals("name"u8))
|
||
{
|
||
name = property.Value.GetString();
|
||
continue;
|
||
}
|
||
if (property.NameEquals("color"u8))
|
||
{
|
||
color = property.Value.GetString();
|
||
continue;
|
||
}
|
||
}
|
||
return new Cat(name, color);
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**Add customized model:**
|
||
|
||
``` C#
|
||
[CodeGenSerialization(nameof(Size), "size")]
|
||
public partial class Cat
|
||
{
|
||
public int? Size { get; set; }
|
||
}
|
||
```
|
||
|
||
**Generated code after:**
|
||
|
||
``` diff
|
||
// Generated/Models/Cat.cs
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Cat
|
||
{
|
||
/* omit other ctors for brevity */
|
||
- internal Cat(string name, string color)
|
||
+ internal Cat(string name, string color, int? size)
|
||
{
|
||
Name = name;
|
||
Color = color;
|
||
+ Size = size;
|
||
}
|
||
|
||
public string Name { get; set; }
|
||
public string Color { get; set; }
|
||
}
|
||
}
|
||
|
||
// Generated/Models/Cat.Serialization.cs
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Cat : IUtf8JsonSerializable
|
||
{
|
||
void IUtf8JsonSerializable.Write(Utf8JsonWriter writer)
|
||
{
|
||
writer.WriteStartObject();
|
||
writer.WritePropertyName("name"u8);
|
||
writer.WriteStringValue(Name);
|
||
if (Optional.IsDefined(Color))
|
||
{
|
||
writer.WritePropertyName("color"u8);
|
||
writer.WriteStringValue(Color);
|
||
}
|
||
+ if (Optional.IsDefined(Size))
|
||
+ {
|
||
+ writer.WritePropertyName("size"u8);
|
||
+ writer.WriteNumberValue(Size);
|
||
+ }
|
||
writer.WriteEndObject();
|
||
}
|
||
|
||
internal static Cat DeserializeCat(JsonElement element)
|
||
{
|
||
if (element.ValueKind == JsonValueKind.Null)
|
||
{
|
||
return null;
|
||
}
|
||
string name = default;
|
||
Optional<string> color = default;
|
||
+ Optional<int> size = default;
|
||
foreach (var property in element.EnumerateObject())
|
||
{
|
||
if (property.NameEquals("name"u8))
|
||
{
|
||
meow = property.Value.GetString();
|
||
continue;
|
||
}
|
||
if (property.NameEquals("color"u8))
|
||
{
|
||
meow = property.Value.GetString();
|
||
continue;
|
||
}
|
||
+ if (property.NameEquals("size"u8))
|
||
+ {
|
||
+ size = property.Value.GetInt32();
|
||
+ continue;
|
||
+ }
|
||
}
|
||
return new Cat(name);
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
You could also add the `CodeGenSerialization` attribute to the property to have your own serialization/deserialization logic of the new property. You might have to do this if the type of your new property is an object type or any type that our generator does not natively support.
|
||
|
||
**NOTE: Adding property to serialization/deserialization methods currently only works for DPG.**
|
||
|
||
</details>
|
||
|
||
#### Replace the entire serialization/deserialization method
|
||
|
||
If you want to replace the entire serialization/deserialization method, please use the [Replace any generated member](#replace-any-generated-member) approach to replace serialization/deserialization method with a custom implementation.
|
||
|
||
<details>
|
||
|
||
**Generated code before:**
|
||
|
||
``` C#
|
||
// Generated/Models/Cat.Serialization.cs
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Cat
|
||
{
|
||
void IUtf8JsonSerializable.Write(Utf8JsonWriter writer)
|
||
{
|
||
writer.WriteStartObject();
|
||
writer.WritePropertyName("name"u8);
|
||
writer.WriteStringValue(Name);
|
||
if (Optional.IsDefined(Color))
|
||
{
|
||
writer.WritePropertyName("color"u8);
|
||
writer.WriteStringValue(Color);
|
||
}
|
||
writer.WriteEndObject();
|
||
}
|
||
|
||
internal static Cat DeserializeCat(JsonElement element)
|
||
{
|
||
string color = default;
|
||
string name = default;
|
||
foreach (var property in element.EnumerateObject())
|
||
{
|
||
if (property.NameEquals("color"u8))
|
||
{
|
||
if (property.Value.ValueKind == JsonValueKind.Null)
|
||
{
|
||
continue;
|
||
}
|
||
color = property.Value.GetString();
|
||
continue;
|
||
}
|
||
if (property.NameEquals("name"u8))
|
||
{
|
||
if (property.Value.ValueKind == JsonValueKind.Null)
|
||
{
|
||
continue;
|
||
}
|
||
name = property.Value.GetString();
|
||
continue;
|
||
}
|
||
}
|
||
return new Cat(id, name);
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**Add customized model:**
|
||
|
||
``` C#
|
||
// Cat.cs
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Cat
|
||
{
|
||
// currently we have to use a full name to ensure this could be replaced
|
||
void global:Azure.Core.IUtf8JsonSerializable.Write(Utf8JsonWriter writer)
|
||
{
|
||
writer.WriteStartObject();
|
||
writer.WritePropertyName("name"u8);
|
||
writer.WriteStringValue(Name);
|
||
// WORKAROUND: server never needs color, remove it in the customization code
|
||
writer.WriteEndObject();
|
||
}
|
||
|
||
internal static Cat DeserializeCat(JsonElement element)
|
||
{
|
||
string color = default;
|
||
string name = default;
|
||
foreach (var property in element.EnumerateObject())
|
||
{
|
||
if (property.NameEquals("name"))
|
||
{
|
||
if (property.Value.ValueKind == JsonValueKind.Null)
|
||
{
|
||
continue;
|
||
}
|
||
name = property.Value.GetString();
|
||
continue;
|
||
}
|
||
}
|
||
// WORKAROUND: server never sends color, default to black
|
||
color = "black";
|
||
return new Cat(name, color);
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**Generated code after:**
|
||
|
||
Generated code won't contain the `IUtf8JsonSerializable.Write` or `DeserializeCat` method and the custom one would be used for deserialization.
|
||
|
||
</details>
|
||
|
||
### Renaming an enum
|
||
|
||
Redefine an enum with a new name and all the members mark it with `[CodeGenModel("OriginEnumName")]`.
|
||
|
||
**NOTE: because enums can't be partial all values have to be copied**
|
||
|
||
<details>
|
||
|
||
**Generated code before (Generated/Models/Colors.cs):**
|
||
|
||
``` C#
|
||
namespace Azure.Service.Models
|
||
{
|
||
public enum Colors
|
||
{
|
||
Red,
|
||
Green,
|
||
Blue
|
||
}
|
||
}
|
||
```
|
||
|
||
**Add customized model (WallColors.cs)**
|
||
|
||
``` C#
|
||
namespace Azure.Service.Models
|
||
{
|
||
[CodeGenModel("Colors")]
|
||
public enum WallColors
|
||
{
|
||
Red,
|
||
Green,
|
||
Blue
|
||
}
|
||
}
|
||
```
|
||
|
||
**Generated code after (Generated/Models/Model.cs):**
|
||
|
||
``` diff
|
||
-namespace Azure.Service.Models
|
||
-{
|
||
- public enum Colors
|
||
- {
|
||
- Red,
|
||
- Green,
|
||
- Blue
|
||
- }
|
||
-}
|
||
+// Serialization code uses the new WallColors type name
|
||
```
|
||
|
||
</details>
|
||
|
||
### Renaming an enum member
|
||
|
||
Redefine an enum with the same name and all the members, mark renamed member with `[CodeGenMember("OriginEnumMemberName")]`.
|
||
|
||
**NOTE: because enums can't be partial all values have to be copied but only the ones being renamed should be marked with an attributes**
|
||
|
||
<details>
|
||
|
||
**Generated code before (Generated/Models/Colors.cs):**
|
||
|
||
``` C#
|
||
namespace Azure.Service.Models
|
||
{
|
||
public enum Colors
|
||
{
|
||
Red,
|
||
Green,
|
||
Blue
|
||
}
|
||
}
|
||
```
|
||
|
||
**Add customized model (Colors.cs)**
|
||
|
||
``` C#
|
||
namespace Azure.Service.Models
|
||
{
|
||
public enum Colors
|
||
{
|
||
Red,
|
||
Green,
|
||
[CodeGenMember("Blue")]
|
||
SkyBlue
|
||
}
|
||
}
|
||
```
|
||
|
||
**Generated code after (Generated/Models/Model.cs):**
|
||
|
||
``` diff
|
||
-namespace Azure.Service.Models
|
||
-{
|
||
- public enum Colors
|
||
- {
|
||
- Red,
|
||
- Green,
|
||
- Blue
|
||
- }
|
||
-}
|
||
+// Serialization code uses the new SkyBlue member name
|
||
```
|
||
|
||
</details>
|
||
|
||
### Changing an enum to an extensible enum
|
||
|
||
Redefine an enum into an extensible enum by creating an empty struct with the same name as original enum.
|
||
|
||
<details>
|
||
|
||
**Generated code before (Generated/Models/Colors.cs):**
|
||
|
||
``` C#
|
||
namespace Azure.Service.Models
|
||
{
|
||
public enum Colors
|
||
{
|
||
Red,
|
||
Green
|
||
}
|
||
}
|
||
```
|
||
|
||
**Add customized model (Colors.cs)**
|
||
|
||
``` C#
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial struct Colors
|
||
{
|
||
}
|
||
}
|
||
```
|
||
|
||
**Generated code after (Generated/Models/Model.cs):**
|
||
|
||
``` diff
|
||
namespace Azure.Service.Models
|
||
{
|
||
- public enum Colors
|
||
- {
|
||
- Red,
|
||
- Green
|
||
- }
|
||
+ public readonly partial struct Colors : IEquatable<Colors>
|
||
+ {
|
||
+ private readonly string _value;
|
||
|
||
+ public Colors(string value)
|
||
+ {
|
||
+ _value = value ?? throw new ArgumentNullException(nameof(value));
|
||
+ }
|
||
|
||
+ private const string Red = "red";
|
||
+ private const string Green = "green";
|
||
|
||
+ public static Colors Red { get; } = new Colors(Red);
|
||
+ public static Colors Green { get; } = new Colors(Green);
|
||
+ public static bool operator ==(Colors left, Colors right) => left.Equals(right);
|
||
...
|
||
}
|
||
```
|
||
|
||
</details>
|
||
|
||
### Make a client internal
|
||
|
||
Define a class with the same namespace and name as generated client and use the desired accessibility.
|
||
|
||
<details>
|
||
|
||
**Generated code before (Generated/Operations/ServiceClient.cs):**
|
||
|
||
``` C#
|
||
namespace Azure.Service.Operations
|
||
{
|
||
public partial class ServiceClient { }
|
||
}
|
||
```
|
||
|
||
**Add customized model (Model.cs)**
|
||
|
||
``` C#
|
||
namespace Azure.Service.Operations
|
||
{
|
||
internal partial class ServiceClient { }
|
||
}
|
||
```
|
||
|
||
**Generated code after (Generated/Operations/ServiceClient.cs):**
|
||
|
||
``` diff
|
||
namespace Azure.Service.Operations
|
||
{
|
||
- public partial class ServiceClient { }
|
||
+ internal partial class ServiceClient { }
|
||
}
|
||
```
|
||
|
||
</details>
|
||
|
||
|
||
### Rename a client
|
||
|
||
Define a partial client class with a new name and mark it with `[CodeGenClient("OriginalName")]`
|
||
|
||
<details>
|
||
|
||
**Generated code before (Generated/Operations/ServiceClient.cs):**
|
||
|
||
``` C#
|
||
namespace Azure.Service.Operations
|
||
{
|
||
public partial class ServiceClient {}
|
||
}
|
||
```
|
||
|
||
**Add customized model (Model.cs)**
|
||
|
||
``` C#
|
||
namespace Azure.Service.Operations
|
||
{
|
||
[CodeGenClient("ServiceClient")]
|
||
public partial class TableClient { }
|
||
}
|
||
```
|
||
|
||
**Generated code after (Generated/Operations/ServiceClient.cs):**
|
||
|
||
``` diff
|
||
namespace Azure.Service.Operations
|
||
{
|
||
- public partial class ServiceClient { }
|
||
+ public partial class TableClient { }
|
||
}
|
||
```
|
||
|
||
</details>
|
||
|
||
|
||
### Replace any generated member
|
||
|
||
Works for model and client properties, methods, constructors etc.
|
||
|
||
Define a partial class with member with the same name and for methods same parameters.
|
||
|
||
<details>
|
||
|
||
**Generated code before (Generated/Models/Model.cs):**
|
||
|
||
``` C#
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Model
|
||
{
|
||
public Model()
|
||
{
|
||
Property = "a";
|
||
}
|
||
|
||
public string Property { get; set; }
|
||
}
|
||
}
|
||
```
|
||
|
||
**Add customized model (Model.cs)**
|
||
|
||
``` C#
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Model
|
||
{
|
||
internal Model()
|
||
{
|
||
Property = "b";
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**Generated code after (Generated/Models/Model.cs):**
|
||
|
||
``` diff
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Model
|
||
{
|
||
- public Model()
|
||
- {
|
||
- Property = "a";
|
||
- }
|
||
}
|
||
}
|
||
```
|
||
|
||
</details>
|
||
|
||
### Remove any generated member
|
||
|
||
Works for model and client properties, methods, constructors etc.
|
||
|
||
Define a partial class with `[CodeGenSuppress("NameOfMember", typeof(Parameter1Type), typeof(Parameter2Type))]` attribute.
|
||
|
||
<details>
|
||
|
||
**Generated code before (Generated/Models/Model.cs):**
|
||
|
||
``` C#
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Model
|
||
{
|
||
public Model()
|
||
{
|
||
Property = "a";
|
||
}
|
||
|
||
public Model(string property)
|
||
{
|
||
Property = property;
|
||
}
|
||
|
||
public string Property { get; set; }
|
||
}
|
||
}
|
||
```
|
||
|
||
**Add customized model (Model.cs)**
|
||
|
||
``` C#
|
||
namespace Azure.Service.Models
|
||
{
|
||
[CodeGenSuppress("Model", typeof(string))]
|
||
public partial class Model
|
||
{
|
||
}
|
||
}
|
||
```
|
||
|
||
**Generated code after (Generated/Models/Model.cs):**
|
||
|
||
``` diff
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Model
|
||
{
|
||
- public Model(string property)
|
||
- {
|
||
- Property = property;
|
||
- }
|
||
}
|
||
}
|
||
```
|
||
|
||
</details>
|
||
|
||
### Change model namespace or accessibility in bulk
|
||
|
||
<details>
|
||
|
||
**Generated code before:**
|
||
|
||
``` C#
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Model1 {}
|
||
public partial class Model2 {}
|
||
public partial class Model3 {}
|
||
public partial class Model4 {}
|
||
}
|
||
```
|
||
|
||
**Add autorest.md transformation**
|
||
|
||
```
|
||
directive:
|
||
from: swagger-document
|
||
where: $.definitions.*
|
||
transform: >
|
||
$["x-namespace"] = "Azure.Search.Documents.Indexes.Models"
|
||
$["x-accessibility"] = "internal"
|
||
```
|
||
|
||
**Generated code after:**
|
||
|
||
``` diff
|
||
-namespace Azure.Service.Models
|
||
+namespace Azure.Search.Documents.Indexes.Models
|
||
{
|
||
- public partial class Model1 {}
|
||
+ internal partial class Model1 {}
|
||
- public partial class Model2 {}
|
||
+ internal partial class Model2 {}
|
||
- public partial class Model3 {}
|
||
+ internal partial class Model3 {}
|
||
- public partial class Model4 {}
|
||
+ internal partial class Model4 {}
|
||
}
|
||
```
|
||
|
||
</details>
|
||
|
||
### Change operation accessibility in bulk
|
||
|
||
<details>
|
||
|
||
**Generated code before (Generated/Client.cs):**
|
||
|
||
``` C#
|
||
public virtual Response Operation(string body = null, CancellationToken cancellationToken = default)
|
||
public virtual async Task<Response> OperationAsync(string body = null, CancellationToken cancellationToken = default)
|
||
```
|
||
|
||
**Add autorest.md transformation**
|
||
|
||
```
|
||
directive:
|
||
- from: swagger-document
|
||
where: $..[?(@.operationId=='Operation')]
|
||
transform: >
|
||
$["x-accessibility"] = "internal";
|
||
```
|
||
|
||
**Generated code after (Generated/Client.cs):**
|
||
|
||
``` C#
|
||
internal virtual Response Operation(string body = null, CancellationToken cancellationToken = default)
|
||
internal virtual async Task<Response> OperationAsync(string body = null, CancellationToken cancellationToken = default)
|
||
```
|
||
|
||
</details>
|
||
|
||
### Exclude models from namespace
|
||
|
||
<details>
|
||
|
||
**Generated code before (Generated/Models/Model.cs):**
|
||
|
||
``` C#
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Model { }
|
||
}
|
||
```
|
||
|
||
**Add `model-namespace` in autorest.md**
|
||
|
||
```
|
||
model-namespace: false
|
||
input-file: "swagger-document"
|
||
```
|
||
|
||
**Generated code after (Generated/Models/Model.cs):**
|
||
|
||
``` diff
|
||
- namespace Azure.Service.Models
|
||
+ namespace Azure.Service
|
||
{
|
||
public partial class Model { }
|
||
}
|
||
```
|
||
</details>
|
||
|
||
|
||
### Extending a model with additional constructors
|
||
|
||
<details>
|
||
|
||
As with most customization, you can define a partial class for Models and extend them with methods and constructors.
|
||
|
||
**Generated code before (Generated/Models/Model.cs):**
|
||
|
||
```csharp
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Model { }
|
||
}
|
||
```
|
||
|
||
**Add customized model (Model.cs)**
|
||
|
||
```csharp
|
||
namespace Azure.Service.Models
|
||
{
|
||
public partial class Model {
|
||
public Model(int x)
|
||
{
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
</details>
|
||
|
||
### Change the file license header
|
||
|
||
<details>
|
||
Add the `--custom-header="// Your header content\n"` option to the autorest command, or add the following configuration in your `md` file for autorest
|
||
```yaml
|
||
custom-header: "// Your header content\n"
|
||
```
|
||
to change the file license headers in all generated files.
|
||
</details>
|
||
|
||
<details>
|
||
<summary>Repository-specific pipeline configuration</summary>
|
||
|
||
```yaml
|
||
# autorest-core version
|
||
version: 3.9.7
|
||
save-inputs: true
|
||
use: $(this-folder)/artifacts/bin/AutoRest.CSharp/Debug/net8.0/
|
||
clear-output-folder: true
|
||
public-clients: true
|
||
skip-csproj-packagereference: true
|
||
```
|
||
|
||
</details>
|
||
|
||
## Management plane concepts and configurations
|
||
|
||
See the documentation [here](./docs/mgmt/readme.md)
|