SaveChangesAsync not working inside Azure webjob - azure

I have an Azure job which asynchronously saves records to the database. I am finding that it does not actually save anything to the database. I am definitely using async/awaits everywhere. I am adding parent (market) and child records. My database has referential constraints so the parent has to exist before the child, but that should be fine as I am doing them in the right order. I have no try-catches around my methods and there is nothing in the azure logs so it appears the job is succeeding. I have called Method1 with an await from a Winforms exe and it works fine from that. What can be wrong?
public static async Task MyJob([TimerTrigger("00:02:00", RunOnStartup = true)] TimerInfo timerInfo, TextWriter log)
{
await Jobs.Method1(Client, Logger);
}
public static async Task Method1(IClient client, ILogger logger)
{
await DataRepository.AddMarket(event.Id, event.MarketId);
await DataRepository.AddMarketChild(event.MarketId, 999);
}
public static async Task<Market> AddMarket(string eventId, string marketId)
{
using (var ctx = BTBEntities.CreateContext())
{
var market = new Market()
{
MarketId = marketId,
EventId = eventId,
};
ctx.Markets.Add(market);
await ctx.SaveChangesAsync();
return market;
}
}
public static async Task<HorseBet> AddHorseBet(string marketId, long selectionId)
{
using (var ctx = BTBEntities.CreateContext())
{
var bet = new MarketChild()
{
MarketId = marketId,
SelectionId = selectionId,
};
ctx.MarketChilds.Add(bet);
await ctx.SaveChangesAsync();
return bet;
}
}

Turns out there was a database problem it was just being swallowed by Azure. This is how I fixed it.
public override System.Threading.Tasks.Task<int> SaveChangesAsync()
{
try
{
return base.SaveChangesAsync();
}
catch (DbEntityValidationException ex)
{
var errorMessages = ex.EntityValidationErrors
.SelectMany(x => x.ValidationErrors)
.Select(x => x.ErrorMessage);
var fullErrorMessage = string.Join("; ", errorMessages);
var exceptionMessage = string.Concat(ex.Message, " The validation errors are: ", fullErrorMessage);
throw new DbEntityValidationException(exceptionMessage, ex.EntityValidationErrors);
}
}

Related

How to check specific record exist in azure service bus topic

How do i to check specific record exist in azure service bus topic. In my case, multiple client enter record into service bus topic so i need to validate the existing record. If not record exist in topic than enter the record. i need to validate record in SendMessageAsync method
following is my code
private ITopicClient client;
private DateTime tokenExpiresAtUtc = DateTime.MinValue;
private string tokenValue = string.Empty;
public async Task SendOrderMessageAsync(string OrderNumber, int RecipientStoreId)
{
await SendMessageAsync(GetMessage(JsonConvert.SerializeObject(new OrderNotificationViewModel
{
SenderStoreId = this.SenderStoreId.Value,
NotificationType = NotificationTypes.Order,
OrderNumber = OrderNumber,
StoreId = RecipientStoreId
})), RecipientStoreId);
}
private async Task SendMessageAsync(Message message, int StoreId)
{
try
{
await (await GetClient(GetTopicName(StoreId))).SendAsync(message);
}
catch (MessagingEntityNotFoundException ex)
{
var topic = await CreateTopic(GetTopicName(StoreId));
var subscription = await CreateSubscription(topic.Name, GetSubscriptionName(StoreId));
await (await GetClient(GetTopicName(StoreId))).SendAsync(message);
}
}
public async Task SendOrderNotesMessageAsync(string OrderNumber, List<string> Notes, int RecipientStoreId)
{
await SendMessageAsync(GetMessage(JsonConvert.SerializeObject(new OrderNotesNotificationViewModel
{
SenderStoreId = this.SenderStoreId.Value,
NotificationType = NotificationTypes.OrderNotes,
OrderNumber = OrderNumber,
Notes = Notes,
StoreId = RecipientStoreId
})), RecipientStoreId);
}
private async Task<ITopicClient> GetClient(string TopicName)
{
if (client != null && client.TopicName != TopicName)
{
await client.CloseAsync();
client = null;
}
if (client == null)
{
client = new TopicClient(GlobalConfig.Instance.ServiceBusConnectionString, TopicName);
}
return client;
}
private string GetTopicName(int StoreId)
{
return GlobalConfig.Instance.ServiceBusTopicNamePattern.Replace("{StoreId}", StoreId.ToString());
}
private string GetSubscriptionName(int StoreId)
{
return GlobalConfig.Instance.ServiceBusSubscriptionNamePattern.Replace("{StoreId}", StoreId.ToString());
}
private Message GetMessage(string messageBody)
{
return new Message(Encoding.UTF8.GetBytes(messageBody));
}
Do not do this. Service Bus is a messaging service, not a data store.
Topics specifically are designed for multi-casting and decoupling senders/publishers from the receivers/subscribers.
If you need duplicate message detection, use a feature specifically designed for that. You can leverage deduplication to work with any content you need, which I described here.

MultipleResumeHandlerExeption when forwarding from LuisDialog to IDialog [duplicate]

I'm creating a bot for FAQ . When bot start conversations send a PromptDialog with 2 options: english, french.
I want to forward the dialog to EnglishLuis when user chooses English button, and FrenchLuis when choosing French.
Here is my code :
Rootdialog.cs
public class RootDialog : IDialog<object>
{
private const string EnglishMenu = "English";
private const string FrenchMenu = "French";
private const string QAMenu = "Q&A";
private List<string> mainMenuList = new List<string>() { EnglishMenu, FrenchMenu, QAMenu };
private string location;
public async Task StartAsync(IDialogContext context)
{
await context.PostAsync("Welcome to Root Dialog");
context.Wait(MessageReceiveAsync);
}
private async Task MessageReceiveAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
var reply = await result;
if (reply.Text.ToLower().Contains("help"))
{
await context.PostAsync("You can implement help menu here");
}
else
{
await ShowMainmenu(context);
}
}
private async Task ShowMainmenu(IDialogContext context)
{
//Show menues
PromptDialog.Choice(context, this.CallDialog, this.mainMenuList, "What do you want to do?");
}
private async Task CallDialog(IDialogContext context, IAwaitable<string> result)
{
//This method is resume after user choise menu
// this.luisResult = result;
// var message = await result;
var selectedMenu = await result;
var message = await result;
switch (selectedMenu)
{
case EnglishMenu:
//Call child dialog without data
// context.Call(new EnglishLuis(),ResumeAfterDialog);
// context.Call(new EnglishLuis(), ResumeAfterDialog);
await Conversation.SendAsync(context.MakeMessage(), () => new EnglishLuis());
break;
case FrenchMenu:
//Call child dialog with data
context.Call(new HotelDialog(location), ResumeAfterDialog);
break;
case QAMenu:
context.Call(new LuisCallDialog(),ResumeAfterDialog);
break;
}
}
private async Task ResumeAfterDialog(IDialogContext context, IAwaitable<object> result)
{
//Resume this method after child Dialog is done.
var test = await result;
if (test != null)
{
location = test.ToString();
}
else
{
location = null;
}
await this.ShowMainmenu(context);
}
}
}
EnglishLuis.cs :
public class EnglishLuis : LuisDialog<object>
{
private string location;
// string message = $"welcome to english dialog";
public async Task None(IDialogContext context, LuisResult result)
{
string message = $"Sorry, I did not understand '{result.Query}'. Please try again";
await context.PostAsync(message);
context.Wait(this.MessageReceived);
context.Done(true);
}
[LuisIntent("gretting")]
[LuisIntent("intentfr")]
public async Task Greeting(IDialogContext context, IAwaitable<IMessageActivity> activity, LuisResult result)
{
await context.PostAsync("Welcome :) ");
context.Wait(MessageReceived);
context.Done(true);
}
[LuisIntent("test")]
public async Task test(IDialogContext context, IAwaitable<IMessageActivity> activity, LuisResult result)
{
await context.PostAsync("Do you want to test our bot ? We suggest to type : hi or who are you, help etc..");
// context.Done(true);
context.Wait(MessageReceived);
context.Done(true);
}
My problem is that when i choose English ( even French ) i got this error : here was an error sending this message to your bot: HTTP status code InternalServerError
Can you please help me how to start luis dialog ?
P.S if i start from MessagesController.cs directly works good...but my intentions is to let people choose between two languages.
I try to call the luis using : await context.Forward(new EnglishLuis(), ResumeAfterDialog, message, CancellationToken.None); but without result .
new file RootDialog.cs ( updated ) :
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Connector;
using System.Threading;
namespace TeamsBot.Dialogs
{
[Serializable]
public class RootDialog : IDialog<object>
{
private const string EnglishMenu = "English";
private const string FrenchMenu = "French";
private const string QAMenu = "Q&A";
private List<string> mainMenuList = new List<string>() { EnglishMenu,
FrenchMenu, QAMenu };
private string location;
private string originalMessage;
public async Task StartAsync(IDialogContext context)
{
await context.PostAsync("Welcome to Root Dialog");
context.Wait(MessageReceiveAsync);
}
private async Task MessageReceiveAsync(IDialogContext context,
IAwaitable<IMessageActivity> result)
{
var reply = await result;
this.originalMessage = reply.Text;
if (reply.Text.ToLower().Contains("help"))
{
await context.PostAsync("You can implement help menu here");
}
else
{
await ShowMainmenu(context);
}
}
private async Task ShowMainmenu(IDialogContext context)
{
//Show menues
PromptDialog.Choice(context, this.CallDialog, this.mainMenuList,
"What do you want to do?");
}
private async Task CallDialog(IDialogContext context, IAwaitable<string>
result)
{
var selectedMenu = await result;
switch (selectedMenu)
{
case EnglishMenu:
//Call child dialog without data
var newMessage = context.MakeMessage();
newMessage.Text = reply.Text;
await context.Forward(new EnglishLuis(), ResumeAfterDialog, newMessage, CancellationToken.None);
break;
case FrenchMenu:
//Call child dialog with data
// context.Call(new HotelDialog(location), ResumeAfterDialog);
var frenchLuis = new FrenchLuis();
var messageToForward = await result;
// await context.Forward(new FrenchLuis(), ResumeAfterDialog, messageToForward, CancellationToken.None);
break;
case QAMenu:
context.Call(new LuisCallDialog(),ResumeAfterDialog);
break;
}
}
private async Task ResumeAfterDialog(IDialogContext context, IAwaitable<object> result)
{
//Resume this method after child Dialog is done.
var test = await result;
if (test != null)
{
location = test.ToString();
}
else
{
location = null;
}
await this.ShowMainmenu(context);
}
}
}
First, doing
context.Wait(this.MessageReceived);
context.Done(true);
it's wrong. You need to choose: or you wait for a new message in the EnglishDialog or you end the EnglishDialog (with Done)
Then, you are trying to send a string in the context.Forward and you need to forward an IMessageActivity. And I suspect you want to send the original message, so you will need to save that in global variable before continuing with the prompt. Try with:
var newMessage = context.MakeMessage();
newMessage.Text = this.originalMessageText //the variable that contains the text of the original message that you will have to save at MessageReceiveAsync
await context.Forward(new EnglishLuis(), ResumeAfterDialog, newMessage, CancellationToken.None);
MessageReceivedAsync in RootDialog should looks like:
private async Task MessageReceiveAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
var reply = await result;
if (reply.Text.ToLower().Contains("help"))
{
await context.PostAsync("You can implement help menu here");
}
else
{
this.originalMessage = reply.Text;
await ShowMainmenu(context);
}
}
This is how I would have implemented your method for calling the different dialogs. I tend to use dependency injection of dialogs so I don't have to constantly new them up.
private async Task CallDialog(IDialogContext context, IAwaitable<string> result)
{
//These two variables will be exactly the same, you only need one
//var selectedMenu = await result;
var message = await result;
switch (selectedMenu)
{
case EnglishMenu:
// Forward the context to the new LuisDialog to bring it to the top of the stack.
// This will also send your message to it so it gets processed there.
await context.Forward<object>(new EnglishLuis(), ResumeAfterDialog, message , CancellationToken.None);
break;
case FrenchMenu:
await context.Forward<object>(new HotelDialog(location), ResumeAfterDialog, message , CancellationToken.None);
break;
case QAMenu:
await context.Forward<object>(new LuisCallDialog(), ResumeAfterDialog, message , CancellationToken.None);
context.Call(new LuisCallDialog(),ResumeAfterDialog);
break;
}
}
There is an issue in your EnglishLuis dialog where you are using:
context.Wait(this.MessageReceived);
context.Done(true);
The issue is that both of these lines will execute when it passes through the dialog. The context.Done will cause this dialog to leave the stack so you'll end up going to the previous dialog instead, which clashes with the fact you're trying to wait for a response.
There shouldn't really be a context.Done in your luis dialog unless you want to go back to the previous Dialog. So if you're choosing to use context.Done either put it in the resumeAfter method with an appropriate condition or under a single intent for exiting this part of your program.
You didn't include a stack trace, but something that can cause issues when using Luis is if you're using one from a region other than the US. In which case you need to set the properties accordingly where the domain points to the right Luis service.
public EnglishLuis(ConstructorParameters parameters)
: base(new LuisService(new LuisModelAttribute(
"<AppId>",
"<SubscriptionKey>",
domain: "westeurope.api.cognitive.microsoft.com")))
{
// Constructor Stuff...
}

Any Example of WebJob using EventHub?

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

Why does HttpClient PostAsJsonAsync exit Azure Web Job without running code after it?

I have an Azure Web Job built using the Azure SDK whose only job is to call a web service (Web API) and then log a response based on the return value (a class). The problem is that as soon as it calls the HttpClient PostAsJsonAsync method to call the service, it exits out of the web job without executing any of the response handling. My code is:
public class Result
{
// Properties ---------------------------------------------------------
public bool Success { get; set; }
public string Error { get; set; }
}
public class Functions
{
// This function will be triggered based on the schedule you have set for this WebJob
// This function will enqueue a message on an Azure Queue called queue
[NoAutomaticTrigger]
public async static void ManualTrigger(TextWriter log, int value)
{
using (var client = new HttpClient())
{
var rootUrl = ConfigurationManager.AppSettings.Get("WebJobTargetUrl");
client.BaseAddress = new System.Uri(rootUrl);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
Console.WriteLine("Call service");
var response = await client.PostAsJsonAsync("api/Reminder/ProcessDueReminders", new { ItemID = 1 });
Console.WriteLine("After service");
var result = await response.Content.ReadAsAsync<Result>();
Console.WriteLine("After result");
if (result.Success)
Console.WriteLine("Reminders Processed");
else
Console.WriteLine("Reminder process error: " + result.Error);
}
}
}
and the execution logs from the portal are:
I believe it has something to do with the asynchronous operation but I can't figure out a pattern that will work. Any help would be appreciated.
You must define the return value of your own async method as Task instead of void.
On a related note, you should suffix the name of your method with Async. That's not going to solve the problem, but it indicates that you're using the async/await pattern.
There is probably an exception in your PostAsJsonAsync call. Try to put a try catch around it to and log the error:
try {
var response = await client.PostAsJsonAsync("api/Reminder/ProcessDueReminders", new { ItemID = 1 });
} catch (Exception ex){
Console.WriteLine("Exception: "+ ex);
}
Console.WriteLine("After service");

C# Parse how to wait until Query returns a value

I'm trying to retrieve user data from Parse (xamarin.ios using c#). I'm using an async method with await. My challenge is,each time I navigate to the tableView in the app, which should populate the user data in question,the table is always empty.
I would like to wait until the results have been returned before proceeding with the other portion of code.I have tried to use the ContinueWith() function but constantly ran into a build error -
Cannot implicitly convert type 'void' to System.Collections.Generic.IEnumerable<Parse.ParseObject>
My Questions:
Is this the best way to wait for the result?
How do I solve the build error?
Here is my current implementation:
public async void retrieveData(string username)
{
try
{
this.requests.ClearRequests();
refreshed = false;
var query = ParseObject.GetQuery("Requests").WhereEqualTo("username", username);
IEnumerable<ParseObject> results = await query.FindAsync().ContinueWith(t =>{
if(results != null)
{
foreach(ParseObject parseObject in results)
{
UserRequest request = new UserRequest();
request.objectId = parseObject.ObjectId;
request.make = parseObject.Get<string> ("item1");
request.model = parseObject.Get<string> ("item2");
request.year = parseObject.Get<string> ("item3");
request.userName = parseObject.Get<string> ("username");
this.requests.addRequest (request);
}
refreshed = true;
}
});
}
catch(ParseException e) {
Console.WriteLine (e.Message + e.StackTrace);
}
}
You shouldn't need a ContinueWith...that's what the await should handle.
await waits on a Task and then brings back the result with the proper return type. ContinueWith returns a Task, so you would have to grab the Result from the task to make it usable.
For more on this type of thing, you may want to check out Difference between await and ContinueWith
You can try something like this.
public async void retrieveData(string username, )
{
try
{
this.requests.ClearRequests();
refreshed = false;
var query = ParseObject.GetQuery("Requests").WhereEqualTo("username", username);
IEnumerable<ParseObject> results = await query.FindAsync();
if(results != null)
{
foreach(ParseObject parseObject in results)
{
UserRequest request = new UserRequest();
request.objectId = parseObject.ObjectId;
request.make = parseObject.Get<string> ("item1");
request.model = parseObject.Get<string> ("item2");
request.year = parseObject.Get<string> ("item3");
request.userName = parseObject.Get<string> ("username");
this.requests.addRequest (request);
}
refreshed = true;
}
//This is your refresh method for your TableView
this.RefreshTableView();
//or, if in iOS
NSNotificationCenter.DefaultCenter.PostNotificationName("resultsRetrieved", null);
}
catch(ParseException e) {
Console.WriteLine (e.Message + e.StackTrace);
}
}
To show the results in the tableView, I would recommend moving the refreshing of the tableView to a separate method that gets triggered synchronously after the results have been retrieved and parsed. This is shown with the this.RefreshTableView() call above.
If in iOS on Xamarin, another option is to post a notification to the NSNotificationCenter (the Xamarin documentation for which is here). Use the PostNotificationName part seen above instead and then add an observer in the ViewControllers that you want to be dependent on the data. This is done as follows:
Make a notificationToken object:
NSObject notificationToken;
Then in your setup method (you could put this inside of your ViewDidLoad):
void Setup ()
{
notificationToken = NSNotificationCenter.DefaultCenter.AddObserver ("resultsRetrieved", RefreshData);
}
Make your RefeshData method:
void RefreshData (NSString notifString)
{
this.tableView.ReloadData();
}
And then, make sure you dispose of the notification observer when you tear down the class
void Teardown ()
{
NSNotificationCenter.DefaultCenter.RemoveObserver (notificationToken);
}
I had a similar issue so started using callbacks. I'm using them in Xamarin.Android, pretty sure they're available in Xamarin.iOS.
Method that starts the task method - Note I am passing in a method of this class as a parameter
private async void updatedData()
{
await Utils.DataTasks.getNewLiveTips(populateTipsList);
}
Method that calls for data from server
public class DataTasks
{
public static async Task getAllData(Action<IEnumerable<ParseObjects>> callback) {
var query = new ParseQuery<ParseObjects>().OrderByDescending("updatedAt").Limit(5);
IEnumerable<ParseObjects> parseTips = await query.FindAsync();
foreach (var tip in parseTips)
{
// Save data to DB if needed
}
callback(parseTips);
}
Method I passed as parameter in the first instance is now called
private void populateTipsList(IEnumerable<ParseObjects> results)
{
mAdapter = new TipAdapter(this.Activity, results);
mRecyclerView.SetAdapter(mAdapter);
refresher.Refreshing = false;
}

Resources