Difficulty calling method from Hang Fire - c#

I have a controller method which is responsible for scheduling appointments. I'm looking to schedule a task to remind the user via SMS (twilio) 5 minutes before their appointment to login to the service.
Here is my method call:
private SMSNotifier sms = new SMSNotifier();
BackgroundJob.Schedule( () => sms.SendPatientUpcomingApptNotificationTest(appointment), appointment.Start.AddMinutes(-5));
and here is my class:
public SMSNotifier()
{
var accountSid = ConfigurationManager.AppSettings["TwilioSid"];
// Your Auth Token from twilio.com/console
var authToken = ConfigurationManager.AppSettings["TwilioToken"]; ;
TwilioClient.Init(accountSid, authToken);
}
public void SendPatientUpcomingApptNotificationTest(Appointment appt)
{
var message = MessageResource.Create(
to: new PhoneNumber("+xxxxxxxxxx"),
from: new PhoneNumber("+xxxxxxxxxx"),
body: string.Format("Hello {0} {1}! You have an upcoming web based video appointment with xxxxxxxxxx. Please login to the website to be seen. Your appointment time is: {2} Thank you - xxxxxxxx", "xxxxxxx", "xxxxxxxx", appt.Start));
}
}
I keep getting this error:
Server Error in '/' Application.
Self referencing loop detected for property 'User' with type 'System.Data.Entity.DynamicProxies.AspNetUser_80E6332CC002F8FCF589159A68E74A0922BEE992586B9FE280D950E149CCC7EB'. Path 'Patient.ActiveSessions[0]'.
But I just don't understand why. I don't reference a User object anywhere.
I should mention I obviously googled that error:
JSON.NET Error Self referencing loop detected for type
Self referencing loop detected - Getting back data from WebApi to the browser
∞
None of this provided any progress. I still have the same error.
What do you guys think?
I definitely think it has to do with the way I'm trying to schedule the 'SendPatientUpcomingApptNotificationTest' method. I can do a Console.WriteLine and it queues the job fine.
IE:
BackgroundJob.Schedule( () => Console.WriteLine("TestSchedule"), appointment.Start.AddMinutes(-5));
Works perfectly

It sounds like it's running into this error when trying to serialize your appointment object.
When you schedule a background job, Hangfire saves info about the method you are calling and its arguments to its job store. If you're passing a complex object as an argument to the method, it attempts to serialize the whole object graph to json.
The recommended approach is to keep your job arguments small and simple. For example, pass an appointmentId to the job instead of the whole appointment:
BackgroundJob.Schedule( () => sms.SendPatientUpcomingApptNotificationTest(appointmentId), ...);
Then you would retrieve the actual appointment within the job.

Related

How to wait for another service to finish a task in the background without awaiting?

I'm creating an ASP.NET Core WebApi, which uses the Autodesk Model Derivative API to convert a Revit file into another file format. After I've uploaded the file, the Autodesk API starts working in the background and can take several minutes to finish its work.
I want to monitor the status of the Autodesk API, to know if the conversion has been finished yet and notify the user. I'm looking for the best way to monitor the status of the job without 'awaiting' and letting the request hang stuck for several minutes.
I've tried just running the task asynchronously without awaiting the result. This worked, up to the point where I wanted to update a value in my Database Context, because that had been disposed due to the request having ended.
I've also researched several options on implementing background services, but haven't found a clear way to do that.
public async Task<ActionResult<Response<JobResponse>>> UploadFile(string bucketKey, IFormFile file)
{
// ....
// File has been uploaded
Response<JobResponse> response
= await NetworkManager.PostAsync<JobResponse>(URI.Job.Url, JsonConvert.SerializeObject(jobData));
// The job has been created in the Autodesk API, so I create a record in my own database
var job = new Job(urn, file.FileName);
context.Jobs.Add(job);
await context.SaveChangesAsync();
// This method is what I want to do in the background
MonitorStatus(job);
return Respond(response);
}
private async Task MonitorStatus(Job job)
{
bool isDone = false;
while (!isDone)
{
isDone = await IsDone(job.Urn);
if (!isDone)
await Task.Delay(10000);
}
string guid = await new JobRepository(job).GetGuid();
// The line underneath throws an error because the context has been disposed
(await context.Jobs.FindAsync(job.Id)).Finish(guid);
await context.SaveChangesAsync();
// ...
// Notify the user
}
Translation of files in Model Derivative API boils down to two main endpoints:
POST job for triggering the translation, and
GET manifest for getting the manifest of the translation (incl. its status while the translation is still running)
If you're making the HTTP requests yourself, you can just poll the manifest until you see that the translation has been completed.
If you're using the Forge .NET SDK, you can trigger the translation using the Translate method, and poll the results using the GetManifest method.
I ended up using a Webhook of Autodesk Forge, which calls an endpoint that notifies the user that the conversion has been completed. This webhook contains a body with information about which job has been completed, so I can update the database accordingly.
This webhook removes the need for my MonitorStatus(job) method.
https://forge.autodesk.com/en/docs/webhooks/v1/tutorials/create-a-hook-model-derivative/

Task is not running asynchronously

I have wrapped the action in Task.Run but it seems that I am missing something very basic. But unable to figure it out.
public void SaveOrderList(List<Order> inputList)
{
Dictionary<string, string> result = new Dictionary<string, string>();
string code = string.Empty;
Task.Run(() =>
{
foreach (var item in inputList)
{
code = CreateSingleOrder(item);
result.Add(item.TicketNumber, code);
}
////TODO: Write logic to send mail
emailSender.SendEmail("abc#xyz.com");
});
}
Since there can be many entries in inputList and each entry may take 5 sec to process, I don't want the UI to be blocked for end user. Instead, I will send a mail and notify how many processed successfully and what all are failed.
To achieve this, best I knew was Task.Run. But, the problem is as soon as function completes, I don't see that the code inside the foreach loop ever worked because it never made to the DB.
Can anyone help me find out what is that I am missing here.
Just for information, this function is called from Web API and Web API POST method is called from javascript. Below is the code for Web API endpoint.
[HttpPost, Route("SaveOrderList")]
[ResponseType(typeof(bool))]
public IHttpActionResult SaveOrderList(List<Order> orderList)
{
orderManagerService.SaveOrderList(orderList)
return this.Ok();
}
Thanks in advance for help.
You need to consider carefully how this works. There are a few suggestions in this article:
https://blog.stephencleary.com/2014/06/fire-and-forget-on-asp-net.html
But I would point out that 'fire and forget' on a web application is usually the wrong approach.
For your example, you really want to consider your UX - if I make an order on your site and then only find out some time later that the order failed (via email, which I may not be checking), I'd not be too impressed. It would be better to await the save result, or make multiple API requests for single order items and show the incremental result of successful orders on your front end.
I'd also suggest a hard look at why your order saving is so slow - this will continue to be problematic for you until it's faster.

How to call, Hang up, put on hold and unhold calls using Twilio?

I have a Twilio number and I understood that in order to do those 4 actions(Call, Hang up, put onhold and unhold calls) I need to create a conference call, but I don't understand how I add my Twilio number to the conference and how do I add another number of a mobile of a client. For example, if my Twilio number is " +9728888888" and the customer's I want to call to mobile number is "+9725555555" – I want code examples of :
1. Calling the customer(from Twilio number " +9728888888" to mobile number "+9725555555")
2. Hold the call
3. UnHold the cold
4. Hangout the call.
I'm using Twilio NuGet on web api project. Can you give me the code examples , considering the numbers I gave(Twilio and mobile) for all of those four scenarios above? I would really appreciate it.
BTW, I saw the code example on their site:
using Twilio.TwiML;
class Example
{
static void Main()
{
var response = new VoiceResponse();
var dial = new Dial();
dial.Conference("moderated-conference-room",
startConferenceOnEnter: false);
response.Dial(dial);
System.Console.WriteLine(response.ToString());
}
}
but it doesn't acknowledge the Twilio or the mobile phone or even the Twilio authentication so I'm not sure how can it work..
Twilio developer evangelist here.
If you want to make a call from your Twilio number to a end user and put both your agent and the user into a conference call then this is the way that I would do it. I am not, however, a C# developer, so while I'll try to give code samples I'm not experienced in .NET web api projects.
You say that you are using the Twilio package from Nuget, that's a good start.
First up, you need to generate a call to your agent and place them in the conference call to wait for the user. To do this, you would use the Twilio REST API to place the call. That code looks a bit like this
const string accountSid = "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
const string authToken = "your_auth_token";
TwilioClient.Init(accountSid, authToken);
var to = new PhoneNumber("AGENT_PHONE_NUMBER");
var from = new PhoneNumber("+9728888888");
var call = CallResource.Create(
to,
from,
url: new Uri("http://example.com/conference")
);
When this call connects with the agent's number Twilio will make a request to the URL that is sent to the API. Your application needs to respond to tell Twilio what to do with the call. In this case, we should put the agent into a <Conference> to wait for the user. Within this action we also need to generate a call to the user.
public IHttpActionResult Conference()
{
// make the call to the user
var to = new PhoneNumber("+9725555555");
var from = new PhoneNumber("+9728888888");
var call = CallResource.Create(
to,
from,
url: new Uri("http://example.com/user_conference")
);
// return the TwiML
var response = new VoiceResponse();
var dial = new Dial();
dial.Conference("Conference", endConferenceOnExit: true);
response.Dial(dial);
Ok(response.ToString());
}
Note: I've set the agent side of this conference to endConferenceOnExit: true. This means that when the agent hangs up, the conference call will end for all participants.
Now, Twilio will make the call to the user and when that connects ask the new URL what to do with the call. This time you just need to respond with the TwiML to connect to the same conference. I'll leave that for you to deal with.
Finally, to put participants on hold you need to use the REST API again. You need to get the conference SID and the SID of the participant you want to put on hold. Since you will be putting the user on hold from your agent, you get both of these SIDs in the second webhook callback to your application.
With the conference and call SID, make a POST request like this:
const string conferenceSid = "CONFERENCE_SID";
const string callSid = "CALL_SID";
ParticipantResource.Update(
conferenceSid,
callSid,
hold: true,
holdUrl: new Uri("http://example.com/hold")
);
You can also provide a hold URL which can provide music while the user waits. For more information, check the participant resource documentation. Unholding the user is the same process, but you set hold to false.
Let me know if this gets you on the way to creating this feature.

Send message to specific channel/routing key with Masstransit/RabbitMQ in C#

I've been working on an application that starts some worker roles based on messaging.
This is the way I want the application to work:
Client sends a request for work (RPC).
One of the worker roles accepts the work, generates a random id, and responds to the RPC with the new id.
The worker will post its debug logs on a log channel with the id.
The client will subscribe to this channel so users can see what's going on.
The RPC is working fine, but I can't seem to figure out how to implement the log-sending.
This is the code that accepts work (simplified)
var bus = Bus.Factory.CreateUsingRabbitMq(sbc =>
{
var host = sbc.Host(new Uri("rabbitmq://xxxxxx.nl"), h =>
{
h.Username("xxx");
h.Password("xxxx");
});
sbc.ReceiveEndpoint(host, "post_work_item", e =>
{
e.Consumer<CreateWorkItemCommand>();
});
sbc.ReceiveEndpoint(host, "list_work_items", e =>
{
e.Consumer<ListWorkItemsCommand>();
});
});
The CreateWorkItemCommand will create the thread, do the work, etc. Now, how would I implement the log-sending with Masstransit? I was thinking something like:
bus.Publish(
obj: WorkUpdate{ Message = "Hello world!" },
channel: $"work/{work_id}"
)
And the client will do something this:
bus.ReceiveFromEvented($"work/{rpc.work_id}").OnMessage += { more_psuedo_code() }
I can't seem to find out how to do this.
Can anyone help me out?
Thanks!
It looks both like a saga and turnout. Current Turnout implementation is monitoring the job itself and I doubt you can really subscribe to that message flow. And it is still not really done.
You might solve this using the saga. Some external trigger (a command) will start the first saga, which will use Request/Response to start the process, which will do the work, and get its correlation id (job id). The long job can publish progress reports using the same correlation id and the saga will consume them, doing what it needs to do.
The "work/{rpc.work_id}" will be then replaced by the correlation.

Facebook Graph API {id}/feed?limit=x - restrict to messages since a certain message id

I have a small problem, I am working on an aggregation application that is collecting messages from pages in realtime.
This is working fine, but I get the same message on every call and then filter out the messages that I have already seen manually.
This means that a large amount of data is being transferred every time I make a call to the graph api.
Is there a way to limit the message as messages since this message id?
currently using the c# Facebook SDK
var fb = new FacebookClient("access_token");
dynamic parameters = new ExpandoObject();
parameters.limit = _facebookMessagesToRetrieveAtATime.ToString(CultureInfo.InvariantCulture);
//Want to add a new param here to say messages since this id.
var facebookUrl = String.Format("{0}/feed", "Page ID");
dynamic resp = fb.Get(facebookUrl, parameters);
Thanks in advance.
You can use the since url parameter in your calls, as described at https://developers.facebook.com/docs/graph-api/using-graph-api/v2.1#paging
This would make it necessary that you store somewhere in your application the timestamp when you last requested the respective feed
This would yield in
var facebookUrl = String.Format("{0}/feed?since={last_update_timestamp}", "Page ID");
where {last_update_timestamp} is the timestamp (unixtime in seconds) of the last update.

Categories