-
Notifications
You must be signed in to change notification settings - Fork 1
Samples
public class WorkflowContext: EternityContext {
public WorkflowContext(IServiceProvider services):
base(new EternityAzureStorage(("ET", "azure storage connection string...")), services, new EternityClock()) {
}
}
// register this as background service
public class WorkflowBackgroundService : BackgroundService {
private readonly WorkflowContext workflowContext;
private readonly TelemetryClient telemetryClient;
public WorkflowBackgroundService(WorkflowContext workflowContext, TelemetryClient telemetryClient)
{
this.workflowContext = workflowContext;
this.telemetryClient = telemetryClient;
}
protected async override Task ExecuteAsync(CancellationToken stoppingToken) {
while (!stoppingToken.IsCancellationRequested) {
try {
await workflowService.ProcessMessagesAsync(cancellationToken: stoppingToken);
} catch (Exception ex) {
telemetryClient.TrackException(ex);
}
}
}
}
To enable Microsoft.DependencyInjection.Extensions
Scope, add following in configure method.
services.AddEternityServiceScope();
This will make every activity execute in separate service scope, you can inject scoped services in Activities.
// create new workflow and execute now
var id = await SignupWorkflow.CreateAsync(context, "[email protected]");
// raise an event...
await context.RaiseEventAsync(id, SignupWorkflow.Verify, verificationCode);
Lets assume we want to verify email address of user before signup, we want to set max timeout to 45 minutes and maximum 3 retries.
Activities are methods of the same class marked with [Activity]
attribute and methods must be public and virtual.
Activities can also be scheduled in future by passing a parameter marked with [Schedule]
attribute as shown below.
public class SignupWorkflow : Workflow<SignupWorkflow, string, string> {
// name of external event
public const string Resend = nameof(Resend);
// name of external event
public const string Verify = nameof(Verify);
public override async Task<string> RunAsync(string input)
{
var maxWait = TimeSpan.FromMinutes(15);
var code = (this.CurrentUtc.Ticks & 0xF).ToString();
await SendEmailAsync(input, code);
for (int i = 0; i < 3; i++)
{
var (name, result) = await WaitForExternalEventsAsync(maxWait, Resend, Verify);
switch(name)
{
case Verify:
if(result == code)
{
return "Verified";
}
break;
case Resend:
await SendEmailAsync(input, code, i);
break;
}
}
return "NotVerified";
}
[Activity]
public virtual async Task<string> SendEmailAsync(
string emailAddress,
string code,
int attempt = -1,
[Inject] MockEmailService emailService = null) {
await Task.Delay(100);
emailService.Emails.Add((emailAddress, code, CurrentUtc));
return $"{emailService.Emails.Count-1}";
}
}
For mobile, on iOS, there is no way to generate the code, so you can use Schedule method by importing .Mobile
namespace as shown below.
var maxWait = TimeSpan.FromMinutes(15);
var code = (this.CurrentUtc.Ticks & 0xF).ToString();
await SendEmailAsync(input, code);
for (int i = 0; i < 3; i++)
{
var (name, result) = await WaitForExternalEventsAsync(maxWait, Resend, Verify);
switch(name)
{
case Verify:
if(result == code)
{
return "Verified";
}
break;
case Resend:
// note this will use method delegate and will ensure that we are passing
// same type of parameters, the only problem is you will have to supply all
// default parameters as well
await this.ScheduleAsync( SendEmailAsync, input, code, i, null);
break;
}
}
return "NotVerified";
In the following example, we are creating Renew Membership Workflow when user registers for one year. In the following example, we will renew the membership after 364 days from the current utc date. The workflow will be suspended immediately and it will not occupy any memory. After 364 days, workflow will restart and will continue to renew. However, if user decides to cancel, you can raise an event with context
that will cause workflow to cancel.
public class RenewMembershipWorkflow: Workflow<RenewMembershipWorkflow,long,string> {
public const string Cancel = nameof(Cancel);
public async Task<string> RunAsync(long id) {
var till = TimeSpan.FromDays(364);
var (name, result) = await this.WaitForExtenralEvents(till, Cancel);
if (name == Cancel) {
// user has cancelled the membership, exit..
return "Cancelled";
}
// at this time, this workflow will be suspended and removed from the execution
// internally it will throw `ActivitySuspendedException` and it will start
// just before the given timespan
for(int i = 0; i<3; i++) {
var success = await RewewAsync(id, at);
if(success) {
// restart the same workflow
await RenewMembershipWorkflow.CreateAsync(this.Context, id);
return "Done";
}
// try after 3 days again...
at = TimeSpan.FromDays(3);
}
// renewal failed...
return "Failed";
}
[Activity]
public virtual async Task<bool> RenewAsync(
long id,
[Schedule] TimeSpan at,
[Inject] IPaymentService paymentService = null,
[Inject] IEmailService emailService = null
) {
var result = await paymentService.ChargeAsync(id);
if(result.Success) {
return true;
}
await emailService.SendFailedRenewalAsync(id);
return false;
}
}