Repository: dotnet/efcore
File: src/EFCore/ChangeTracking/EntityEntry.cs (C#)

Code before:
    ///         in the database.
    ///     </para>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-entity-entries">Accessing tracked entities in EF Core</see> for more information and
    ///         examples.
    ///     </para>
    /// </remarks>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation. The task result contains the store values,
    ///     or <see langword="null" /> if the entity does not exist in the database.
    /// </returns>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public virtual async Task<PropertyValues?> GetDatabaseValuesAsync(CancellationToken cancellationToken = default)
    {
        var values = await Finder.GetDatabaseValuesAsync(InternalEntry, cancellationToken).ConfigureAwait(false);
        var nullComplexPropertyFlags = values == null ? null : CreateNullComplexPropertyFlags(InternalEntry, values);

        return values == null ? null : new ArrayPropertyValues(InternalEntry, values, nullComplexPropertyFlags);
    }

    private static bool[]? CreateNullComplexPropertyFlags(InternalEntityEntry entry, object?[] values)
    {
        List<IComplexProperty>? nullableComplexProperties = null;
        foreach (var complexProperty in entry.StructuralType.GetFlattenedComplexProperties())
        {
            if (complexProperty.IsCollection || !complexProperty.IsNullable || complexProperty.IsShadowProperty())
            {
                continue;
            }

            (nullableComplexProperties ??= []).Add(complexProperty);
        }

        if (nullableComplexProperties == null)
        {
            return null;
        }

        var flags = new bool[nullableComplexProperties.Count];
        for (var i = 0; i < nullableComplexProperties.Count; i++)
        {
            var complexProperty = nullableComplexProperties[i];
            var scalarProperties = complexProperty.ComplexType.GetFlattenedProperties();

            IProperty? requiredProperty = null;
            foreach (var property in scalarProperties)

Review comment (suggestion):
Add a constructor to `ArrayPropertyValues` that doesn't take `nullComplexPropertyFlags` and put the logic below in it. It will have access to `PropertyValues.NullableComplexProperties` so it can be simplified

Code after:
    ///         in the database.
    ///     </para>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-entity-entries">Accessing tracked entities in EF Core</see> for more information and
    ///         examples.
    ///     </para>
    /// </remarks>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation. The task result contains the store values,
    ///     or <see langword="null" /> if the entity does not exist in the database.
    /// </returns>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public virtual async Task<PropertyValues?> GetDatabaseValuesAsync(CancellationToken cancellationToken = default)
    {
        var values = await Finder.GetDatabaseValuesAsync(InternalEntry, cancellationToken).ConfigureAwait(false);

        // ArrayPropertyValues will compute null-complex-property flags from the returned value buffer.
        return values == null ? null : new ArrayPropertyValues(InternalEntry, values);
    }

    /// <summary>
    ///     Reloads the entity from the database overwriting any property values with values from the database.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         The entity will be in the <see cref="EntityState.Unchanged" /> state after calling this method,
    ///         unless the entity does not exist in the database, in which case the entity will be
    ///         <see cref="EntityState.Detached" />. Finally, calling Reload on an <see cref="EntityState.Added" />
    ///         entity that does not exist in the database is a no-op. Note, however, that an Added entity may
    ///         not yet have had its permanent key value created.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-entity-entries">Accessing tracked entities in EF Core</see> for more information and
    ///         examples.
    ///     </para>
    /// </remarks>
    public virtual void Reload()
        => Reload(GetDatabaseValues());

    /// <summary>
    ///     Reloads the entity from the database overwriting any property values with values from the database.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         The entity will be in the <see cref="EntityState.Unchanged" /> state after calling this method,
    ///         unless the entity does not exist in the database, in which case the entity will be