// ------------------------------------------------------------------------ // Copyright 2021 The Dapr Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ------------------------------------------------------------------------ using System; using System.Text.Json; using System.Threading.Tasks; using Dapr.Actors.Runtime; using IDemoActor; namespace DemoActor { // The following example showcases a few features of Actors // // Every actor should inherit from the Actor type, and must implement one or more actor interfaces. // In this case the actor interfaces are IDemoActor and IBankActor. // // For Actors to use Reminders, it must derive from IRemindable. // If you don't intend to use Reminder feature, you can skip implementing IRemindable and reminder // specific methods which are shown in the code below. public class DemoActor : Actor, IDemoActor.IDemoActor, IBankActor, IRemindable { private const string StateName = "my_data"; private readonly BankService bank; public DemoActor(ActorHost host, BankService bank) : base(host) { // BankService is provided by dependency injection. // See Program.cs this.bank = bank; } public async Task SaveData(MyData data, TimeSpan ttl) { Console.WriteLine($"This is Actor id {this.Id} with data {data}."); // Set State using StateManager, state is saved after the method execution. await this.StateManager.SetStateAsync(StateName, data, ttl); } public Task GetData() { // Get state using StateManager. return this.StateManager.GetStateAsync(StateName); } public Task TestThrowException() { throw new NotImplementedException(); } public Task TestNoArgumentNoReturnType() { return Task.CompletedTask; } public async Task RegisterReminder() { await this.RegisterReminderAsync("TestReminder", null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5)); } public async Task RegisterReminderWithTtl(TimeSpan ttl) { await this.RegisterReminderAsync("TestReminder", null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5), ttl); } public async Task RegisterReminderWithRepetitions(int repetitions) { await this.RegisterReminderAsync("TestReminder", null, TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(1), repetitions); } public async Task RegisterReminderWithTtlAndRepetitions(TimeSpan ttl, int repetitions) { await this.RegisterReminderAsync("TestReminder", null, TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(1), repetitions, ttl); } public async Task GetReminder() { var reminder = await this.GetReminderAsync("TestReminder"); return reminder is not null ? new ActorReminderData { Name = reminder.Name, Period = reminder.Period, DueTime = reminder.DueTime } : null; } public Task UnregisterReminder() { return this.UnregisterReminderAsync("TestReminder"); } public async Task ReceiveReminderAsync(string reminderName, byte[] state, TimeSpan dueTime, TimeSpan period) { // This method is invoked when an actor reminder is fired. var actorState = await this.StateManager.GetStateAsync(StateName); actorState.PropertyB = $"Reminder triggered at '{DateTime.Now:yyyy-MM-ddTHH:mm:ss}'"; await this.StateManager.SetStateAsync(StateName, actorState, ttl: TimeSpan.FromMinutes(5)); } class TimerParams { public int IntParam { get; set; } public string StringParam { get; set; } } /// public Task RegisterTimer() { var timerParams = new TimerParams { IntParam = 100, StringParam = "timer test", }; var serializedTimerParams = JsonSerializer.SerializeToUtf8Bytes(timerParams); return this.RegisterTimerAsync("TestTimer", nameof(this.TimerCallback), serializedTimerParams, TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(3)); } public Task RegisterTimerWithTtl(TimeSpan ttl) { var timerParams = new TimerParams { IntParam = 100, StringParam = "timer test", }; var serializedTimerParams = JsonSerializer.SerializeToUtf8Bytes(timerParams); return this.RegisterTimerAsync("TestTimer", nameof(this.TimerCallback), serializedTimerParams, TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(3), ttl); } public Task UnregisterTimer() { return this.UnregisterTimerAsync("TestTimer"); } // This method is called whenever an actor is activated. // An actor is activated the first time any of its methods are invoked. protected override Task OnActivateAsync() { // Provides opportunity to perform some optional setup. return Task.CompletedTask; } // This method is called whenever an actor is deactivated after a period of inactivity. protected override Task OnDeactivateAsync() { // Provides Opportunity to perform optional cleanup. return Task.CompletedTask; } /// /// This method is called when the timer is triggered based on its registration. /// It updates the PropertyA value. /// /// Timer input data. /// A task that represents the asynchronous operation. public async Task TimerCallback(byte[] data) { var state = await this.StateManager.GetStateAsync(StateName); state.PropertyA = $"Timer triggered at '{DateTime.Now:yyyyy-MM-ddTHH:mm:s}'"; await this.StateManager.SetStateAsync(StateName, state, ttl: TimeSpan.FromMinutes(5)); var timerParams = JsonSerializer.Deserialize(data); Console.WriteLine("Timer parameter1: " + timerParams.IntParam); Console.WriteLine("Timer parameter2: " + timerParams.StringParam); } public async Task GetAccountBalance() { var starting = new AccountBalance() { AccountId = this.Id.GetId(), Balance = 100m, // Start new accounts with 100, we're pretty generous. }; var balance = await this.StateManager.GetOrAddStateAsync("balance", starting); return balance; } public async Task Withdraw(WithdrawRequest withdraw) { var starting = new AccountBalance() { AccountId = this.Id.GetId(), Balance = 100m, // Start new accounts with 100, we're pretty generous. }; var balance = await this.StateManager.GetOrAddStateAsync("balance", starting); // Throws Overdraft exception if the account doesn't have enough money. var updated = this.bank.Withdraw(balance.Balance, withdraw.Amount); balance.Balance = updated; await this.StateManager.SetStateAsync("balance", balance); } } }