Skip to content

Samples

Akash Kava edited this page Nov 9, 2021 · 8 revisions

Configure Workflow Service

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);
            }
        }
    }
}

Register Service Scope

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

// create new workflow and execute now
var id = await SignupWorkflow.CreateAsync(context, "sample@gmail.com");

// raise an event...
await context.RaiseEventAsync(id, SignupWorkflow.Verify, verificationCode);

Signup Example

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}";
    }
}

Mobile Sample

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";

Renew Membership

In the following example, we are creating Renew Membership Workflow when user registers for one year.

public class RenewMembershipWorkflow: Workflow<RenewMembershipWorkflow,long,string> {
    
    public async Task<string> RunAsync(long id) {

        var at = TimeSpan.FromDays(364);
        
        // 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;
    }   
}
Clone this wiki locally