Cleipnir.ResilientFunctions.SqlServer
2.0.1
Prefix Reserved
See the version list below for details.
dotnet add package Cleipnir.ResilientFunctions.SqlServer --version 2.0.1
NuGet\Install-Package Cleipnir.ResilientFunctions.SqlServer -Version 2.0.1
<PackageReference Include="Cleipnir.ResilientFunctions.SqlServer" Version="2.0.1" />
paket add Cleipnir.ResilientFunctions.SqlServer --version 2.0.1
#r "nuget: Cleipnir.ResilientFunctions.SqlServer, 2.0.1"
// Install Cleipnir.ResilientFunctions.SqlServer as a Cake Addin #addin nuget:?package=Cleipnir.ResilientFunctions.SqlServer&version=2.0.1 // Install Cleipnir.ResilientFunctions.SqlServer as a Cake Tool #tool nuget:?package=Cleipnir.ResilientFunctions.SqlServer&version=2.0.1
Cleipnir's Resilient Functions
Realizing the saga-pattern by providing a simple way to ensure your code gets run - until you say it is done!
Resilient Functions is a simple and intuitive .NET framework for managing the execution of functions which must complete in their entirety despite: failures, restarts, deployments, versioning etc.
It automatically retries a function invocation until it completes potentially across process restarts and physical nodes.
The framework also supports postponing/suspending invocations or failing invocations for manually handling. Furthermore, versioning is natively supported.
It requires a minimal amount of setup to get started and seamlessly scales with multiple running instances.
Crucially, all this allows the saga pattern to be implemented in a simple yet powerful way.
Out-of-the-box you also get:
- synchronized invocation across multiple process instances
- cloud independence & support for multiple databases
- simple debuggability & testability
- easy versioning of functions
- native support for rpc and message-based communication
- graceful-shutdown
What it is not? |
---|
Unlike other saga frameworks Resilient Functions does not require a message-broker to operate.<br /> It is a fully self-contained solution - which operates on top of a database of choice or in-memory when testing.<br /> |
Sections
Getting Started
Only three steps needs to be performed to get started.
Firstly, install the relevant nuget package (using either Postgres, SqlServer, MySQL or Azure Blob-storage as persistence layer). I.e.
dotnet add package Cleipnir.ResilientFunctions.PostgreSQL
Secondly, setup the framework:
var store = new PostgreSqlFunctionStore(ConnStr);
await store.Initialize();
var functionsRegistry = new FunctionsRegistry(
store,
new Settings(
unhandledExceptionHandler: e => Console.WriteLine($"Unhandled framework exception occured: '{e}'"),
leaseLength: TimeSpan.FromSeconds(5)
)
);
Finally, register and invoke a function using the framework:
var rAction = functionsRegistry.RegisterAction(
functionTypeId: "OrderProcessor",
async (Order order, OrderState state) =>
{
await _paymentProviderClient.Reserve(order.CustomerId, state.TransactionId, order.TotalPrice);
await state.DoAtMostOnce(
workStatus: s => s.ProductsShippedStatus,
work: () => _logisticsClient.ShipProducts(order.CustomerId, order.ProductIds)
);
await _paymentProviderClient.Capture(state.TransactionId);
await _emailClient.SendOrderConfirmation(order.CustomerId, order.ProductIds);
}
);
var order = new Order(
OrderId: "MK-4321",
CustomerId: Guid.NewGuid(),
ProductIds: new[] { Guid.NewGuid(), Guid.NewGuid() },
TotalPrice: 123.5M
);
await rAction.Invoke(order.OrderId, order);
Congrats, any non-completed Order flows are now automatically restarted by the framework.
Message-based solution:
It is also possible to implement message-based flows using the framework. I.e. awaiting 2 external messages before completing an invocation can be accomplished as follows:
var rAction = functionsRegistry.RegisterAction(
functionTypeId: "MessageWaitingFunc",
async (string param, Workflow workflow) =>
{
var messages = await workflow.Messages;
await messages
.OfTypes<FundsReserved, InventoryLocked>()
.Take(2)
.Completion();
}
);
Show me more code
In the following chapter several stand-alone examples are presented.
Hello-World
Firstly, the compulsory, ‘hello world’-example can be realized as follows:
var store = new InMemoryFunctionStore();
var functions = new FunctionsRegistry(store, unhandledExceptionHandler: Console.WriteLine);
var rFunc = functions.RegisterFunc(
functionTypeId: "HelloWorld",
inner: (string param) => param.ToUpper()
).Invoke;
var returned = await rFunc(functionInstanceId: "", param: "hello world");
Console.WriteLine($"Returned: '{returned}'");
HTTP-call & database
Allright, not useful, here are a couple of simple, but common, use-cases.
Invoking a HTTP-endpoint and storing the response in a database table:
public static async Task RegisterAndInvoke(IDbConnection connection, IFunctionStore store)
{
var functions = new FunctionsRegistry(store, new Settings(UnhandledExceptionHandler: Console.WriteLine));
var httpClient = new HttpClient();
var rAction = functions.RegisterAction(
functionTypeId: "HttpAndDatabaseSaga",
inner: async (Guid id) =>
{
var response = await httpClient.PostAsync(URL, new StringContent(id.ToString()));
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
await connection.ExecuteAsync(
"UPDATE Entity SET State=@State WHERE Id=@Id",
new {State = content, Id = id}
);
}).Invoke;
var id = Guid.NewGuid();
await rAction(functionInstanceId: id.ToString(), param: id);
}
Sending customer emails
Consider a travel agency which wants to send a promotional email to its customers:
public static class EmailSenderSaga
{
public static async Task Start(MailAndRecipients mailAndRecipients, State state)
{
var (recipients, subject, content) = mailAndRecipients;
using var client = new SmtpClient();
await client.ConnectAsync("mail.smtpbucket.com", 8025);
for (var atRecipient = state.AtRecipient; atRecipient < mailAndRecipients.Recipients.Count; atRecipient++)
{
var recipient = recipients[atRecipient];
var message = new MimeMessage();
message.To.Add(new MailboxAddress(recipient.Name, recipient.Address));
message.From.Add(new MailboxAddress("The Travel Agency", "offers@thetravelagency.co.uk"));
message.Subject = subject;
message.Body = new TextPart(TextFormat.Html) { Text = content };
await client.SendAsync(message);
state.AtRecipient = atRecipient;
await state.Save();
}
}
public class State : WorkflowState
{
public int AtRecipient { get; set; }
}
}
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
-
net6.0
- Cleipnir.ResilientFunctions (>= 2.0.1)
- Microsoft.Data.SqlClient (>= 5.2.0)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Cleipnir.ResilientFunctions.SqlServer:
Package | Downloads |
---|---|
Cleipnir.Flows.SqlServer
Cleipnir Flows is a powerful .NET framework designed for ASP.NET services, providing a straightforward workflow-as-code approach. It ensures that your code executes reliably, even in the face of failures, restarts, deployments, versioning, and other challenges. While similar to Azure Durable Functions, Cleipnir Flows is specifically tailored for ASP.NET Core. |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
3.0.3 | 110 | 8/30/2024 |
3.0.2 | 78 | 7/25/2024 |
3.0.1 | 69 | 7/25/2024 |
3.0.0 | 98 | 7/14/2024 |
2.0.2 | 210 | 3/16/2024 |
2.0.1 | 122 | 3/15/2024 |
2.0.0 | 117 | 3/14/2024 |
1.0.16 | 382 | 8/30/2023 |
1.0.15 | 361 | 8/27/2023 |
1.0.14 | 372 | 8/13/2023 |
1.0.13 | 367 | 8/10/2023 |
1.0.12 | 393 | 8/2/2023 |
1.0.11 | 388 | 8/1/2023 |
1.0.10 | 401 | 7/30/2023 |
1.0.9 | 168 | 7/25/2023 |
1.0.8 | 398 | 7/24/2023 |
1.0.7 | 391 | 7/19/2023 |
1.0.6 | 366 | 7/12/2023 |
1.0.5 | 161 | 7/2/2023 |
1.0.4 | 329 | 4/4/2023 |
1.0.3 | 335 | 2/4/2023 |
1.0.2 | 340 | 2/1/2023 |
0.0.4 | 435 | 6/19/2022 |