Return code of DeleteAsync -- Xamarin with Azure - c#

I need to know the function / method to retrieve the return code to remove data of Azure DataBase (I use DeleteAsync), so that in case of error or NO-OK show me a screen with error. I need the same with different operations (Insert, update ...)
Next step show my code ...
void delete_click_button(object sender, EventArgs a)
{
if (ID != "")
{
App.AzureService.Delete(ID);
**//If return correct code....{**
DisplayAlert("Warning", "Delete satisfactory", "Back");
Navigation.PopAsync();
**}
//If return code error when delete....
{
//display message
}**
}
Thanks, I hope answer :D

According to your description, I assumed that you are using azure mobile apps as your mobile backend. For C# mobile app backend, the delete action under the TableController would be defined as follows by default:
//DELETE tables/TodoItem/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task DeleteTodoItem(string id)
{
return DeleteAsync(id);
}
For you mobile client, you could invoke the delete operation as follows:
//delete a item and return nothing
await onlineTodoTable.DeleteAsync(new TodoItem() {Id= "bb29f1655fb94897a3074f8e5b91b86d"});
Or
//delete a item and return a JToken
JObject obj = new JObject();
obj.Add("id", "faa9a47e57114988b0395a4c32b7d05d");
var jtoken=await onlineTodoTable.DeleteAsync(obj);
For deleting a item and return custom response, you could change the action as follows:
//DELETE tables/TodoItem/48D68C86-6EA6-4C25-AA33-223FC9A27959
public async Task<string> DeleteTodoItem(string id)
{
await DeleteAsync(id);
return id;
}
As I known, the Insert,Update operations would return the related values after the action executes successfully. And mobile client operations against your mobile backend would throw a exception when the request failed. I recommend that you could just wrap your mobile client operations with try-catch and handle the exception (e.g log the error message or alert the client user) when calling CRUD operations against your mobile app backend instead of retrieving the http status code.

Related

HttpDelete call to API controller is returning a status code of 200 but is not deleting anything

I am using the NetCore 3.1 Entity Framework for a gaming website.
I have the HttpDelete controller below that is giving me odd behavior.
When I test it via the Postman program, the API is sending back a status code of 200(OK).
But it doesn't delete anything from the database.
When I step through it using Visual Studio and breakpoints, I see this message in my Try Catch DbUpdateException block:
Database operation expected to affect 1 row(s) but actually affected 0
row(s). Data may have been modified or deleted since entities were
loaded.
But I am not sure why. I think I am doing everything right, but obviously I am not.
In the controller, it's kind of a 3 step delete process.
Because I have to delete related data first, before deleting the actual game catalog.
// DELETE: api/GameCatalogs/5
[HttpDelete("{id}")]
public async Task<ActionResult<GameCatalog>> DeleteGameCatalog(string id)
{
var gameCatalog = await _context.GameCatalog.FindAsync(id);
Catalog<GameCatalog_MediaCatalog> gameMediaRef;
PlayerCatalog playerRef;
if (gameCatalog == null)
{
return NotFound();
}
// if media exists, delete it all
if (gameCatalog.MediaId != null)
{
gameMediaRef = _context.GameCatalog_MediaCatalog.Where(x => x.MediaId == gameCatalog.MediaId).ToCatalog();
_context.GameCatalog_MediaCatalog.RemoveRange(gameMediaRef);
}
// if a player exists, delete the reference
if (gameCatalog.PlayerId != null)
{
playerRef = _context.PlayerCatalog.Where(x => x.PlayerId == gameCatalog.PlayerId).FirstOrDefault();
_context.PlayerCatalog.Remove(playerRef);
}
// finally we can delete the catalog
_context.GameCatalog.Remove(gameCatalog);
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateException e)
{}
return gameCatalog;
}
Is there something I'm missing or that I need to do?
Thanks!

Nservice message field issue

I have implemented Voice call in my code using .net with NServiceBus version 7.
Below is the code snippet to send voice call:
public Task Handle(AddServiceAuto message, IMessageHandlerContext context)
{
try
{
string VoiceCallCode = null;
Guid userID = User.userID;
VoiceCallCode = GetVoiceCallCode(userID);
if (VoiceCallCode != null)
{
publishAddVoiceCallEvent(context, user.caseID, userID.Mobile,
userID.Voicecall, VoiceMessageText, VoiceCallCode);
}
}
}
private void publishAddVoiceCallEvent(IMessageHandlerContext context,
Guid caseID, string mobile, bool voicecall,
string voiceMessageText, string voiceCallCode)
{
AddVoiceCallEvent addVoiceCallEvent = new AddVoiceCallEvent()
{
CaseID = caseID,
Mobile = mobile,
Voicecall = voicecall,
VoiceMessageText = voiceMessageText,
VoiceCallCode = voiceCallCode
};
context.Publish(addVoiceCallEvent).ConfigureAwait(false);
}
public Task Handle(AddVoiceCallEvent message, IMessageHandlerContext context)
{
try
{
Logger.InfoFormat("message.CaseID: {0}", message.CaseID);
Logger.InfoFormat("message.Voicecall= {0}", message.Voicecall);
Logger.InfoFormat("message.Mobile {0}", message.Mobile);
Logger.InfoFormat("message.VoiceCallCode {0}", message.VoiceCallCode);
// The user should satisfy below conditions in order to receive a voice call.
if ((message.Voicecall) && !string.IsNullOrEmpty(message.Mobile) &&
!string.IsNullOrEmpty(message.VoiceMessageText) &&
!string.IsNullOrEmpty(message.VoiceCallCode))
{
Voicecall(message.Mobile, message.Voicecall,
message.VoiceMessageText, message.VoiceCallCode);
}
else
{
Logger.Error("Mobile Value is Empty (OR) Voicecall is False (OR)
+ VoiceMessageText is Empty (OR) VoiceCallCode is Empty");
}
}
}
If condition satisfied it will send voice call, else it will print log.
Problem:
The Voice call is random i.e. sometimes user is receiving voice call and sometimes not(even though with same settings i.e mobile, VoiceCallCode values stored properly in DB and Voicecall is also true)
and the Strange part is, though the values are stored correctly DB, when we look into the logs that we are printing, it shows the value of Mobile, VoiceCallCode is null and Voicecall is false.
Again after 5 mins I tried, it worked.
One more thing is, when voice call is not working.
Logger.InfoFormat("message.CaseID: {0}", message.CaseID); // CaseID printed
For Below, data is not printing even though data is there in available in DB (i.e. printing as null)
Logger.InfoFormat("message.Voicecall= {0}", message.Voicecall);
Logger.InfoFormat("message.Mobile {0}", message.Mobile);
Logger.InfoFormat("message.VoiceCallCode {0}", message.VoiceCallCode);
Strange is that, for CaseID it printed while for others it is not printing.
Why this is happening? Can someone please help on this?
The code you've shared doesn't seem to be a running code (try w/o catch) therefore it would be hard to pinpoint what contributes to the issue. But the random behaviour could be attributed to improper use of async APIs. The handler methods should return a Task or use async/await. So are operations invoked on IMessageHandlerContext.
For example, publishAddVoiceCallEvent should be returning a Task and not void. The code inside it (context.Publish(addVoiceCallEvent).ConfigureAwait(false);) should be either return context.Publish(addVoiceCallEvent); or await context.Publish(addVoiceCallEvent).ConfigureAwait(false);.
NServiceBus comes with a Rozlyn analyzer to help with these issues.

MassTransit - Wait for all activities to complete and then continue processing

If I have to much activities, does it cause blocking resources or request time out?
Here is my scenario:
I have an api controller which sends an Order request to consumer; I use Request/Response patern to recieve ErrorMessage property from consumer and base on that property response back, if it's null I would want to return OK() otherwise, return BadRequest or Ok but with a message like: Product out of stock to notify to the client.
In my consumer, I have build a routing slip which have 2 activities:
CreateOrderActivity: Which creates an order with order details.
ReserveProductActivity: Which reduces the quantity of product in stock, if product quantity < 0 I'll publish a message with an ErrorMessage back to the consumer and compensate the previous activity.
public async Task Consume(ConsumeContext<ProcessOrder> context)
{
try
{
if (!string.IsNullOrEmpty(context.Message.ErrorMessage))
{
await context.RespondAsync<OrderSubmitted>(new
{
context.Message.OrderId,
context.Message.ErrorMessage
});
return;
}
RoutingSlipBuilder builder = new RoutingSlipBuilder(context.Message.OrderId);
// get configs
var settings = new Settings(_configuration);
// Add activities
builder.AddActivity(settings.CreateOrderActivityName, settings.CreateOrderExecuteAddress);
builder.SetVariables(new { context.Message.OrderId, context.Message.Address, context.Message.CreatedDate, context.Message.OrderDetails });
builder.AddActivity(settings.ReserveProductActivityName, settings.ReserveProductExecuteAddress);
builder.SetVariables(new { context.Message.OrderDetails });
await context.Execute(builder.Build());
await context.RespondAsync<OrderSubmitted>(new
{
context.Message.OrderId
});
}
catch (Exception ex)
{
_log.LogError("Can not create Order {OrderId}", context.Message.OrderId);
throw new Exception(ex.Message);
}
}
Code for ReserveProductActivity:
public async Task<ExecutionResult> Execute(ExecuteContext<ReserveProductArguments> context)
{
var orderDetails = context.Arguments.OrderDetails;
foreach (var orderDetail in orderDetails)
{
var product = await _productRepository.GetByProductId(orderDetail.ProductId);
if (product == null) continue;
var quantity = product.SetQuantity(product.QuantityInStock - orderDetail.Quantity);
if (quantity < 0)
{
var errorMessage = "Out of stock.";
await context.Publish<ProcessOrder>(new
{
ErrorMessage = errorMessage
});
throw new RoutingSlipException(errorMessage);
}
await _productRepository.Update(product);
}
return context.Completed(new Log(orderDetails.Select(x => x.ProductId).ToList()));
}
This line of code in a consumer method await context.Execute(builder.Build())
At first I thought it would build the routing slip and execute all activities first before going to the next line but it's not. Instead it's immediately going to the next line of code (which responses back to controller) and then after execute activities, which is not what I want. I need to check the quantity of product in 2nd activity first and base on that return back to the controller.
(In current, it always responses back to controller first - the line after buider.Buid(), and then if quantity < 0 it still goes to the very first if condition of the consume method but since it already responses, I cannot trigger response inside that if statement again).
So in short, if product is still available in 2nd activity I can send the reponse back like normal (which executes the code after context.Execute(builder.Build()), but if quantity < 0 - which I publish back to the consumer method with ErrorMessage, I would like it to jump to the very first if condition of Consume method (if(!string.IsNullOrEmpty(context.Message.ErrorMessage)) ...) and base on the ErrorMessage notify the client.
Is there something wrong with this approach? How can I achieve something like this?
Thanks
It isn't documented, but it is possible to use a proxy to execute a routing slip, and response to the request with the result of the routing slip. You can see the details in the unit tests:
https://github.com/MassTransit/MassTransit/blob/master/tests/MassTransit.Tests/Courier/RequestRoutingSlip_Specs.cs#L20
You could create the proxy, which builds the routing slip and executes it, and the response proxy - both of which are then configured on a receive endpoint as .Instance consumers.
class RequestProxy :
RoutingSlipRequestProxy<Request>
{
protected override void BuildRoutingSlip(RoutingSlipBuilder builder, ConsumeContext<Request> request)
{
// get configs
var settings = new Settings(_configuration);
// Add activities
builder.AddActivity(settings.CreateOrderActivityName, settings.CreateOrderExecuteAddress);
builder.SetVariables(new { context.Message.OrderId, context.Message.Address, context.Message.CreatedDate, context.Message.OrderDetails });
builder.AddActivity(settings.ReserveProductActivityName, settings.ReserveProductExecuteAddress);
builder.SetVariables(new { context.Message.OrderDetails });
}
}
class ResponseProxy :
RoutingSlipResponseProxy<Request, Response>
{
protected override Response CreateResponseMessage(ConsumeContext<RoutingSlipCompleted> context, Request request)
{
return new Response();
}
}
You could then call it from the consumer, or put the ordering logic in the proxy - whichever makes sense, and then use the request client from your controller to send the request and await the response.

Best way to update data after an API call gets a record

I am currently working on an API where a record should only be allowed to be pulled once. It's basically a queue where once a client pulls the record, the Retrieved field on the record is marked true. The Get calls only pull records where the Retrieved field is false.
Controller:
[HttpGet]
public virtual IActionResult GetAll([FromQuery] int? limit)
{
try
{
return Ok(_repository.Get(limit));
}
catch
{
return new StatusCodeResult(StatusCodes.Status500InternalServerError);
}
}
Repository:
public IQueryable<Report> Get(int? limit)
{
IQueryable<Report> reports;
if (limit == null)
{
reports = _context.Reports.Where(r => r.Retrieved == false);
}
else
{
reports = _context.Reports.Where(r => r.Retrieved == false).Take((int)limit);
}
return reports;
}
What would be the best way to modify the records that have been pulled by the Get call? If I do the modification before returning results from the repository code, then when the controller actually converts the IQueryable to real data, the field has changed and it won't pull any results, but the Controller seems like the wrong place to be doing this sort of modification to the database.
I would split this functionality away from the retrieval. Let the caller/client indicate that the report has been successfully retrieved and read with a second call. It is a little more overhead but it adds resilience. Example: if there is a failure in the retrieval after the server call (maybe in the network on browser or client app) then the client has another opportunity to retrieve the data.
Controller:
[HttpPut]
public virtual async Task<IActionResult> MarkAsRetrieved(IEnumerable<int> reportIds, CancellationToken token)
{
await _repository.MarkRetrievedAsync(reportIds, token).ConfigureAwait(true);
return Ok();
}
Repository:
public Task MarkRetrievedAsync([FromBody]IEnumerable<int> reportIds, CancellationToken token)
{
foreach (Report report in reportIds.Select(x => new Report{ReportId = x, Retrieved = false}))
{
_context.Reports.Attach(report);
report.Retrieved = true;
}
return _context.SaveChangesAsync(token);
}
Notes
It is only necessary to send over the identifier for a Report instance. You can then attach an empty instance with that same identifier and update the Retrieved property to true, just that will be sent in the corresponding store update statement.
I would changed the Retrieved bit in the database to a handle of some kind-- Guid or record id to another table that records the fetch, or some other unique value. Then I would determine the handle, update the records I am about to fetch with that that handle, then retrieve the records that match that handle. At any point if you fail, you can set the retrieved handle back to NULL for the handle value you started.

Trouble inserting into Azure db from Windows Store app

I'm currently working on a Windows Store app (for a school assignment), and I'm having trouble inserting data into my database which is stored in Azure. Whenever I attempt to insert data into the db, the MobileServiceInvalidOperationException gets thrown. My code is as follows:
In my model class
class Division
{
public string Id {get; set;}
[JsonProperty(PropertyName = "divisionTitle")]
public string DivisionTitle {get; set;}
}
And the relevant code in my MainPage.xaml.cs file
private MobileServiceCollection<Division, Division> divisionItems;
private IMobileServiceTable<Division> divisionTable = App.MobileService.GetTable<Division>();
private async void InsertDivision(Division divisionItem)
{
// This code inserts a new division Item into the database.
// When the operation completes and Mobile Services has
// assigned an Id, the item is added to the collection
try
{
await divisionTable.InsertAsync(divisionItem);
divisionItems.Add(divisionItem);
}
/////////////////////////////////////////////////////////
// The MessageDialog that pops up when this exception //
// gets thrown is: //
// //
// Internal Server Error (HTTP 500) //
////////////////////////////////////////////////////////
catch (MobileServiceInvalidOperationException e)
{
MessageDialog errormsg = new MessageDialog(e.Message,
string.Format("{0} (HTTP {1})",
e.Response.ReasonPhrase,
(int)e.Response.StatusCode));
var ignoreAsyncOpResult = errormsg.ShowAsync();
}
}
private void DivisionButtonSave_Click(object sender, RoutedEventArgs e)
{
var DivisionItem = new Division
{
DivisionTitle = DivisionInput.Text
};
InsertDivision(DivisionItem);
}
I also added a script in the management portal:
function insert(item, user, request) {
if (item.DivisionTitle.length > 15) {
request.respond(statusCodes.BAD_REQUEST, 'Division title must be under 15 characters');
}
else {
request.execute();
}
}
Before making the changes above, I was having no trouble communicating with Azure from within the app and wasn't having any problems inserting data. It's only after editing the script in Azure (the default insert method is simply the request.execute() statement), and since I added the InsertDivision method (I was previously entering data into the db directly from the event handler with the command await App.MobileService.GetTable<Division>().InsertAsync(DivisionItem);) that this problem has started to occur. I've tried a couple of different things and nothing has worked. After looking at my code does anything stick out? Thanks in advance to anyone who can help.
In the request sent to the service, the property DivisionTitle is sent with the first letter in lower case (since you defined it as such with the JsonProperty attribute):
{"divisionTitle":"the actual title"}
On your script, you're trying to access the property item.DivisionTitle (which doesn't exist, JavaScript is case-sensitive), and then access a property (length) of this undefined value. That will cause an error in your script. If you either change the script to use the actual JSON name (item.divisionTitle.length > 15) or change the JsonProperty declaration in the client to send the property with the first letter in upper case, it should work.
By the way, if you go to the "logs" tab in the portal, you should see some error which explains why you're getting the internal server error.

Categories