diff --git a/Core/Cleipnir.ResilientFunctions.Tests/InMemoryTests/StoreTests.cs b/Core/Cleipnir.ResilientFunctions.Tests/InMemoryTests/StoreTests.cs index c175d8cf..9e1d0d7a 100644 --- a/Core/Cleipnir.ResilientFunctions.Tests/InMemoryTests/StoreTests.cs +++ b/Core/Cleipnir.ResilientFunctions.Tests/InMemoryTests/StoreTests.cs @@ -269,16 +269,4 @@ public override Task GetResultsReturnsEmptyDictionaryForEmptyInput() [TestMethod] public override Task GetResultsReturnsOnlyExistingFunctionResults() => GetResultsReturnsOnlyExistingFunctionResults(FunctionStoreFactory.Create()); - - [TestMethod] - public override Task SetResultSucceedsWhenOwnerMatches() - => SetResultSucceedsWhenOwnerMatches(FunctionStoreFactory.Create()); - - [TestMethod] - public override Task SetResultDoesNothingWhenOwnerDoesNotMatch() - => SetResultDoesNothingWhenOwnerDoesNotMatch(FunctionStoreFactory.Create()); - - [TestMethod] - public override Task SetResultDoesNothingWhenFunctionDoesNotExist() - => SetResultDoesNothingWhenFunctionDoesNotExist(FunctionStoreFactory.Create()); } \ No newline at end of file diff --git a/Core/Cleipnir.ResilientFunctions.Tests/TestTemplates/StoreTests.cs b/Core/Cleipnir.ResilientFunctions.Tests/TestTemplates/StoreTests.cs index 719e3f81..ff7bd510 100644 --- a/Core/Cleipnir.ResilientFunctions.Tests/TestTemplates/StoreTests.cs +++ b/Core/Cleipnir.ResilientFunctions.Tests/TestTemplates/StoreTests.cs @@ -2357,72 +2357,4 @@ await store.SucceedFunction( results.ContainsKey(nonExistentFunctionId).ShouldBeFalse(); } - public abstract Task SetResultSucceedsWhenOwnerMatches(); - protected async Task SetResultSucceedsWhenOwnerMatches(Task storeTask) - { - var store = await storeTask; - var functionId = TestStoredId.Create(); - var owner = ReplicaId.NewId(); - var result = "test result".ToJson().ToUtf8Bytes(); - - // Create function with owner - await store.CreateFunction( - functionId, - "humanInstanceId", - param: Test.SimpleStoredParameter, - leaseExpiration: DateTime.UtcNow.Ticks, - postponeUntil: null, - timestamp: DateTime.UtcNow.Ticks, - parent: null, - owner: owner - ).ShouldNotBeNullAsync(); - - // Set result - await store.SetResult(functionId, result, owner); - - // Verify result was set - var results = await store.GetResults([functionId]); - results[functionId].ShouldBe(result); - } - - public abstract Task SetResultDoesNothingWhenOwnerDoesNotMatch(); - protected async Task SetResultDoesNothingWhenOwnerDoesNotMatch(Task storeTask) - { - var store = await storeTask; - var functionId = TestStoredId.Create(); - var owner = ReplicaId.NewId(); - var wrongOwner = ReplicaId.NewId(); - var result = "test result".ToJson().ToUtf8Bytes(); - - // Create function with owner - await store.CreateFunction( - functionId, - "humanInstanceId", - param: Test.SimpleStoredParameter, - leaseExpiration: DateTime.UtcNow.Ticks, - postponeUntil: null, - timestamp: DateTime.UtcNow.Ticks, - parent: null, - owner: owner - ).ShouldNotBeNullAsync(); - - // Try to set result with wrong owner - await store.SetResult(functionId, result, wrongOwner); - - // Verify result was not set - var results = await store.GetResults([functionId]); - results[functionId].ShouldBeNull(); - } - - public abstract Task SetResultDoesNothingWhenFunctionDoesNotExist(); - protected async Task SetResultDoesNothingWhenFunctionDoesNotExist(Task storeTask) - { - var store = await storeTask; - var nonExistentFunctionId = TestStoredId.Create(); - var owner = ReplicaId.NewId(); - var result = "test result".ToJson().ToUtf8Bytes(); - - // Try to set result for non-existent function (should not throw) - await store.SetResult(nonExistentFunctionId, result, owner); - } } \ No newline at end of file diff --git a/Core/Cleipnir.ResilientFunctions.Tests/TestTemplates/WatchDogsTests/CrashableFunctionStore.cs b/Core/Cleipnir.ResilientFunctions.Tests/TestTemplates/WatchDogsTests/CrashableFunctionStore.cs index 28ad548d..f7d52aff 100644 --- a/Core/Cleipnir.ResilientFunctions.Tests/TestTemplates/WatchDogsTests/CrashableFunctionStore.cs +++ b/Core/Cleipnir.ResilientFunctions.Tests/TestTemplates/WatchDogsTests/CrashableFunctionStore.cs @@ -215,11 +215,6 @@ public Task DeleteFunction(StoredId storedId) ? Task.FromException>(new TimeoutException()) : _inner.GetResults(storedIds); - public Task SetResult(StoredId storedId, byte[] result, ReplicaId expectedReplica) - => _crashed - ? Task.FromException(new TimeoutException()) - : _inner.SetResult(storedId, result, expectedReplica); - public IFunctionStore WithPrefix(string prefix) => _inner.WithPrefix(prefix); } diff --git a/Core/Cleipnir.ResilientFunctions/ActionRegistration.cs b/Core/Cleipnir.ResilientFunctions/ActionRegistration.cs index a0630f91..601e4082 100644 --- a/Core/Cleipnir.ResilientFunctions/ActionRegistration.cs +++ b/Core/Cleipnir.ResilientFunctions/ActionRegistration.cs @@ -26,9 +26,8 @@ public ActionRegistration( Invoker invoker, ControlPanelFactory controlPanelFactory, MessageWriters messageWriters, - Postman postman, UtcNow utcNow - ) : base(storedType, postman, functionStore, utcNow) + ) : base(storedType, functionStore, utcNow) { Type = flowType; _invoker = invoker; @@ -59,8 +58,8 @@ public async Task SendMessage( FlowInstance flowInstance, T message, string? idempotencyKey = null - ) where T : class => await Postman.SendMessage(StoredId.Create(StoredType, flowInstance.Value), message, idempotencyKey); + ) where T : class => await MessageWriters.For(flowInstance).AppendMessage(message, idempotencyKey); public async Task SendMessages(IReadOnlyList messages) - => await Postman.SendMessages(messages); + => await MessageWriters.AppendMessages(messages); } \ No newline at end of file diff --git a/Core/Cleipnir.ResilientFunctions/BaseRegistration.cs b/Core/Cleipnir.ResilientFunctions/BaseRegistration.cs index 23aecfc5..ad77f183 100644 --- a/Core/Cleipnir.ResilientFunctions/BaseRegistration.cs +++ b/Core/Cleipnir.ResilientFunctions/BaseRegistration.cs @@ -3,7 +3,6 @@ using System.Threading.Tasks; using Cleipnir.ResilientFunctions.CoreRuntime; using Cleipnir.ResilientFunctions.Domain; -using Cleipnir.ResilientFunctions.Messaging; using Cleipnir.ResilientFunctions.Storage; namespace Cleipnir.ResilientFunctions; @@ -11,17 +10,15 @@ namespace Cleipnir.ResilientFunctions; public abstract class BaseRegistration { private readonly IFunctionStore _functionStore; - protected Postman Postman { get; } public StoredType StoredType { get; } protected UtcNow UtcNow { get; } - protected BaseRegistration(StoredType storedType, Postman postman, IFunctionStore functionStore, UtcNow utcNow) + protected BaseRegistration(StoredType storedType, IFunctionStore functionStore, UtcNow utcNow) { _functionStore = functionStore; StoredType = storedType; - Postman = postman; UtcNow = utcNow; - } + } public StoredId MapToStoredId(FlowInstance instance) => StoredId.Create(StoredType, instance.Value); diff --git a/Core/Cleipnir.ResilientFunctions/Domain/Result.cs b/Core/Cleipnir.ResilientFunctions/Domain/Result.cs index be985804..e486f086 100644 --- a/Core/Cleipnir.ResilientFunctions/Domain/Result.cs +++ b/Core/Cleipnir.ResilientFunctions/Domain/Result.cs @@ -4,12 +4,6 @@ namespace Cleipnir.ResilientFunctions.Domain; -public static class Result -{ - public static Result SucceedWithValue(T value) => new(value); - public static Result SucceedWithUnit { get; } = new Result(Unit.Instance); -} - public static class Succeed { public static Result WithValue(T value) => new Result(value); diff --git a/Core/Cleipnir.ResilientFunctions/FuncRegistration.cs b/Core/Cleipnir.ResilientFunctions/FuncRegistration.cs index b506b69e..165f4506 100644 --- a/Core/Cleipnir.ResilientFunctions/FuncRegistration.cs +++ b/Core/Cleipnir.ResilientFunctions/FuncRegistration.cs @@ -25,9 +25,8 @@ public FuncRegistration( Invoker invoker, ControlPanelFactory controlPanelFactory, MessageWriters messageWriters, - Postman postman, UtcNow utcNow - ) : base(storedType, postman, functionStore, utcNow) + ) : base(storedType, functionStore, utcNow) { Type = flowType; _invoker = invoker; @@ -59,8 +58,8 @@ public async Task SendMessage( FlowInstance flowInstance, T message, string? idempotencyKey = null - ) where T : class => await Postman.SendMessage(StoredId.Create(StoredType, flowInstance.Value), message, idempotencyKey); + ) where T : class => await MessageWriters.For(flowInstance).AppendMessage(message, idempotencyKey); public async Task SendMessages(IReadOnlyList messages) - => await Postman.SendMessages(messages); + => await MessageWriters.AppendMessages(messages); } \ No newline at end of file diff --git a/Core/Cleipnir.ResilientFunctions/FunctionsRegistry.cs b/Core/Cleipnir.ResilientFunctions/FunctionsRegistry.cs index 40c7fec6..3eff1acc 100644 --- a/Core/Cleipnir.ResilientFunctions/FunctionsRegistry.cs +++ b/Core/Cleipnir.ResilientFunctions/FunctionsRegistry.cs @@ -258,8 +258,6 @@ public FuncRegistration RegisterFunc( serializer ); - var postman = new Postman(messageWriters); - var registration = new FuncRegistration( flowType, storedType, @@ -267,7 +265,6 @@ public FuncRegistration RegisterFunc( invoker, controlPanels, messageWriters, - postman, _settings.UtcNow ); _functions[flowType] = registration; @@ -343,8 +340,6 @@ private ParamlessRegistration RegisterParamless( serializer ); - var postman = new Postman(messageWriters); - var registration = new ParamlessRegistration( flowType, storedType, @@ -352,7 +347,6 @@ private ParamlessRegistration RegisterParamless( invoker, controlPanels, messageWriters, - postman, _settings.UtcNow ); _functions[flowType] = registration; @@ -427,8 +421,6 @@ public ActionRegistration RegisterAction( _functionStore, serializer ); - var postman = new Postman(messageWriters); - var registration = new ActionRegistration( flowType, storedType, @@ -436,7 +428,6 @@ public ActionRegistration RegisterAction( rActionInvoker, controlPanels, messageWriters, - postman, _settings.UtcNow ); _functions[flowType] = registration; diff --git a/Core/Cleipnir.ResilientFunctions/Messaging/MessageAndIdempotencyKey.cs b/Core/Cleipnir.ResilientFunctions/Messaging/MessageAndIdempotencyKey.cs index c308d159..586c5d55 100644 --- a/Core/Cleipnir.ResilientFunctions/Messaging/MessageAndIdempotencyKey.cs +++ b/Core/Cleipnir.ResilientFunctions/Messaging/MessageAndIdempotencyKey.cs @@ -1,9 +1,3 @@ -namespace Cleipnir.ResilientFunctions.Messaging; +namespace Cleipnir.ResilientFunctions.Messaging; -public record MessageAndIdempotencyKey(object Message, string? IdempotencyKey = null); - -public static class MessageAndIdempotencyKeyExtensions -{ - public static MessageAndIdempotencyKey ToMessageAndIdempotencyKey(this object message, string? idempotencyKey = null) - => new(message, idempotencyKey); -} \ No newline at end of file +public record MessageAndIdempotencyKey(object Message, string? IdempotencyKey = null); \ No newline at end of file diff --git a/Core/Cleipnir.ResilientFunctions/Messaging/Postman.cs b/Core/Cleipnir.ResilientFunctions/Messaging/Postman.cs deleted file mode 100644 index 0fe16be1..00000000 --- a/Core/Cleipnir.ResilientFunctions/Messaging/Postman.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using Cleipnir.ResilientFunctions.Storage; - -namespace Cleipnir.ResilientFunctions.Messaging; - -public class Postman(MessageWriters messageWriters) -{ - public Task SendMessage( - StoredId instance, - TMessage message, - string? idempotencyKey = null - ) where TMessage : class => messageWriters.For(instance).AppendMessage(message, idempotencyKey); - - public async Task SendMessages(IReadOnlyList messages) - => await messageWriters.AppendMessages(messages); -} \ No newline at end of file diff --git a/Core/Cleipnir.ResilientFunctions/ParamlessRegistration.cs b/Core/Cleipnir.ResilientFunctions/ParamlessRegistration.cs index 42b60eeb..5db56df5 100644 --- a/Core/Cleipnir.ResilientFunctions/ParamlessRegistration.cs +++ b/Core/Cleipnir.ResilientFunctions/ParamlessRegistration.cs @@ -27,9 +27,8 @@ public ParamlessRegistration( Invoker invoker, ControlPanelFactory controlPanelFactory, MessageWriters messageWriters, - Postman postman, UtcNow utcNow - ) : base(storedType, postman, functionStore, utcNow) + ) : base(storedType, functionStore, utcNow) { Type = flowType; _invoker = invoker; @@ -70,9 +69,9 @@ public async Task SendMessage( await Schedule(flowInstance); } - await Postman.SendMessage(StoredId.Create(StoredType, flowInstance.Value), message, idempotencyKey); + await MessageWriters.For(flowInstance).AppendMessage(message, idempotencyKey); } public async Task SendMessages(IReadOnlyList messages) - => await Postman.SendMessages(messages); + => await MessageWriters.AppendMessages(messages); } \ No newline at end of file diff --git a/Core/Cleipnir.ResilientFunctions/Storage/IFunctionStore.cs b/Core/Cleipnir.ResilientFunctions/Storage/IFunctionStore.cs index c6f795b6..dea9f328 100644 --- a/Core/Cleipnir.ResilientFunctions/Storage/IFunctionStore.cs +++ b/Core/Cleipnir.ResilientFunctions/Storage/IFunctionStore.cs @@ -110,5 +110,4 @@ Task SuspendFunction( IFunctionStore WithPrefix(string prefix); Task> GetResults(IEnumerable storedIds); - Task SetResult(StoredId storedId, byte[] result, ReplicaId expectedReplica); } \ No newline at end of file diff --git a/Core/Cleipnir.ResilientFunctions/Storage/InMemoryFunctionStore.cs b/Core/Cleipnir.ResilientFunctions/Storage/InMemoryFunctionStore.cs index 62c71bb7..709933fc 100644 --- a/Core/Cleipnir.ResilientFunctions/Storage/InMemoryFunctionStore.cs +++ b/Core/Cleipnir.ResilientFunctions/Storage/InMemoryFunctionStore.cs @@ -563,22 +563,6 @@ public virtual Task DeleteFunction(StoredId storedId) } } - public Task SetResult(StoredId storedId, byte[] result, ReplicaId expectedReplica) - { - lock (_sync) - { - if (!_states.ContainsKey(storedId)) - return Task.CompletedTask; - - var state = _states[storedId]; - if (state.Owner != expectedReplica) - return Task.CompletedTask; - - state.Result = result; - return Task.CompletedTask; - } - } - private class InnerState { public StoredId StoredId { get; init; } = null!; diff --git a/Samples/Sample.ConsoleApp/Utils/CrashableFunctionStore.cs b/Samples/Sample.ConsoleApp/Utils/CrashableFunctionStore.cs index 1aa519ba..df42a16d 100644 --- a/Samples/Sample.ConsoleApp/Utils/CrashableFunctionStore.cs +++ b/Samples/Sample.ConsoleApp/Utils/CrashableFunctionStore.cs @@ -196,11 +196,6 @@ public Task DeleteFunction(StoredId storedId) ? Task.FromException>(new TimeoutException()) : _inner.GetResults(storedIds); - public Task SetResult(StoredId storedId, byte[] result, ReplicaId expectedReplica) - => _crashed - ? Task.FromException(new TimeoutException()) - : _inner.SetResult(storedId, result, expectedReplica); - public IFunctionStore WithPrefix(string prefix) => _inner.WithPrefix(prefix); } \ No newline at end of file diff --git a/Stores/MariaDB/Cleipnir.ResilientFunctions.MariaDB.Tests/StoreTests.cs b/Stores/MariaDB/Cleipnir.ResilientFunctions.MariaDB.Tests/StoreTests.cs index 95a1340c..e4d0f183 100644 --- a/Stores/MariaDB/Cleipnir.ResilientFunctions.MariaDB.Tests/StoreTests.cs +++ b/Stores/MariaDB/Cleipnir.ResilientFunctions.MariaDB.Tests/StoreTests.cs @@ -258,16 +258,4 @@ public override Task GetResultsReturnsEmptyDictionaryForEmptyInput() [TestMethod] public override Task GetResultsReturnsOnlyExistingFunctionResults() => GetResultsReturnsOnlyExistingFunctionResults(FunctionStoreFactory.Create()); - - [TestMethod] - public override Task SetResultSucceedsWhenOwnerMatches() - => SetResultSucceedsWhenOwnerMatches(FunctionStoreFactory.Create()); - - [TestMethod] - public override Task SetResultDoesNothingWhenOwnerDoesNotMatch() - => SetResultDoesNothingWhenOwnerDoesNotMatch(FunctionStoreFactory.Create()); - - [TestMethod] - public override Task SetResultDoesNothingWhenFunctionDoesNotExist() - => SetResultDoesNothingWhenFunctionDoesNotExist(FunctionStoreFactory.Create()); } \ No newline at end of file diff --git a/Stores/MariaDB/Cleipnir.ResilientFunctions.MariaDB/MariaDbFunctionStore.cs b/Stores/MariaDB/Cleipnir.ResilientFunctions.MariaDB/MariaDbFunctionStore.cs index 946d3903..d5e332f2 100644 --- a/Stores/MariaDB/Cleipnir.ResilientFunctions.MariaDB/MariaDbFunctionStore.cs +++ b/Stores/MariaDB/Cleipnir.ResilientFunctions.MariaDB/MariaDbFunctionStore.cs @@ -913,28 +913,6 @@ public IFunctionStore WithPrefix(string prefix) return results; } - private string? _setResultSql; - public async Task SetResult(StoredId storedId, byte[] result, ReplicaId expectedReplica) - { - await using var conn = await CreateOpenConnection(_connectionString); - _setResultSql ??= $@" - UPDATE {_tablePrefix} - SET result_json = ? - WHERE id = ? AND owner = ?"; - - await using var command = new MySqlCommand(_setResultSql, conn) - { - Parameters = - { - new() { Value = result }, - new() { Value = storedId.AsGuid.ToString("N") }, - new() { Value = expectedReplica.AsGuid.ToString("N") } - } - }; - - await command.ExecuteNonQueryAsync(); - } - private string? _deleteFunctionSql; private async Task DeleteStoredFunction(StoredId storedId) { diff --git a/Stores/PostgreSQL/Cleipnir.ResilientFunctions.PostgreSQL.Tests/StoreTests.cs b/Stores/PostgreSQL/Cleipnir.ResilientFunctions.PostgreSQL.Tests/StoreTests.cs index 19833e63..dae2af4a 100644 --- a/Stores/PostgreSQL/Cleipnir.ResilientFunctions.PostgreSQL.Tests/StoreTests.cs +++ b/Stores/PostgreSQL/Cleipnir.ResilientFunctions.PostgreSQL.Tests/StoreTests.cs @@ -261,16 +261,4 @@ public override Task GetResultsReturnsEmptyDictionaryForEmptyInput() [TestMethod] public override Task GetResultsReturnsOnlyExistingFunctionResults() => GetResultsReturnsOnlyExistingFunctionResults(FunctionStoreFactory.Create()); - - [TestMethod] - public override Task SetResultSucceedsWhenOwnerMatches() - => SetResultSucceedsWhenOwnerMatches(FunctionStoreFactory.Create()); - - [TestMethod] - public override Task SetResultDoesNothingWhenOwnerDoesNotMatch() - => SetResultDoesNothingWhenOwnerDoesNotMatch(FunctionStoreFactory.Create()); - - [TestMethod] - public override Task SetResultDoesNothingWhenFunctionDoesNotExist() - => SetResultDoesNothingWhenFunctionDoesNotExist(FunctionStoreFactory.Create()); } \ No newline at end of file diff --git a/Stores/PostgreSQL/Cleipnir.ResilientFunctions.PostgreSQL/PostgreSqlFunctionStore.cs b/Stores/PostgreSQL/Cleipnir.ResilientFunctions.PostgreSQL/PostgreSqlFunctionStore.cs index b14205de..54152840 100644 --- a/Stores/PostgreSQL/Cleipnir.ResilientFunctions.PostgreSQL/PostgreSqlFunctionStore.cs +++ b/Stores/PostgreSQL/Cleipnir.ResilientFunctions.PostgreSQL/PostgreSqlFunctionStore.cs @@ -831,28 +831,6 @@ public IFunctionStore WithPrefix(string prefix) return results; } - private string? _setResultSql; - public async Task SetResult(StoredId storedId, byte[] result, ReplicaId expectedReplica) - { - await using var conn = await CreateConnection(); - _setResultSql ??= $@" - UPDATE {_tableName} - SET result_json = $1 - WHERE id = $2 AND owner = $3"; - - await using var command = new NpgsqlCommand(_setResultSql, conn) - { - Parameters = - { - new() { Value = result }, - new() { Value = storedId.AsGuid }, - new() { Value = expectedReplica.AsGuid } - } - }; - - await command.ExecuteNonQueryAsync(); - } - private string? _deleteFunctionSql; private async Task DeleteStoredFunction(StoredId storedId) { diff --git a/Stores/SqlServer/Cleipnir.ResilientFunctions.SqlServer.Tests/StoreTests.cs b/Stores/SqlServer/Cleipnir.ResilientFunctions.SqlServer.Tests/StoreTests.cs index 1e37cd66..f5d3fec1 100644 --- a/Stores/SqlServer/Cleipnir.ResilientFunctions.SqlServer.Tests/StoreTests.cs +++ b/Stores/SqlServer/Cleipnir.ResilientFunctions.SqlServer.Tests/StoreTests.cs @@ -261,16 +261,4 @@ public override Task GetResultsReturnsEmptyDictionaryForEmptyInput() [TestMethod] public override Task GetResultsReturnsOnlyExistingFunctionResults() => GetResultsReturnsOnlyExistingFunctionResults(FunctionStoreFactory.Create()); - - [TestMethod] - public override Task SetResultSucceedsWhenOwnerMatches() - => SetResultSucceedsWhenOwnerMatches(FunctionStoreFactory.Create()); - - [TestMethod] - public override Task SetResultDoesNothingWhenOwnerDoesNotMatch() - => SetResultDoesNothingWhenOwnerDoesNotMatch(FunctionStoreFactory.Create()); - - [TestMethod] - public override Task SetResultDoesNothingWhenFunctionDoesNotExist() - => SetResultDoesNothingWhenFunctionDoesNotExist(FunctionStoreFactory.Create()); } diff --git a/Stores/SqlServer/Cleipnir.ResilientFunctions.SqlServer/SqlServerFunctionStore.cs b/Stores/SqlServer/Cleipnir.ResilientFunctions.SqlServer/SqlServerFunctionStore.cs index 25414058..0566dd29 100644 --- a/Stores/SqlServer/Cleipnir.ResilientFunctions.SqlServer/SqlServerFunctionStore.cs +++ b/Stores/SqlServer/Cleipnir.ResilientFunctions.SqlServer/SqlServerFunctionStore.cs @@ -953,23 +953,6 @@ public IFunctionStore WithPrefix(string prefix) return results; } - private string? _setResultSql; - public async Task SetResult(StoredId storedId, byte[] result, ReplicaId expectedReplica) - { - await using var conn = await _connFunc(); - _setResultSql ??= @$" - UPDATE {_tableName} - SET ResultJson = @ResultJson - WHERE Id = @Id AND Owner = @Owner"; - - await using var command = new SqlCommand(_setResultSql, conn); - command.Parameters.AddWithValue("@ResultJson", result); - command.Parameters.AddWithValue("@Id", storedId.AsGuid); - command.Parameters.AddWithValue("@Owner", expectedReplica.AsGuid); - - await command.ExecuteNonQueryAsync(); - } - private string? _deleteFunctionSql; private async Task DeleteStoredFunction(StoredId storedId) {