diff --git a/src/Abstractions/Entities/TaskEntityOperationExtensions.cs b/src/Abstractions/Entities/TaskEntityOperationExtensions.cs index 8cff9b66..085bddac 100644 --- a/src/Abstractions/Entities/TaskEntityOperationExtensions.cs +++ b/src/Abstractions/Entities/TaskEntityOperationExtensions.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.Reflection; +using System.Runtime.ExceptionServices; namespace Microsoft.DurableTask.Entities; @@ -74,9 +75,18 @@ internal static bool TryDispatch( i++; } - result = method.Invoke(target, inputs); - returnType = method.ReturnType; - return true; + try + { + result = method.Invoke(target, inputs); + returnType = method.ReturnType; + return true; + } + catch (TargetInvocationException ex) when (ex.InnerException is not null) + { + // Re-throw the inner exception so that the stack trace is preserved. + ExceptionDispatchInfo.Capture(ex.InnerException).Throw(); + throw; // Unreachable. + } static void ThrowIfDuplicateBinding( ParameterInfo? existing, ParameterInfo parameter, string bindingConcept, TaskEntityOperation operation) diff --git a/test/Abstractions.Tests/Entities/EntityTaskEntityTests.cs b/test/Abstractions.Tests/Entities/EntityTaskEntityTests.cs index 4ca908b4..f441d9a2 100644 --- a/test/Abstractions.Tests/Entities/EntityTaskEntityTests.cs +++ b/test/Abstractions.Tests/Entities/EntityTaskEntityTests.cs @@ -153,6 +153,21 @@ public async Task ExplicitDelete_Overridden(string op) operation.State.GetState(typeof(int)).Should().Be(0); } + [Fact] + public async Task Throws_ExceptionPreserved() + { + string error = "this message should be preserved"; + TestEntityOperation operation = new("throws", error); + TestEntity entity = new(); + + Func act = async () => await entity.RunAsync(operation); + + await act.Should() + .ThrowAsync() + .WithMessage(error) + .Where(x => x.StackTrace!.StartsWith(" at Microsoft.DurableTask.Entities.Tests.EntityTaskEntityTests.TestEntity.Throws(String message)")); + } + #pragma warning disable CA1822 // Mark members as static #pragma warning disable IDE0060 // Remove unused parameter class TestEntity : TaskEntity @@ -222,6 +237,11 @@ static async Task Slow() return sync ? new("success") : new(Slow()); } + public void Throws(string message) + { + throw new InvalidOperationException(message); + } + int Add(int? value, Optional context) { if (context.HasValue)