Cleipnir.ResilientFunctions.AzureBlob
1.0.8
Prefix Reserved
See the version list below for details.
dotnet add package Cleipnir.ResilientFunctions.AzureBlob --version 1.0.8
NuGet\Install-Package Cleipnir.ResilientFunctions.AzureBlob -Version 1.0.8
<PackageReference Include="Cleipnir.ResilientFunctions.AzureBlob" Version="1.0.8" />
paket add Cleipnir.ResilientFunctions.AzureBlob --version 1.0.8
#r "nuget: Cleipnir.ResilientFunctions.AzureBlob, 1.0.8"
// Install Cleipnir.ResilientFunctions.AzureBlob as a Cake Addin #addin nuget:?package=Cleipnir.ResilientFunctions.AzureBlob&version=1.0.8 // Install Cleipnir.ResilientFunctions.AzureBlob as a Cake Tool #tool nuget:?package=Cleipnir.ResilientFunctions.AzureBlob&version=1.0.8
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.
Install-Package Cleipnir.ResilientFunctions.PostgreSQL
Secondly, setup the framework:
var store = new PostgreSqlFunctionStore(ConnStr);
await store.Initialize();
var rFunctions = new RFunctions(
store,
new Settings(
unhandledExceptionHandler: e => Console.WriteLine($"Unhandled framework exception occured: '{e}'"),
signOfLifeFrequency: TimeSpan.FromSeconds(5)
)
);
Finally, register and invoke a function using the framework:
var rAction = rFunctions.RegisterAction(
functionTypeId: "OrderProcessor",
async (Order order, OrderScrapbook scrapbook) =>
{
await _paymentProviderClient.Reserve(order.CustomerId, scrapbook.TransactionId, order.TotalPrice);
await scrapbook.DoAtMostOnce(
workStatus: s => s.ProductsShippedStatus,
work: () => _logisticsClient.ShipProducts(order.CustomerId, order.ProductIds)
);
await _paymentProviderClient.Capture(scrapbook.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 = rFunctions.RegisterAction(
functionTypeId: "MessageWaitingFunc",
async (string param, Context context) =>
{
var eventSource = await context.EventSource;
await EventSource
.OfTypes<FundsReserved, InventoryLocked>()
.Take(2)
.ToList();
}
);
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 RFunctions(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 RFunctions(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, Scrapbook scrapbook)
{
var (recipients, subject, content) = mailAndRecipients;
using var client = new SmtpClient();
await client.ConnectAsync("mail.smtpbucket.com", 8025);
for (var atRecipient = scrapbook.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);
scrapbook.AtRecipient = atRecipient;
await scrapbook.Save();
}
}
public class Scrapbook : RScrapbook
{
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
- Azure.Storage.Blobs (>= 12.17.0)
- Cleipnir.ResilientFunctions (>= 1.0.8)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Cleipnir.ResilientFunctions.AzureBlob:
Package | Downloads |
---|---|
Cleipnir.Flows.AzureBlob
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.