From 1beed5524cc2632ddcd66888ca86a236a1788cd9 Mon Sep 17 00:00:00 2001 From: rcj1 Date: Thu, 16 Apr 2026 12:57:51 -0700 Subject: [PATCH 1/3] Update ContractRegistry.cs --- .../ContractRegistry.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/ContractRegistry.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/ContractRegistry.cs index ddd8c9e2ff9d53..200be4fe517165 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/ContractRegistry.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/ContractRegistry.cs @@ -3,6 +3,7 @@ using System; using Microsoft.Diagnostics.DataContractReader.Contracts; +using System.Diagnostics.CodeAnalysis; namespace Microsoft.Diagnostics.DataContractReader; @@ -118,6 +119,20 @@ public abstract class ContractRegistry public abstract TContract GetContract() where TContract : IContract; + public bool TryGetContract([NotNullWhen(true)] out TContract? contract) where TContract : IContract + { + try + { + contract = GetContract(); + return true; + } + catch (NotImplementedException) + { + contract = default; + return false; + } + } + /// /// Register a contract implementation for a specific version. /// External packages use this to add contract versions or entirely new contract interfaces. From 2138268433412236cb9791c324bef7ae637577cc Mon Sep 17 00:00:00 2001 From: Rachel Date: Thu, 16 Apr 2026 13:18:48 -0700 Subject: [PATCH 2/3] Update src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/ContractRegistry.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../ContractRegistry.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/ContractRegistry.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/ContractRegistry.cs index 200be4fe517165..f37b88990eb7f7 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/ContractRegistry.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/ContractRegistry.cs @@ -119,6 +119,19 @@ public abstract class ContractRegistry public abstract TContract GetContract() where TContract : IContract; + /// + /// Attempts to get an instance of the requested contract for the target. + /// + /// The contract type to retrieve. + /// + /// When this method returns , contains the requested contract instance; otherwise, . + /// + /// + /// if the requested contract is present and was retrieved successfully; if the contract is not present or registered and throws . + /// + /// + /// Any exception thrown by other than is not handled by this method and will propagate to the caller. + /// public bool TryGetContract([NotNullWhen(true)] out TContract? contract) where TContract : IContract { try From fba9889f5b9464c00ef73b9973bd86913a02bedf Mon Sep 17 00:00:00 2001 From: rcj1 Date: Thu, 16 Apr 2026 16:45:56 -0700 Subject: [PATCH 3/3] update to have base as TryGet --- .../ContractRegistry.cs | 32 +++++++++---------- .../CachingContractRegistry.cs | 29 +++++++++++++---- .../cdac/tests/TestPlaceholderTarget.cs | 28 ++++++++++------ 3 files changed, 57 insertions(+), 32 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/ContractRegistry.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/ContractRegistry.cs index f37b88990eb7f7..1715c7b7a39245 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/ContractRegistry.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/ContractRegistry.cs @@ -2,8 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using Microsoft.Diagnostics.DataContractReader.Contracts; using System.Diagnostics.CodeAnalysis; +using Microsoft.Diagnostics.DataContractReader.Contracts; namespace Microsoft.Diagnostics.DataContractReader; @@ -117,8 +117,6 @@ public abstract class ContractRegistry /// public virtual IDebugger Debugger => GetContract(); - public abstract TContract GetContract() where TContract : IContract; - /// /// Attempts to get an instance of the requested contract for the target. /// @@ -126,24 +124,26 @@ public abstract class ContractRegistry /// /// When this method returns , contains the requested contract instance; otherwise, . /// + /// + /// When this method returns , contains a human-readable explanation of why the contract could not be retrieved; otherwise, . + /// /// - /// if the requested contract is present and was retrieved successfully; if the contract is not present or registered and throws . + /// if the requested contract is present and was retrieved successfully; if the contract is not present or registered"/>. /// - /// - /// Any exception thrown by other than is not handled by this method and will propagate to the caller. - /// - public bool TryGetContract([NotNullWhen(true)] out TContract? contract) where TContract : IContract + public abstract bool TryGetContract([NotNullWhen(true)] out TContract contract, out string? failureReason) where TContract : IContract; + + public TContract GetContract() where TContract : IContract { - try + if (!TryGetContract(out TContract contract, out string? failureReason)) { - contract = GetContract(); - return true; - } - catch (NotImplementedException) - { - contract = default; - return false; + throw new NotImplementedException($"Contract '{typeof(TContract).Name}' is not supported by the target. Reason: {failureReason ?? "no reason provided"}"); } + return contract; + } + + public bool TryGetContract([NotNullWhen(true)] out TContract contract) where TContract : IContract + { + return TryGetContract(out contract, out _); } /// diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader/CachingContractRegistry.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader/CachingContractRegistry.cs index 10c9497447efb5..ca735474a15a7f 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader/CachingContractRegistry.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader/CachingContractRegistry.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using Microsoft.Diagnostics.DataContractReader.Contracts; namespace Microsoft.Diagnostics.DataContractReader; @@ -37,22 +38,36 @@ public override void Register(int version, Func cr _creators[(typeof(TContract), version)] = t => creator(t); } - public override TContract GetContract() + public override bool TryGetContract([NotNullWhen(true)] out TContract contract, out string? failureReason) { + contract = default!; + failureReason = null; if (_contracts.TryGetValue(typeof(TContract), out IContract? cached)) - return (TContract)cached; + { + contract = (TContract)cached; + return true; + } if (!_tryGetContractVersion(TContract.Name, out int version)) - throw new NotImplementedException($"Contract '{TContract.Name}' is not present in the contract descriptor."); + { + failureReason = $"Target does not support contract '{typeof(TContract).Name}'."; + return false; + } if (!_creators.TryGetValue((typeof(TContract), version), out Func? creator)) - throw new NotImplementedException($"No implementation registered for contract '{TContract.Name}' version {version}."); + { + failureReason = $"Target supports contract '{typeof(TContract).Name}' version {version}, but no implementation is registered for that version."; + return false; + } - TContract contract = (TContract)creator(_target); + contract = (TContract)creator(_target); if (_contracts.TryAdd(typeof(TContract), contract)) - return contract; + { + return true; + } - return (TContract)_contracts[typeof(TContract)]; + contract = (TContract)_contracts[typeof(TContract)]; + return true; } public override void Flush() diff --git a/src/native/managed/cdac/tests/TestPlaceholderTarget.cs b/src/native/managed/cdac/tests/TestPlaceholderTarget.cs index 8e9fc73072acaf..fdbe316b65fa68 100644 --- a/src/native/managed/cdac/tests/TestPlaceholderTarget.cs +++ b/src/native/managed/cdac/tests/TestPlaceholderTarget.cs @@ -521,30 +521,40 @@ public void SetMock(TContract mock) where TContract : IContract public override void Register(int version, Func creator) => _creators[(typeof(TContract), version)] = t => creator(t); - public override TContract GetContract() + public override bool TryGetContract([NotNullWhen(true)] out TContract contract, out string? failureReason) { + contract = default!; + failureReason = null; if (_resolved.TryGetValue(typeof(TContract), out var cached)) - return (TContract)cached; + { + contract = (TContract)cached; + return true; + } - IContract contract; + IContract resolved; if (_mocks.TryGetValue(typeof(TContract), out var mock)) { - contract = mock; + resolved = mock; } else if (_versions.TryGetValue(typeof(TContract), out int version)) { if (!_creators.TryGetValue((typeof(TContract), version), out var creator)) - throw new NotImplementedException($"No implementation registered for contract '{typeof(TContract).Name}' version {version}."); + { + failureReason = $"Target supports contract '{typeof(TContract).Name}' version {version}, but no implementation is registered for that version."; + return false; + } - contract = creator(_target); + resolved = creator(_target); } else { - throw new NotImplementedException($"Contract {typeof(TContract).Name} is not registered. Use SetVersion(version) or SetMock(mock) to configure contracts."); + failureReason = $"Contract '{typeof(TContract).Name}' is not supported by the target."; + return false; } - _resolved[typeof(TContract)] = contract; - return (TContract)contract; + _resolved[typeof(TContract)] = resolved; + contract = (TContract)resolved; + return true; } public override void Flush() { }