mirror of
https://github.com/clockworklabs/SpacetimeDB.git
synced 2026-05-12 18:57:51 -04:00
39f01289e5
# Description of Changes Implements the C# equivalent of #3638 This implement uses inheritance, where abstract base classes (like `ProcedureContextBase` in `ProcedureContext.cs`) store the core of the implementation, and then generated wrappers (like `ProcedureContext` in the generated FFI.cs file) inherit from them. For error handling, we work like Rust's implementation of `Result<T,E>` but we require `where E : Exception` because of how exceptions work in C#. Transaction-level failures come back as a `TxOutcome` and user errors should follow the `Result<T,E>` pattern. In this implementation, we have `UnwrapOrThrow()` throws exceptions directly because of C#'s error handling pattern. Unlike the Rust implementation's direct `Result` propagation, we are using an `AbortGuard` pattern (in `ProcedureContext.cs`) for exception handling, which uses `IDisposable` for automatic cleanup. Most changes should have fairly similar Rust-equivalents beyond that. For module authors, the changes here allow for the transation logic to work like: ```csharp ctx.TryWithTx<ResultType, Exception>(tx => { // transaction logic return Result<ResultType, Exception>.Ok(result); }); ``` This change includes a number of tests added to the `sdks/csharp/examples~/regression-tests/`'s `server` and `client` to validate the behavior of the changes. `server` changes provide further usage examples for module authors. # API and ABI breaking changes Should not be a breaking change # Expected complexity level and risk 2 # Testing - [x] Created Regression Tests that show transitions in procedures working in various ways, all of which pass.
78 lines
2.0 KiB
C#
78 lines
2.0 KiB
C#
namespace SpacetimeDB.Internal;
|
|
|
|
using System;
|
|
using System.IO;
|
|
using System.Text;
|
|
using SpacetimeDB.BSATN;
|
|
|
|
/// <summary>
|
|
/// Represents a procedure that can be registered and invoked by the module runtime.
|
|
/// </summary>
|
|
public interface IProcedure
|
|
{
|
|
/// <summary>
|
|
/// Creates a procedure definition for registration with the module system.
|
|
/// </summary>
|
|
RawProcedureDefV9 MakeProcedureDef(ITypeRegistrar registrar);
|
|
|
|
/// <summary>
|
|
/// Invokes the procedure with the given arguments and context.
|
|
/// </summary>
|
|
byte[] Invoke(BinaryReader reader, IProcedureContext ctx);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Represents the context for a procedure call.
|
|
/// </summary>
|
|
public interface IProcedureContext
|
|
{
|
|
/// <summary>
|
|
/// Gets the identity of the current procedure caller.
|
|
/// </summary>
|
|
/// <returns>The identity of the caller.</returns>
|
|
public static Identity GetIdentity()
|
|
{
|
|
FFI.identity(out var identity);
|
|
return identity;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Internal interface for procedure context with additional functionality.
|
|
/// </summary>
|
|
public interface IInternalProcedureContext : IProcedureContext
|
|
{
|
|
TxContext EnterTxContext(long timestampMicros);
|
|
void ExitTxContext();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Provides utility methods for procedure-related functionality.
|
|
/// </summary>
|
|
public static class ProcedureExtensions
|
|
{
|
|
/// <summary>
|
|
/// Schedules an immediate volatile, non-atomic procedure call.
|
|
/// </summary>
|
|
public static void VolatileNonatomicScheduleImmediate(string name, MemoryStream args)
|
|
{
|
|
var name_bytes = Encoding.UTF8.GetBytes(name);
|
|
var args_bytes = args.ToArray();
|
|
|
|
try
|
|
{
|
|
FFI.volatile_nonatomic_schedule_immediate(
|
|
name_bytes,
|
|
(uint)name_bytes.Length,
|
|
args_bytes,
|
|
(uint)args_bytes.Length
|
|
);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Error($"Failed to schedule procedure {name}: {ex}");
|
|
throw;
|
|
}
|
|
}
|
|
}
|