diff --git a/CoreRemoting.Tests/RpcTests.cs b/CoreRemoting.Tests/RpcTests.cs index 8893746..2e44ba7 100644 --- a/CoreRemoting.Tests/RpcTests.cs +++ b/CoreRemoting.Tests/RpcTests.cs @@ -538,6 +538,50 @@ public void NonSerializableError_method_throws_Exception() } } + [Fact] + public void AfterCall_event_handler_can_translate_exceptions_to_improve_diagnostics() + { + // replace cryptic database error report with a user-friendly error message + void AfterCall(object sender, ServerRpcContext ctx) + { + var errorMsg = ctx.Exception?.Message ?? string.Empty; + if (errorMsg.StartsWith("23503:")) + ctx.Exception = new Exception("Deleting clients is not allowed.", + ctx.Exception); + } + + _serverFixture.Server.AfterCall += AfterCall; + try + { + using var client = new RemotingClient(new ClientConfig() + { + ConnectionTimeout = 5, + InvocationTimeout = 5, + SendTimeout = 5, + Channel = ClientChannel, + MessageEncryption = false, + ServerPort = _serverFixture.Server.Config.NetworkPort + }); + + client.Connect(); + + // simulate a database error on the server-side + var proxy = client.CreateProxy(); + var ex = Assert.Throws(() => + proxy.Error("23503: delete from table 'clients' violates foreign key constraint 'order_client_fk' on table 'orders'")) + .GetInnermostException(); + + Assert.NotNull(ex); + Assert.Equal("Deleting clients is not allowed.", ex.Message); + } + finally + { + // reset the error counter for other tests + _serverFixture.ServerErrorCount = 0; + _serverFixture.Server.AfterCall -= AfterCall; + } + } + [Fact] public void Failing_component_constructor_throws_RemoteInvocationException() { diff --git a/CoreRemoting/ClientRpcContext.cs b/CoreRemoting/ClientRpcContext.cs index 5050d15..b70eea6 100644 --- a/CoreRemoting/ClientRpcContext.cs +++ b/CoreRemoting/ClientRpcContext.cs @@ -17,17 +17,17 @@ internal ClientRpcContext() UniqueCallKey = Guid.NewGuid(); WaitHandle = new EventWaitHandle(initialState: false, EventResetMode.ManualReset); } - + /// /// Gets the unique key of RPC call. /// public Guid UniqueCallKey { get; } - + /// /// Gets or sets the result message, that was received from server after the call was invoked on server side. /// public MethodCallResultMessage ResultMessage { get; set; } - + /// /// Gets or sets whether this RPC call is in error state. /// @@ -37,12 +37,12 @@ internal ClientRpcContext() /// Gets or sets an exception that describes an error that occurred on server side RPC invocation. /// public RemoteInvocationException RemoteException { get; set; } - + /// /// Gets a wait handle that is set, when the response of this RPC call is received from server. /// public EventWaitHandle WaitHandle { get; } // TODO: replace with a Task? - + /// /// Frees managed resources. ///