diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentId.cs b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentId.cs index 41545224889c9..1c063cc6d1f05 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentId.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentId.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Runtime.Serialization; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; @@ -15,11 +16,14 @@ namespace Microsoft.CodeAnalysis /// workspace. /// [DebuggerDisplay("{GetDebuggerDisplay(),nq}")] + [DataContract] public sealed class DocumentId : IEquatable, IObjectWritable { + [DataMember(Order = 0)] public ProjectId ProjectId { get; } + [DataMember(Order = 1)] public Guid Id { get; } - + [DataMember(Order = 2)] private readonly string? _debugName; private DocumentId(ProjectId projectId, Guid guid, string? debugName) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectId.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectId.cs index 1a1bfd594d1db..642bc0823659b 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectId.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectId.cs @@ -5,23 +5,34 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Runtime.Serialization; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis { +#pragma warning disable CA1200 // Avoid using cref tags with a prefix /// /// An identifier that can be used to refer to the same across versions. /// + /// + /// This supports the general message-pack of being serializable. However, in + /// practice, this is not serialized directly, but through the use of a custom formatter + /// +#pragma warning restore CA1200 // Avoid using cref tags with a prefix [DebuggerDisplay("{GetDebuggerDisplay(),nq}")] + [DataContract] public sealed class ProjectId : IEquatable, IObjectWritable { - private readonly string? _debugName; - /// /// The system generated unique id. /// + [DataMember(Order = 0)] public Guid Id { get; } + [DataMember(Order = 1)] + private readonly string? _debugName; + private ProjectId(Guid guid, string? debugName) { this.Id = guid; diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionId.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionId.cs index a3bd146fdffa7..4cc4b626221f4 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionId.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionId.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Runtime.Serialization; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis @@ -15,13 +16,16 @@ namespace Microsoft.CodeAnalysis /// An identifier that can be used to refer to the same Solution across versions. /// [DebuggerDisplay("{GetDebuggerDisplay(),nq}")] + [DataContract] public sealed class SolutionId : IEquatable, IObjectWritable { /// /// The unique id of the solution. /// + [DataMember(Order = 0)] public Guid Id { get; } + [DataMember(Order = 1)] private readonly string _debugName; private SolutionId(Guid id, string debugName) diff --git a/src/Workspaces/Remote/Core/Serialization/MessagePackFormatters.cs b/src/Workspaces/Remote/Core/Serialization/MessagePackFormatters.cs index 8ce5e584f6360..e6ede9554549f 100644 --- a/src/Workspaces/Remote/Core/Serialization/MessagePackFormatters.cs +++ b/src/Workspaces/Remote/Core/Serialization/MessagePackFormatters.cs @@ -23,9 +23,7 @@ namespace Microsoft.CodeAnalysis.Remote internal sealed class MessagePackFormatters { internal static readonly ImmutableArray Formatters = ImmutableArray.Create( - SolutionIdFormatter.Instance, ProjectIdFormatter.Instance, - DocumentIdFormatter.Instance, // ForceTypelessFormatter needs to be listed here for each Roslyn abstract type T that is being serialized OOP. // TODO: add a resolver that provides these https://github.com/dotnet/roslyn/issues/60724 new ForceTypelessFormatter(), @@ -41,56 +39,15 @@ internal sealed class MessagePackFormatters internal static IFormatterResolver CreateResolver(ImmutableArray additionalFormatters, ImmutableArray additionalResolvers) => (additionalFormatters.IsEmpty && additionalResolvers.IsEmpty) ? DefaultResolver : CompositeResolver.Create(Formatters.AddRange(additionalFormatters), s_resolvers.AddRange(additionalResolvers)); - internal sealed class SolutionIdFormatter : IMessagePackFormatter - { - public static readonly SolutionIdFormatter Instance = new SolutionIdFormatter(); - - public SolutionId? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) - { - try - { - if (reader.TryReadNil()) - { - return null; - } - - Contract.ThrowIfFalse(reader.ReadArrayHeader() == 2); - var id = GuidFormatter.Instance.Deserialize(ref reader, options); - var debugName = reader.ReadString(); - - return SolutionId.CreateFromSerialized(id, debugName); - } - catch (Exception e) when (e is not MessagePackSerializationException) - { - throw new MessagePackSerializationException(e.Message, e); - } - } - - public void Serialize(ref MessagePackWriter writer, SolutionId? value, MessagePackSerializerOptions options) - { - try - { - if (value is null) - { - writer.WriteNil(); - } - else - { - writer.WriteArrayHeader(2); - GuidFormatter.Instance.Serialize(ref writer, value.Id, options); - writer.Write(value.DebugName); - } - } - catch (Exception e) when (e is not MessagePackSerializationException) - { - throw new MessagePackSerializationException(e.Message, e); - } - } - } - + /// + /// Specialized formatter used so we can cache and reuse instances. This is valuable as + /// it's very common for a set of results to reuse the same ProjectId across long sequences of results + /// containing s. This allows a single instance to be created and shared across that + /// entire sequence, saving on allocations. + /// internal sealed class ProjectIdFormatter : IMessagePackFormatter { - public static readonly ProjectIdFormatter Instance = new ProjectIdFormatter(); + public static readonly ProjectIdFormatter Instance = new(); /// /// Keep a copy of the most recent project ID to avoid duplicate instances when many consecutive IDs @@ -150,57 +107,5 @@ public void Serialize(ref MessagePackWriter writer, ProjectId? value, MessagePac } } } - - internal sealed class DocumentIdFormatter : IMessagePackFormatter - { - public static readonly DocumentIdFormatter Instance = new DocumentIdFormatter(); - - public DocumentId? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) - { - try - { - if (reader.TryReadNil()) - { - return null; - } - - Contract.ThrowIfFalse(reader.ReadArrayHeader() == 3); - - var projectId = ProjectIdFormatter.Instance.Deserialize(ref reader, options); - Contract.ThrowIfNull(projectId); - - var id = GuidFormatter.Instance.Deserialize(ref reader, options); - var debugName = reader.ReadString(); - - return DocumentId.CreateFromSerialized(projectId, id, debugName); - } - catch (Exception e) when (e is not MessagePackSerializationException) - { - throw new MessagePackSerializationException(e.Message, e); - } - } - - public void Serialize(ref MessagePackWriter writer, DocumentId? value, MessagePackSerializerOptions options) - { - try - { - if (value is null) - { - writer.WriteNil(); - } - else - { - writer.WriteArrayHeader(3); - ProjectIdFormatter.Instance.Serialize(ref writer, value.ProjectId, options); - GuidFormatter.Instance.Serialize(ref writer, value.Id, options); - writer.Write(value.DebugName); - } - } - catch (Exception e) when (e is not MessagePackSerializationException) - { - throw new MessagePackSerializationException(e.Message, e); - } - } - } } }