We have to write multiple triggers. I was hoping to create separate functions, based on the trigger types. So if I need 5 timer triggers, that will run at different times, I would create one Timer trigger function class and name the functions like [TimerTrigger1], [TimerTrigger2], [TimerTrigger3] ... and so forth. After I added the code I am not sure if I can do that anymore.
Can someone suggest how I can go about adding multiple triggers? I can't have two Run functions under one class.
public static class TimerTrigger
{
[FunctionName("InsertTimerTrigger1")]
public static void Run([TimerTrigger("0 */5 * * * *")]TimerInfo myTimer, ILogger log)
{
// Do task 1
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
}
[FunctionName("InsertTimerTrigger2")]
public static void Run([TimerTrigger("0 */15 * * * *")]TimerInfo myTimer, ILogger log)
{
//Do Task 2
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
}
}
You can create multiple functions in Single Class. You can change Run Method name.
public static class Function1
{
[FunctionName("Function1")]
public static void Method1([TimerTrigger("0 */2 * * * *")]TimerInfo myTimer, TraceWriter log)
{
log.Info($"C# Timer trigger function executed at: {DateTime.Now}");
}
[FunctionName("Function2")]
public static void Method2([TimerTrigger("0 */3 * * * *")]TimerInfo myTimer, TraceWriter log)
{
log.Info($"C# Timer trigger function executed at: {DateTime.Now}");
}
}
But I will recommend, Create multiple functions will help you (5 in your case).
If you are using common business logic, you can put in a common class and inject in all function.
You can independently Enable/Disable/Delete function from FunctionApp Instance.
You can monitor each function independently (from Function Monitor section)
You can choose any name for the methods. (Naming it as "Run" is not a requirement.)
public static class TimerTrigger
{
[FunctionName("InsertTimerTrigger1")]
public static void InsertTimerTrigger1([TimerTrigger("0 */5 * * * *")]TimerInfo myTimer, ILogger log)
{
// Do task 1
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
}
[FunctionName("InsertTimerTrigger2")]
public static void InsertTimerTrigger2([TimerTrigger("0 */15 * * * *")]TimerInfo myTimer, ILogger log)
{
//Do Task 2
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
}
}
Related
I would like to pass the azure sql input binding where my sql command is a select statement with a parameter. This does not work. It keeps telling me the 'complete' is not a parameter. What am I doing wrong or is what I'm trying to do impossible with sql input binding? When I have a simple statement like select top(10) id, status from Dispatch - it works. Can I not pass a string to the parameters?
[FunctionName("Function1")]
public async Task Run([TimerTrigger("0 */2 * * * *")]TimerInfo myTimer,
[Sql("select top(10)id, status from Dispatch where status = #critstatus; ",
CommandType = System.Data.CommandType.Text,
Parameters = "#critstatus= {'complete'}",
ConnectionStringSetting = "SqlConnectionString")]
IAsyncEnumerable dispatch, ILogger log)
or
[FunctionName("Function1")]
public async Task Run([TimerTrigger("0 */2 * * * *")]TimerInfo myTimer,
[Sql("select top(10)id, status from Dispatch where status = \''complete\'' ",
CommandType = System.Data.CommandType.Text,
ConnectionStringSetting = "SqlConnectionString")]
IAsyncEnumerable dispatch, ILogger log)
According to this MSFT Documentation, Your SQL Query is not bindable as input to the Timer trigger as we have tested in our local environment.
Supported Input Bindings for the Azure Function Timer Trigger:
To fetch the data by giving input parameters for every 2 min (Timer Trigger) in Azure Function, we can call the SQL Query String inside the Run Method Code.
Function 1.cs:
public void Run([TimerTrigger("0 */2 * * * *")]TimerInfo myTimer, ILogger log)
{
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
string connString = "Server=<yourservername>,1433;Initial Catalog=samplesqldb0808;Persist Security Info=False;User ID=<username>;Password=<password>;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;";
//Query to fetch data
string queryString = "SELECT TOP 2 Id, Status FROM [dbo].[Dispatch] WHERE Status = 'Complete';";
//Connection
using (SqlConnection connection = new SqlConnection(connString))
{
SqlCommand command = new SqlCommand(queryString, connection);
command.Parameters.AddWithValue("#Status", "Complete");
connection.Open();
SqlDataReader reader = command.ExecuteReader();
try
{
while (reader.Read())
{
log.LogInformation(String.Format("{0}, {1}", reader["Id"], reader["Status"]));
Console.WriteLine(String.Format("{0}, {1}",
reader["Id"], reader["Status"]));// etc
}
}
finally
{
// Always call Close when done reading.
reader.Close();
}
}
}
Created Sample data in the database with below SQL Query:
CREATE TABLE dbo.Dispatch (
[Id] int primary key,
[Status] nvarchar(200) not null
)
INSERT INTO Dispatch (Id, Status)
VALUES
(571, 'Pending'), (572, 'Complete'),
(573, 'InProgress'), (598, 'Complete'),
(593, 'Complete'),(581, 'Complete'),
(597, 'InProgress'), (596, 'Pending');
Result:
I could see the results in the console for both of the methods Logging and Console WriteLine:
I was not able to get the parameter to work so for now I was able to get the timer to work with the azure sql input binding.
public async Task Run([TimerTrigger("0 */2 * * * *")] TimerInfo myTimer,
[Sql("select top(10)id, status, arrivaldate from Dispatch where
status = \'in progress\';" ,
CommandType = System.Data.CommandType.Text,
ConnectionStringSetting = "SqlConnectionString")]
IAsyncEnumerable<Dispatch> dispatch, ILogger log)
{
IAsyncEnumerator<Dispatch> enumerator =
dispatch.GetAsyncEnumerator();
var dispatchList = new List<FindDispatch>();
while (await enumerator.MoveNextAsync())
{
dispatchList.Add(enumerator.Current);
}
await enumerator.DisposeAsync();
I have a timer-triggered function setup like this
public class PBARCronTrigger
{
private readonly eReserveFunctions _settings;
public PBARCronTrigger(IOptions<eReserveFunctions> settings)
{
_settings = settings.Value;
}
[FunctionName("PBARCronTrigger")]
public async Task Run([TimerTrigger("%PBARCron%")] TimerInfo myTimer, ILogger log)
{
log.LogInformation($"PBARCronTrigger function executing at: {DateTime.Now}");
using (var client = new HttpClient())
and I have the app setting for PBARCron set to every 5 minutes:
but the trigger is not triggering. I connect to live logs and nothing happens. It keeps on saying "No new trace in the past x min(s)"
Your cron expression doesn't look right to me. Checking it in an evaluator even states that it's non-standard and may not work in every environment.
I think what you want is 0-55/5 * * * *, or more simply, */5 * * * *.
I am trying to store the time stamp information in durable entities and retrieve it every time a trigger fired. Here is how I am doing it. I want the timestamp value set by the current execution to be available for the next trigger. But when the control reaches "string prevTS = await context.CallEntityAsync(entityId, "Get");" to goes back to start of the function again. What am I missing here.
I want execution to be sequential between the timer triggers.
'''
***public static class GetOpenDataRealtimeFeed
{
[FunctionName("GetOpenDataOrchestrator")]
public static async Task<List<string>> RunOrchestrator(
[OrchestrationTrigger] IDurableOrchestrationContext context, Binder binder, ILogger log)
{
var outputs = new List<string>();
var entityId = new EntityId(nameof(GetPrevLastModifiedTimestamp), "entityKey2");
string prevTS = await context.CallEntityAsync<string>(entityId, "Get");
string currentTS = DateTime.Now.ToString();
outputs.Add(currentTS);
outputs.Add(prevTS);
context.SignalEntity(entityId, "Set", currentTS);
return null;
}
//Durable entity function to get & set the last modified timestamp
[FunctionName("GetPrevLastModifiedTimestamp")]
public static void GetPrevLastModifiedTimestamp([EntityTrigger] IDurableEntityContext ctx)
{
switch (ctx.OperationName.ToLowerInvariant())
{
case "set":
ctx.SetState(ctx.GetInput<string>());
break;
case "get":
ctx.Return(ctx.GetState<string>());
break;
}
}
[FunctionName("getOpenDataRealtimeFeed_Trigger")]
public static async Task Run(
[TimerTrigger("%triggerTimer%")] TimerInfo myTimer,
[DurableClient] IDurableOrchestrationClient starter,
ILogger log)
{
// Function input comes from the request content.
string instanceId = await starter.StartNewAsync("GetOpenDataOrchestrator", null);
log.LogInformation($"Started orchestration with ID = '{instanceId}'.");
}
}
}***
'''
I assume you are referring to the current line while debugging. If so, this is expected.
Since Durable Functions replays functions after awaiting a durable client call, execution won't ever go through the first round. Only the final replay will be "sequential" step overs.
I want to call another (not timer triggered) azure function from my timer triggered azure function.
It compiles but during runtime I get the error:
System.ArgumentException: 'The function 'HelloWorld' doesn't exist, is disabled, or is not an orchestrator function. Additional info: No orchestrator functions are currently registered!'
I reduced it to this tiny code snippet.
[FunctionName("HelloWorld")]
public static string HelloWorld([ActivityTrigger] string name, ILogger log)
{
return $"Hello {name}!";
}
[FunctionName("DownloadLiveList")]
public async void DownloadLiveList([DurableClient] IDurableOrchestrationClient client, [TimerTrigger("0 0 0 * * *", RunOnStartup = true)]TimerInfo myTimer, ILogger log)
{
await client.StartNewAsync<string>("HelloWorld", "Magdeburg");
}
As I took the idea from the official Microsoft example for that kind of azure function cascading, I've no clue, why the function "HelloWorld" is not registered. After uploading into azure, the function is visible in the azure portal as all other functions from the class.
Your time trigger function needs to invoke the start function written with Durable Function Framework. Here's a sample:
[FunctionName("Function1")]
public async Task Run([TimerTrigger("0 */1 * * * *")]TimerInfo myTimer, ILogger log)
{
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
var url = "http://localhost:7071/api/Durable_Starter";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.AutomaticDecompression = DecompressionMethods.GZip;
using (HttpWebResponse response = (HttpWebResponse) await request.GetResponseAsync())
using (Stream stream = response.GetResponseStream())
using (StreamReader reader = new StreamReader(stream))
{
var html = reader.ReadToEnd();
log.LogInformation(html);
}
}
[FunctionName("Durable_Starter")]
public async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")]HttpRequest req, [DurableClient] IDurableClient starter, ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string instanceId = await starter.StartNewAsync("Durable_Orchestrator");
log.LogInformation($"Started orchestration with ID = '{instanceId}'.");
var checkStatusResponse = starter.CreateCheckStatusResponse(req, instanceId);
return checkStatusResponse;
}
[FunctionName("Durable_Orchestrator")]
public async Task RunOrchestrator([OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log)
{
var message = await context.CallActivityAsync<string>("HelloWorld", "Thiago");
log.LogInformation(message);
}
[FunctionName("HelloWorld")]
public string HelloWorldActivity([ActivityTrigger] string name)
{
return $"Hello {name}!";
}
I've improved my code a bit, moving the timer to app settings and using %% variable in the timer definition of the function.
I have a function that I want to run every night at 1am. But for debugging I really want it to fire as fast as possible after startup. I solved this by setting the pattern to 5 * * * * * timer in app setting locally, and 0 0 1 * * * in the app config in Azure.
But I don't want to have it actually run every 5 sec, I just want it to fire as fast as possible while debugging. So I've added a counter variable. Enabling only the first run to move forward:
private static int count = 0;
[Disable]
[FunctionName("FetchProjects")]
async public static void Run([TimerTrigger("%FetchProjectsTimer%")]TimerInfo myTimer, TraceWriter log)
{
#if DEBUG
if (count != 0) return;
count++;
#endif
How can I improve this if possible? The count isn't thread safe for one, and I want to remove the #if DEBUG check if possible
It's not clear what you're trying to achieve. You just want to test the logic of your time trigger? What about unit test it (just the logic).
I usually leave my Azure functions as thin as possible and unit test the business logic. Here's how you can do it:
public static class Function1
{
[FunctionName("Function1")]
public static void Run([TimerTrigger("0 */5 * * * *")]TimerInfo myTimer, ILogger log)
{
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
FooService.DoFoo();
}
[FunctionName("Function2")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
FooService.DoFoo();
return new OkObjectResult($"Success");
}
}
//extracted logic which I will be tested by Nunit
public static class FooService
{
public static void DoFoo()
{
//some logic in here
}
}