I have written following code to read the data from text file and then separate the sentences and send them to azure event hub.
I am able to send the data to event hub but not able to push data in document db.
How to push data in document db using webjobs? I am running webjobs from console application, Do I need add more configuration to run it locally?
Program.cs
static void Main(string[] args)
{
JobHostConfiguration config = new JobHostConfiguration();
config.Tracing.ConsoleLevel = System.Diagnostics.TraceLevel.Error;
var eventHubConfig = new EventHubConfiguration();
eventHubConfig.AddReceiver(eventHubName, connectionString);
config.UseEventHub(eventHubConfig);
JobHost host = new JobHost(config);
config.DashboardConnectionString = StorageConnectionString;
if (config.IsDevelopment)
{
config.UseDevelopmentSettings();
}
//Send test messages
Task.Run(() =>
{
SendMessagesToEventHub();
});
host.RunAndBlock();
}
function.cs
class Functions
{
public static void Run([EventHubTrigger("azurepochub")] EventData message, [Microsoft.Azure.WebJobs.DocumentDB("testcosmosdb01122018", "Items", ConnectionStringSetting = "dbConnctionString")]out dynamic document)
{
string data = Encoding.UTF8.GetString(message.GetBytes());
document = data;
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine($"Message received. Data: '{data}'");
Console.ResetColor();
}
}
You could get answer from github Azure WebJobs SDK Extensions. If we want to insert document to the Azure documentdb, we need to insert the object into it. In your case, your output is string.
Do I need add more configuration to run it locally?
I also do demo for that. The following is the detail steps
1.Create an .net framework Webjob project.
2.Add the AzureWebJobsDocumentDBConnectionString in the App.config file.
<appSettings>
<!-- Service Bus specific app setings for messaging connections -->
<add key="Microsoft.ServiceBus.ConnectionString" value="Endpoint=sb://[your namespace].servicebus.windows.net;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=[your secret]" />
<add key ="AzureWebJobsDocumentDBConnectionString" value="xxxxx"/>
</appSettings>
3.Add the following code in the Program.cs file.
using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.DocumentDB;
using Microsoft.Azure.WebJobs.ServiceBus;
using Microsoft.ServiceBus.Messaging;
namespace WebJobTest
{
// To learn more about Microsoft Azure WebJobs SDK, please see https://go.microsoft.com/fwlink/?LinkID=320976
class Program
{
// Please set the following connection strings in app.config for this WebJob to run:
// AzureWebJobsDashboard and AzureWebJobsStorage
private static string eventHubName = "eventhubNam";
private static string connectionString = "eventhub connectionstring";
static void Main()
{
JobHostConfiguration config = new JobHostConfiguration();
config.Tracing.ConsoleLevel = System.Diagnostics.TraceLevel.Error;
var eventHubConfig = new EventHubConfiguration();
eventHubConfig.AddReceiver(eventHubName, connectionString);
config.UseDocumentDB(new DocumentDBConfiguration
{
ConnectionString = "DocumentDB ConnectionString"
});
config.UseEventHub(eventHubConfig);
config.DashboardConnectionString = "storage connection string";
JobHost host = new JobHost(config);
if (config.IsDevelopment)
{
config.UseDevelopmentSettings();
}
//Send test messages
Task.Run(() =>
{
SendMessagesToEventHub();
});
host.RunAndBlock();
}
static void SendMessagesToEventHub()
{
var eventHubClient = EventHubClient.CreateFromConnectionString(connectionString, eventHubName);
try
{
var message = Guid.NewGuid().ToString();
Console.WriteLine("{0} > Sending message: {1}", DateTime.Now, message);
eventHubClient.Send(new EventData(Encoding.UTF8.GetBytes(message)));
}
catch (Exception exception)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("{0} > Exception: {1}", DateTime.Now, exception.Message);
Console.ResetColor();
}
Thread.Sleep(200);
}
}
}
4.In the function.cs
public static void Run([EventHubTrigger("eventhub name")] EventData message, [DocumentDB("document Database name", "collection", ConnectionStringSetting = "AzureWebJobsDocumentDBConnectionString")]out Item document)
{
string data = Encoding.UTF8.GetString(message.GetBytes());
document = new Item{Text = data};
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine($"Message received. Data: '{data}'");
Console.ResetColor();
}
public class Item
{
public string Text;
}
5.Check the result from console.
Finish debug from the console then check from the Azure documentdb
Related
I'm trying to use Azure Ocr into my website c#.
I added the package Microsoft.Azure.CognitiveServices.Vision.ComputerVision and I wrote code, with key and endpoint of my subscription.
static string subscriptionKey = "mykey";
static string endpoint = "https://myocr.cognitiveservices.azure.com/";
private const string ANALYZE_URL_IMAGE = "https://raw.githubusercontent.com/Azure-Samples/cognitive-services-sample-data-files/master/ComputerVision/Images/printed_text.jpg";
protected void Page_Load(object sender, EventArgs e)
{
// Create a client
ComputerVisionClient client = Authenticate(endpoint, subscriptionKey);
// Analyze an image to get features and other properties.
AnalyzeImageUrl(client, ANALYZE_URL_IMAGE).Wait();
}
public static ComputerVisionClient Authenticate(string endpoint, string key)
{
ComputerVisionClient client =
new ComputerVisionClient(new ApiKeyServiceClientCredentials(key))
{ Endpoint = endpoint };
return client;
}
public static async Task AnalyzeImageUrl(ComputerVisionClient client, string imageUrl)
{
// Read text from URL
var textHeaders = await client.ReadAsync(imageUrl);
...
}
It seems all ok, but at line
var textHeaders = await client.ReadAsync(urlFile);
website crashes.
I don't understand why. No error, it's just stopped.
So I ask: azure ocr can to be use only with console app?
EDIT
The code is ok for ConsoleApp and WebApp but not working for my asp.net WEBSITE.
Could be a problem with async?
We can use OCR with web app also,I have taken the .net core 3.1 webapp in Visual Studio and installed the dependency of Microsoft.Azure.CognitiveServices.Vision.ComputerVision by selecting the check mark of include prerelease as shown in the below image:
After creating computer vision resource in Azure Portal, copied the key and endpoint from there and used inside the c# code.
using System;
using System.Collections.Generic;
using Microsoft.Azure.CognitiveServices.Vision.ComputerVision;
using Microsoft.Azure.CognitiveServices.Vision.ComputerVision.Models;
using System.Threading.Tasks;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Threading;
using System.Linq;
namespace ComputerVisionQuickstart
{
class Program
{
// Add your Computer Vision subscription key and endpoint
static string subscriptionKey = "c1****b********";
static string endpoint = ".abc.cognitiveservices.azure.com/";
private const string READ_TEXT_URL_IMAGE = "https://raw.githubusercontent.com/Azure-Samples/cognitive-services-sample-data-files/master/ComputerVision/Images/printed_text.jpg";
static void Main(string[] args)
{
Console.WriteLine("Azure Cognitive Services Computer Vision - .NET quickstart example");
Console.WriteLine();
ComputerVisionClient client = Authenticate(endpoint, subscriptionKey);
// Extract text (OCR) from a URL image using the Read API
ReadFileUrl(client, READ_TEXT_URL_IMAGE).Wait();
}
public static ComputerVisionClient Authenticate(string endpoint, string key)
{
ComputerVisionClient client =
new ComputerVisionClient(new ApiKeyServiceClientCredentials(key))
{ Endpoint = endpoint };
return client;
}
public static async Task ReadFileUrl(ComputerVisionClient client, string urlFile)
{
Console.WriteLine("----------------------------------------------------------");
Console.WriteLine("READ FILE FROM URL");
Console.WriteLine();
// Read text from URL
var textHeaders = await client.ReadAsync(urlFile);
// After the request, get the operation location (operation ID)
string operationLocation = textHeaders.OperationLocation;
Thread.Sleep(2000);
// Retrieve the URI where the extracted text will be stored from the Operation-Location header.
// We only need the ID and not the full URL
const int numberOfCharsInOperationId = 36;
string operationId = operationLocation.Substring(operationLocation.Length - numberOfCharsInOperationId);
// Extract the text
ReadOperationResult results;
Console.WriteLine($"Extracting text from URL file {Path.GetFileName(urlFile)}...");
Console.WriteLine();
do
{
results = await client.GetReadResultAsync(Guid.Parse(operationId));
}
while ((results.Status == OperationStatusCodes.Running ||
results.Status == OperationStatusCodes.NotStarted));
// Display the found text.
Console.WriteLine();
var textUrlFileResults = results.AnalyzeResult.ReadResults;
foreach (ReadResult page in textUrlFileResults)
{
foreach (Line line in page.Lines)
{
Console.WriteLine(line.Text);
}
}
Console.WriteLine();
}
}
}
The above code is taken from the Microsoft Document.
I can be able to read the text inside the image successfully as shown in the below screenshot:
Question:
I am not sure if this falls under question or code review because the code works where I do not know if it is implemented correctly. But, do we need to acquire the access token from Microsoft.Graph using either silent or interactive modes? From what I can tell the answer is, No. (see Context below)
The new implementation seems to be drastically scaled down with the whole idea of silent and interactive token retrieval being removed. Is this correct?
using Azure.Identity;
using Microsoft.Graph;
using System;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
var scopes = new[] { "User.Read" };
// Multi-tenant apps can use "common",
// single-tenant apps must use the tenant ID from the Azure portal
var tenantId = "SomeGuid";
// Value from app registration
var clientId = "SomeGuid";
var options = new InteractiveBrowserCredentialOptions
{
TenantId = tenantId,
ClientId = clientId,
AuthorityHost = AzureAuthorityHosts.AzurePublicCloud,
// MUST be http://localhost or http://localhost:PORT
// See https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/System-Browser-on-.Net-Core
RedirectUri = new Uri("http://localhost:1234"),
};
// https://learn.microsoft.com/dotnet/api/azure.identity.interactivebrowsercredential
var interactiveCredential = new InteractiveBrowserCredential(options);
var graphClient = new GraphServiceClient(interactiveCredential, scopes);
// Interactive browser login occurs here.
var me = graphClient.Me.Request().GetAsync().Result;
// Printing the results
Console.WriteLine("-------- Data from call to MS Graph --------");
Console.Write(Environment.NewLine);
Console.WriteLine($"Id: {me.Id}");
Console.WriteLine($"Display Name: {me.DisplayName}");
Console.WriteLine($"Email: {me.Mail}");
//Console.ReadLine();
}
}
}
Context:
As part of our routine maintenance, I was tasked with upgrading our NuGet packages on a Winforms desktop application that is running in Azure and whose users are in Azure Active Directory Services (AADS). One of the packages, Microsoft.Graph, had a major version change. https://www.nuget.org/packages/Microsoft.Graph/4.0.0
The documentation on it indicated a new feature for handling the TokenCredentialClass. https://github.com/microsoftgraph/msgraph-sdk-dotnet/blob/4.0.0/docs/upgrade-to-v4.md#new-capabilities
From what I can tell, there is a separate and distinct break on how the token is retrieved. Previously, we followed the method provided here: https://learn.microsoft.com/en-us/azure/active-directory/develop/tutorial-v2-windows-desktop#add-the-code-to-initialize-msal
Old way:
using Microsoft.Graph;
using Microsoft.Graph.Auth;
using Microsoft.Identity.Client;
using System;
using System.Linq;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
_PublicClientApp = PublicClientApplicationBuilder.Create(ClientId)
.WithRedirectUri("http://localhost:1234")
.WithAuthority(AzureCloudInstance.AzurePublic, TenantId)
.Build();
// We sign the user in here
bolIsAutorizeSSO = CallMicrosoftSSO().GetAwaiter().GetResult();
InteractiveAuthenticationProvider = new InteractiveAuthenticationProvider(PublicClientApp, Scopes);
GraphServiceClient = new Microsoft.Graph.GraphServiceClient(InteractiveAuthenticationProvider);
if (bolIsAutorizeSSO)
{
// We also signt the user in here.
var User = GraphServiceClient.Me.Request().GetAsync().Result;
// Printing the results
Console.WriteLine("-------- Data from call to MS Graph --------");
Console.Write(Environment.NewLine);
Console.WriteLine($"Id: {User.Id}");
Console.WriteLine($"Display Name: {User.DisplayName}");
Console.WriteLine($"Email: {User.Mail}");
}
else
{
// signout
Console.ReadLine();
}
}
public static async Task<bool> CallMicrosoftSSO()
{
AuthenticationResult authResult = null;
var app = PublicClientApp;
var accounts = await app.GetAccountsAsync();
try
{
authResult = await app.AcquireTokenInteractive(Scopes)
.WithAccount(accounts.FirstOrDefault())
.WithPrompt(Microsoft.Identity.Client.Prompt.ForceLogin)
.ExecuteAsync();
}
catch (MsalUiRequiredException _Exception)
{
// A MsalUiRequiredException happened on AcquireTokenSilent.
// This indicates you need to call AcquireTokenInteractive to acquire a token.
Console.WriteLine(_Exception.Message);
}
catch (MsalException msalex)
{
if (msalex.ErrorCode != "authentication_canceled")
{
Console.WriteLine(msalex.Message);
}
}
catch (Exception _Exception)
{
Console.WriteLine(_Exception.Message);
}
if (authResult != null)
{
return true;
}
return false;
}
private static string ClientId = "SomeGuid";
private static string TenantId = "SomeGuid";
private static string[] Scopes = new string[] { "User.Read" };
private static Microsoft.Graph.GraphServiceClient GraphServiceClient;
private static bool bolIsAutorizeSSO = false;
private static InteractiveAuthenticationProvider InteractiveAuthenticationProvider;
private static IPublicClientApplication _PublicClientApp;
public static IPublicClientApplication PublicClientApp { get { return _PublicClientApp; } }
}
}
I am struggling to make sense of it. Partly because the feature is brand new and there are very few code samples up on the internet that say do it this way. What I have found seems to point me back to what we already are using (more on that in a bit). So, the examples may not yet be fully updated.
Azure WebJob, how to be notified if it aborted?
(1)Always Availability is on for the service.
(2) SCM_COMMAND_IDLE_TIMEOUT = 2000.
WEBJOBS_IDLE_TIMEOUT = 2000.
But as i'm new to this. can you please help me on this one where can i put the logic
You could add the logic in the Function.cs file. For more information you could refer to the detail steps
Steps:
1.follow official document to create a webjob project.
2.Add Functions.cs in the project
using System.IO;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions;
using SendGrid;
public class Functions
{
//demo webjob trigger
public static void ProcessQueueMessage([QueueTrigger("queue")] string message, TextWriter log)
{
log.WriteLine(message);
}
// error monitor
public static void ErrorMonitor([ErrorTrigger("0:30:00", 10, Throttle = "1:00:00")]TraceFilter filter, [SendGrid] SendGridMessage message)
{
message.Subject = "WebJobs Error Alert";
message.Text = filter.GetDetailedMessage(5);
}
}
3.If want to use ErrorTrigger and SendGrid we need to config it in the Program.cs file.
static void Main()
{
var config = new JobHostConfiguration();
if (config.IsDevelopment)
{
config.UseDevelopmentSettings();
}
config.UseCore();
config.UseSendGrid(new SendGridConfiguration
{
ApiKey = "xxxxx",
FromAddress = new Email("emailaddress","name"),
ToAddress = new Email("emailaddress","name")
});
var host = new JobHost(config);
// The following code ensures that the WebJob will be running continuously
host.RunAndBlock();
}
4.If we want to test it locally, we need to add Storage connection string in the App Settings collection.
<connectionStrings>
<add name="AzureWebJobsStorage" connectionString="{storage connection string}" />
</connectionStrings>
I am trying to expose a REST API using Azure Functions which returns terms from a specific termset in SharePoint Online using CSOM and C#.
I can definitely invoke this exact same CSOM code from a console app and from an Azure API app and it is able to loop through the terms and output to console or the HTTP response successfully.
However, when the code below is invoked from the Azure Function host, it ALWAYS find a collection of NULL term objects, when looping through the TermCollection or the IEnumerable<Term> (I’ve tried by using ClientContext.LoadQuery on TermSet.GetAllTerms(), as well as by just loading the TermCollection via the TermSet.Terms property).
As soon as the iterator hits a term in the foreach (which I’ve also tried as just a LINQ Select), it thinks that the item is NULL, so calling properties on it throws the NullReferenceException. I cannot reproduce the behavior from the console app or from the API app calling into the same code - it just works as expected there and retrieves each Term object.
Why is this happening when SAME CODE is invoked from different hosts??
Why would this happen in the Azure Functions host, but not in Console app or the Azure API app?
What is the difference when invoked from an Azure Function host??
I would really like to use Azure Functions for the consumption pricing benefits, so I don't have to host this in an App Service.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security;
using Microsoft.SharePoint.Client;
using Microsoft.SharePoint.Client.Taxonomy;
namespace CsomTaxonomyHelper
{
public class TermSearch
{
private readonly ClientContext ctx;
public TermSearch(ClientContext context)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
ctx = context;
}
public IEnumerable<TermViewModel> GetTerms(Guid termSetId)
{
var taxonomySession = TaxonomySession.GetTaxonomySession(ctx);
var termStore = taxonomySession.GetDefaultSiteCollectionTermStore();
var termSet = termStore.GetTermSet(termSetId);
//get flat list of terms, so we don't make recursive calls to SPO
var allTerms = ctx.LoadQuery(termSet.GetAllTerms().IncludeWithDefaultProperties());
ctx.ExecuteQuery();
return ToViewModel(allTerms);
}
static IEnumerable<TermViewModel> ToViewModel(IEnumerable<Term> allTerms)
{
var results = allTerms.Select(term => new TermViewModel
{
Id = term.Id, //BOOM! <-- within the context of an Azure Function the "allTerms" IEnumerable is a list of nulls
Name = term.Name,
ParentId = TryGetParentId(term)
});
return results;
}
static Guid? TryGetParentId(Term term)
{
try
{
if (term.Parent.IsPropertyAvailable("Id"))
return term.Parent.Id;
}
catch (ServerObjectNullReferenceException) { }
return null;
}
}
public class PasswordString
{
public SecureString SecurePassword { get; private set; }
public PasswordString(string password)
{
SecurePassword = new SecureString();
foreach (char c in password.ToCharArray())
{
SecurePassword.AppendChar(c);
}
SecurePassword.MakeReadOnly();
}
}
}
Here's the "run.csx" function, invoking the code above which has been compiled into a DLL and placed in the Bin folder of the Azure Function:
#r "CsomTaxonomyHelper.dll"
#r "Newtonsoft.Json"
using System.Net;
using Microsoft.SharePoint.Client;
using Microsoft.SharePoint.Client.Taxonomy;
using CsomTaxonomyHelper;
using Newtonsoft.Json;
static TraceWriter _log = null;
public static HttpResponseMessage Run(HttpRequestMessage req, TraceWriter log)
{
_log = log;
_log.Info("C# HTTP trigger function processed a request. Getting mmd terms from SPO...");
var terms = GetFocusAreas();
var result = JsonConvert.SerializeObject(terms);
return req.CreateResponse(HttpStatusCode.OK, result);
}
static IEnumerable<TermViewModel> GetFocusAreas()
{
string spSiteUrl = System.Environment.GetEnvironmentVariable("SPOSiteUrl", EnvironmentVariableTarget.Process);
string userName = System.Environment.GetEnvironmentVariable("SPOUserName", EnvironmentVariableTarget.Process);
string password = System.Environment.GetEnvironmentVariable("SPOPassword", EnvironmentVariableTarget.Process);
var securePwd = new PasswordString(password).SecurePassword;
using (var ctx = new ClientContext(spSiteUrl))
{
ctx.Credentials = new SharePointOnlineCredentials(userName, securePwd);
ctx.ExecuteQuery();
_log.Info("Logged into SPO service.");
var search = new TermSearch(ctx);
try
{
var result = search.GetTerms(new Guid("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"));
return result;
}
catch (Exception ex)
{
_log.Error(ex.Message, ex);
throw;
}
}
}
Project.json:
{
"frameworks": {
"net46":{
"dependencies": {
"Microsoft.SharePointOnline.CSOM": "16.1.6112.1200"
}
}
}
}
Here's the screenshot of the local debugger, when using the Azure Functions CLI to debug this (you can see that it did find 10 items in the collection, but all items are null):
Not the solution, but adding to the conversation - I was able to test with PnP-PowerShell (2017-Feb). Terms were just added.
SPO, CSOM and PnP-PowerShell.
Installing PnP-PowerShell to a PowerShell function:
I've tried to come up with something from the example in the WebJobsSDK gitHub
var eventHubConfig = new EventHubConfiguration();
string eventHubName = "MyHubName";
eventHubConfig.AddSender(eventHubName,"Endpoint=sb://test.servicebus.windows.net/;SharedAccessKeyName=SendRule;SharedAccessKey=xxxxxxxx");
eventHubConfig.AddReceiver(eventHubName, "Endpoint=sb://test.servicebus.windows.net/;SharedAccessKeyName=ReceiveRule;SharedAccessKey=yyyyyyy");
config.UseEventHub(eventHubConfig);
JobHost host = new JobHost(config);
But I'm afraid that's not far enough for someone of my limited "skillset"!
I can find no instance of JobHostConfiguration that has a UseEventHub property (using the v1.2.0-alpha-10291 version of the Microsoft.AzureWebJobs package), so I can't pass the EventHubConfiguration to the JobHost.
I've used EventHub before, not within the WebJob context. I don't see if the EventHostProcessor is still required if using the WebJob triggering...or does the WebJob trigger essentially act as the EventHostProcessor?
Anyway, if anyone has a more complete example for a simpleton like me that would be really sweet! Thanks
From the documentation here, you should have all the information you need.
What you are missing is a reference of the Microsoft.Azure.WebJobs.ServiceBus.1.2.0-alpha-10291 nuget package.
The UseEventHub is an extension method that is declared in this package.
Otherwise your configuration seems ok.
Here is an example on how to receive or send messages from/to an EventHub:
public class BasicTest
{
public class Payload
{
public int Counter { get; set; }
}
public static void SendEvents([EventHub("MyHubName")] out Payload x)
{
x = new Payload { Counter = 100 };
}
public static void Trigger(
[EventHubTrigger("MyHubName")] Payload x,
[EventHub("MyHubName")] out Payload y)
{
x.Counter++;
y = x;
}
}
EventProcessorHost is still required, as the WebJob just provides the hosting environment for running it. As far as I know, EventProcessorHost is not integrated so deeply into WebJob, so its triggering mechanism cannot be used for processing EventHub messages. I use WebJob for running EventProcessorHost continuously:
public static void Main()
{
RunAsync().Wait();
}
private static async Task RunAsync()
{
try
{
using (var shutdownWatcher = new WebJobsShutdownWatcher())
{
await Console.Out.WriteLineAsync("Initializing...");
var eventProcessorHostName = "eventProcessorHostName";
var eventHubName = ConfigurationManager.AppSettings["eventHubName"];
var consumerGroupName = ConfigurationManager.AppSettings["eventHubConsumerGroupName"];
var eventHubConnectionString = ConfigurationManager.ConnectionStrings["EventHub"].ConnectionString;
var storageConnectionString = ConfigurationManager.ConnectionStrings["EventHubStorage"].ConnectionString;
var eventProcessorHost = new EventProcessorHost(eventProcessorHostName, eventHubName, consumerGroupName, eventHubConnectionString, storageConnectionString);
await Console.Out.WriteLineAsync("Registering event processors...");
var processorOptions = new EventProcessorOptions();
processorOptions.ExceptionReceived += ProcessorOptions_ExceptionReceived;
await eventProcessorHost.RegisterEventProcessorAsync<CustomEventProcessor>(processorOptions);
await Console.Out.WriteLineAsync("Processing...");
await Task.Delay(Timeout.Infinite, shutdownWatcher.Token);
await Console.Out.WriteLineAsync("Unregistering event processors...");
await eventProcessorHost.UnregisterEventProcessorAsync();
await Console.Out.WriteLineAsync("Finished.");
}
catch (Exception ex)
{
await HandleErrorAsync(ex);
}
}
}
private static async void ProcessorOptions_ExceptionReceived(object sender, ExceptionReceivedEventArgs e)
{
await HandleErrorAsync(e.Exception);
}
private static async Task HandleErrorAsync(Exception ex)
{
await Console.Error.WriteLineAsync($"Critical error occured: {ex.Message}{ex.StackTrace}");
}