Skip to content

ClientModel Prototype: Nullable annotations in clients#41952

Closed
annelo-msft wants to merge 8 commits intoAzure:feature/core-experimentfrom
annelo-msft:core2-testclient-nullable
Closed

ClientModel Prototype: Nullable annotations in clients#41952
annelo-msft wants to merge 8 commits intoAzure:feature/core-experimentfrom
annelo-msft:core2-testclient-nullable

Conversation

@annelo-msft
Copy link
Copy Markdown
Member

@azure-sdk
Copy link
Copy Markdown
Collaborator

azure-sdk commented Feb 13, 2024

API change check

API changes are not detected in this pull request.

private readonly string _apiVersion;

public MapsClient(Uri endpoint, ApiKeyCredential credential, MapsClientOptions options = default)
public MapsClient(Uri endpoint, ApiKeyCredential credential, MapsClientOptions? options = default)
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ClientOptions become nullable.

}

public virtual async Task<ClientResult> GetCountryCodeAsync(string ipAddress, RequestOptions options = null)
public virtual async Task<ClientResult> GetCountryCodeAsync(string ipAddress, RequestOptions? options = null)
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RequestOptions become nullable.

}

internal ModelX(string kind, string name, int xProperty, int? nullProperty, IList<string> fields, IDictionary<string, string> keyValuePairs, Dictionary<string, BinaryData> rawData)
internal ModelX(string kind, string? name, int xProperty, int? nullProperty, IList<string> fields, IDictionary<string, string> keyValuePairs, Dictionary<string, BinaryData> rawData)
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional reference type properties become nullable in the serialization constructor.

public ModelX() : base(null)
{
Kind = "X";
Fields = new List<string>();
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-nullable properties must either be initialized or follow the assert pattern.

if (modelX == null)
{
return null;
throw new ArgumentNullException(nameof(modelX));
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-nullable models throw ArgumentNullException in methods that expect them to be non-null.

{
writer.WritePropertyName("nullProperty"u8);
writer.WriteNumberValue(NullProperty.Value);
writer.WriteNumberValue(NullProperty!.Value);
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We ignore the nullability annotation if we've already done a null check.

}

internal static ModelX DeserializeModelX(JsonElement element, ModelReaderWriterOptions options = default)
internal static ModelX DeserializeModelX(JsonElement element, ModelReaderWriterOptions? options = default)
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

options are nullable.

if (element.ValueKind == JsonValueKind.Null)
{
return null;
throw new JsonException($"Invalid JSON provided to deserialize type '{nameof(ModelX)}'");
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-nullable model deserialize routines throw JsonException if JSON contains null.

}
string kind = default;

string? kind = default;
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Required properties are declared as nullable and then later checked to make sure they have a value before passing them to the type's constructor.

if (property.NameEquals("fields"u8))
{
fields = property.Value.EnumerateArray().Select(element => element.GetString()).ToList();
// TODO: May do something different depending on the semantics of expected values
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Collections should be clear about whether they can contain nullable values or not. They would throw JsonException if values shouldn't be null but the JSON provides null for their values.

}
}

if (kind is null)
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We check that required property values have been populated from JSON, or throw if they have not.

//}

private int? _xProperty;
public int XProperty
Copy link
Copy Markdown
Member Author

@annelo-msft annelo-msft Feb 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Required property pattern for value types.

This will throw at serialization-time if the user creating the type doesn't set a value required for serialization.


// <auto-generated/>

#nullable disable
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These aren't needed one way or another because the <auto-generated/> comment opts-out of nullability annotations per: https://learn.microsoft.com/en-us/dotnet/csharp/nullable-migration-strategies

private string? _name;
/// <summary> The name of the resource. </summary>
public string Name { get; }
public string Name
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assert pattern for required value types.


/// <summary> Azure Resource Manager metadata containing createdBy and modifiedBy information. </summary>
public SystemData SystemData { get; }
public SystemData? SystemData { get; }
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Example nullable nested model.

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

// <auto-generated/>
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For ClientModel-based client models, we need to either remove this line or explicitly set #nullable enable per https://learn.microsoft.com/en-us/dotnet/csharp/nullable-migration-strategies

@annelo-msft
Copy link
Copy Markdown
Member Author

Closing in favor of #41968

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants