diff --git a/src/Components/Blazor/Build/test/RuntimeDependenciesResolverTest.cs b/src/Components/Blazor/Build/test/RuntimeDependenciesResolverTest.cs
index bf96e57a93..dd3456d99d 100644
--- a/src/Components/Blazor/Build/test/RuntimeDependenciesResolverTest.cs
+++ b/src/Components/Blazor/Build/test/RuntimeDependenciesResolverTest.cs
@@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Blazor.Build.Test
{
public class RuntimeDependenciesResolverTest
{
- [ConditionalFact]
+ [ConditionalFact(Skip = " https://github.com/aspnet/AspNetCore/issues/12059")]
[SkipOnHelix("https://github.com/aspnet/AspNetCore/issues/10426")]
public void FindsReferenceAssemblyGraph_ForStandaloneApp()
{
diff --git a/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.cs b/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.cs
index 93ae0a2b02..a5404c911e 100644
--- a/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.cs
+++ b/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.cs
@@ -154,8 +154,6 @@ namespace Microsoft.AspNetCore.Components
public readonly partial struct ElementReference
{
private readonly object _dummy;
- [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
- public string __internalId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public readonly partial struct EventCallback
diff --git a/src/Components/Components/src/ElementReference.cs b/src/Components/Components/src/ElementReference.cs
index be85bffab0..92affa1249 100644
--- a/src/Components/Components/src/ElementReference.cs
+++ b/src/Components/Components/src/ElementReference.cs
@@ -2,7 +2,9 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
-using System.ComponentModel;
+using System.Globalization;
+using System.Text.Json;
+using System.Text.Json.Serialization;
using System.Threading;
namespace Microsoft.AspNetCore.Components
@@ -10,25 +12,23 @@ namespace Microsoft.AspNetCore.Components
///
/// Represents a reference to a rendered element.
///
+ [JsonConverter(typeof(ElementReferenceConverter))]
public readonly struct ElementReference
{
private static long _nextIdForWebAssemblyOnly = 1;
///
- /// Gets a unique identifier for .
+ /// Gets a unique identifier for .
///
///
/// The Id is unique at least within the scope of a given user/circuit.
/// This property is public to support Json serialization and should not be used by user code.
///
- [EditorBrowsable(EditorBrowsableState.Never)]
- public string __internalId { get; }
-
- internal string Id => __internalId;
+ internal string Id { get; }
private ElementReference(string id)
{
- __internalId = id;
+ Id = id;
}
internal static ElementReference CreateWithUniqueId()
@@ -45,13 +45,56 @@ namespace Microsoft.AspNetCore.Components
// fields for ElementRefCaptureId, of which only one would be in use depending
// on the platform.
var id = Interlocked.Increment(ref _nextIdForWebAssemblyOnly);
- return id.ToString();
+ return id.ToString(CultureInfo.InvariantCulture);
}
else
{
// For remote rendering, it's important not to disclose any cross-user state,
// such as the number of IDs that have been assigned.
- return Guid.NewGuid().ToString("D");
+ return Guid.NewGuid().ToString("D", CultureInfo.InvariantCulture);
+ }
+ }
+
+ private sealed class ElementReferenceConverter : JsonConverter
+ {
+ private static readonly JsonEncodedText IdProperty = JsonEncodedText.Encode("__internalId");
+
+ public override ElementReference Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ string id = null;
+ while (reader.Read() && reader.TokenType != JsonTokenType.EndObject)
+ {
+ if (reader.TokenType == JsonTokenType.PropertyName)
+ {
+ if (reader.ValueTextEquals(IdProperty.EncodedUtf8Bytes))
+ {
+ reader.Read();
+ id = reader.GetString();
+ }
+ else
+ {
+ throw new JsonException($"Unexpected JSON property '{reader.GetString()}'.");
+ }
+ }
+ else
+ {
+ throw new JsonException($"Unexcepted JSON Token {reader.TokenType}.");
+ }
+ }
+
+ if (id is null)
+ {
+ throw new JsonException("__internalId is required.");
+ }
+
+ return new ElementReference(id);
+ }
+
+ public override void Write(Utf8JsonWriter writer, ElementReference value, JsonSerializerOptions options)
+ {
+ writer.WriteStartObject();
+ writer.WriteString(IdProperty, value.Id);
+ writer.WriteEndObject();
}
}
}
diff --git a/src/Components/Components/test/ElementReferenceTest.cs b/src/Components/Components/test/ElementReferenceTest.cs
new file mode 100644
index 0000000000..39c947aaf0
--- /dev/null
+++ b/src/Components/Components/test/ElementReferenceTest.cs
@@ -0,0 +1,82 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Text.Json;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Components
+{
+ public class ElementReferenceTest
+ {
+ [Fact]
+ public void Serializing_Works()
+ {
+ // Arrange
+ var elementReference = ElementReference.CreateWithUniqueId();
+ var expected = $"{{\"__internalId\":\"{elementReference.Id}\"}}";
+
+ // Act
+ var json = JsonSerializer.Serialize(elementReference, JsonSerializerOptionsProvider.Options);
+
+ // Assert
+ Assert.Equal(expected, json);
+ }
+
+ [Fact]
+ public void Deserializing_Works()
+ {
+ // Arrange
+ var id = ElementReference.CreateWithUniqueId().Id;
+ var json = $"{{\"__internalId\":\"{id}\"}}";
+
+ // Act
+ var elementReference = JsonSerializer.Deserialize(json, JsonSerializerOptionsProvider.Options);
+
+ // Assert
+ Assert.Equal(id, elementReference.Id);
+ }
+
+ [Fact]
+ public void Deserializing_WithFormatting_Works()
+ {
+ // Arrange
+ var id = ElementReference.CreateWithUniqueId().Id;
+ var json =
+@$"{{
+ ""__internalId"": ""{id}""
+}}";
+
+ // Act
+ var elementReference = JsonSerializer.Deserialize(json, JsonSerializerOptionsProvider.Options);
+
+ // Assert
+ Assert.Equal(id, elementReference.Id);
+ }
+
+ [Fact]
+ public void Deserializing_Throws_IfUnknownPropertyAppears()
+ {
+ // Arrange
+ var json = "{\"id\":\"some-value\"}";
+
+ // Act
+ var ex = Assert.Throws(() => JsonSerializer.Deserialize(json, JsonSerializerOptionsProvider.Options));
+
+ // Assert
+ Assert.Equal("Unexpected JSON property 'id'.", ex.Message);
+ }
+
+ [Fact]
+ public void Deserializing_Throws_IfIdIsNotSpecified()
+ {
+ // Arrange
+ var json = "{}";
+
+ // Act
+ var ex = Assert.Throws(() => JsonSerializer.Deserialize(json, JsonSerializerOptionsProvider.Options));
+
+ // Assert
+ Assert.Equal("__internalId is required.", ex.Message);
+ }
+ }
+}