Azure CosmosDb: Fresh read item and restore by ReplaceItemAsync() says 404 - c#

Simple process: I am getting items from my container and iterate them. Change a property and store it again.
Method to get items
public async Task<ActionItem[]> GetActionsNotExecutedAsync(string transferId)
{
QueryDefinition query = new("SELECT * FROM c WHERE c.TransferId = #transferId AND c.Executed = #executed");
query.WithParameter("#transferId", transferId);
query.WithParameter("#executed", false);
QueryRequestOptions opts = new QueryRequestOptions();
opts.PartitionKey = new PartitionKey(transferId);
opts.MaxItemCount = -1;
List<ActionItem> actions = new();
using FeedIterator<ActionItem> feed = _action.GetItemQueryIterator<ActionItem>(query);
while (feed.HasMoreResults)
{
foreach (ActionItem action in await feed.ReadNextAsync())
{
actions.Add(action);
}
}
return actions.ToArray();
}
Method to update an item
public async Task UpdateActionAsync(ActionItem item)
{
//await _transfer.UpsertItemAsync<ActionItem>(item, new PartitionKey(item.TransferId));
await _transfer.ReplaceItemAsync<ActionItem>(item, item.id);
}
Code that uses both Methods
ActionItem[] actions = await db.GetActionsNotExecutedAsync(evt.TransferId);
foreach (ActionItem action in actions)
{
action.Executed = true;
await db.UpdateActionAsync(action);
}
I expect to see the property "Executed" to be changed to TRUE, but instead I get 404 when using ReplaceItemAsync(). No error but also no change of property when using UpsertItemAsync().

Oh, i am so blind! I was using different containers shameonme
As you can see the first method to read the ActionItems uses a container named "_action" and the second method to update the ActionItem uses a container named "_transfer".

Related

Is there a way to pass a List<T> to a restful API from the Blazor WASM client?

This VS-2022 project is Blazor-WASM CORE using restful api for CRUD SQL-server data storage. I am getting a 404-error from my call from the client-service when trying to pass a "List<PTItem>" variable to the controller. The server-controller is never stopped with a breakpoint.
I want to use this ONE call (passing the list) to the server rather than a number of individual client-calls for each PTItem that needs to be updated using standard CRUD call for a single model-object.
My blazor-page code for this call is:
// Save this list using the client-PTITEM-service.
bool bIsSaveValid = (await _ItemService.UpdateListOfPTItems(listTotalItemsToSave));
Your input to this question is welcome...thanks.
Here is my current code for the restful functions:
// PTItemService.cs -- CLIENT-side.
public async Task UpdateListOfPTItems(List pListForUpdate) {
// Local variables.
IEnumerable<PTItem> oFailedPTItems = Enumerable.Empty<PTItem>(); // the return value.
HttpResponseMessage response = await httpClient.PutAsJsonAsync<List<PTItem>>($"/api/ptitem/updatelist", pListForUpdate);
Console.WriteLine($"response is {response.ToString()}");
return true;
}
// PTItemController -- SERVER-side.
[HttpPut("updatelist:list<PTItem>")]
[ActionName(nameof(UpdateListOfPTItems))]
public async Task<ActionResult<bool>> UpdateListOfPTItems(List<PTItem> pListForUpdate) {
// Local variables.
IEnumerable<PTItem> oFailedPTItems = Enumerable.Empty<PTItem>(); // <== this is the breakpoint.
PTItem? oPTItem = null;
// Update each PTItem in the list passed in.
foreach (var oItem in pListForUpdate) {
var response = await UpdatePTItem(oItem);
if (response == null) {
// Append to the 'oFailedPTItems' list.
oFailedPTItems = oFailedPTItems.Append(oItem);
}
}
// Always return the 'oFailedPTItems' records.
Console.WriteLine($"There were {oFailedPTItems.Count()} failed updates of PTItems.");
foreach (var oItem in oFailedPTItems) {
oItem.DisplayRecordValuesHoriz("Failed Updated PTItem:");
}
return (oFailedPTItems.Count()==0);
}
// PTItemRepository.cs -- SERVER-side.
public Task<bool> UpdateListOfPTItems(List<PTItem> pListForUpdate) {
true;
}

`WaitAll()` or `WhenAll` when expecting data

I've never attempted to use WaitAll() or WhenAll() when running async functionality. After looking at many documentations, SO posts, and tutorials, I haven't found enough information for this, so here I am.
I'm trying to figure out the best/proper way(s) to do the following:
Using EF6, get data as List<Entity>.
Iterate through each Entity and call an external API to perform some action.
External API returns data per Entity which I need to store on the same Entity.
Currently I have built (not tested) the following (without the error handling code):
public IEnumerable<Entity> Process() {
bool hasChanged = false;
var data = _db.Entity.Where(x => !x.IsRegistered);
foreach (var entity in data) {
var result = await CallExternalApi(entity.Id, entity.Name);
entity.RegistrationId = result.RegistrationId;
entity.IsRegistered = true;
_db.Entry(entity).State = EntityState.Modified;
hasChanges = true;
}
if (hasChanges) {
uow.Commit();
}
return data;
}
I feel like I may be able to take advantage of some other functionality/feature in async, but if I can I'm not sure how to implement it here.
Any guidance is really appreciated.
Update
The API I'm calling is the Zoom Api to add Registrants. While they do have an route to batch add Registrants, it does not return the RegistrantId and the Join Url I need.
First, figure out if your external API might have a way to get all the items you want in a batch. If it does, use that instead of sending a whole bunch of requests.
If you need to send a separate request for each item, but want to do it concurrently, you could do this:
public async Task<IReadOnlyCollection<Entity>> Process() {
var data = _db.Entity.Where(x => !x.IsRegistered).ToList();
if(!data.Any()) { return data; }
var entityResultTasks = data
.Select(async entity => new { entity, result = await CallExternalApi(entity.Id, entity.Name) })
.ToList();
var entityResults = await Task.WhenAll(entityResultTasks);
foreach (var entityResult in entityResults) {
var entity = entityResult.entity;
var result = entityResult.result;
entity.RegistrationId = result.RegistrationId;
entity.IsRegistered = true;
_db.Entry(entity).State = EntityState.Modified;
}
uow.Commit();
return data;
}
You will want to watch out for possible concurrency limits on the target source. Consider using Chunk to break your work into batches of acceptable sizes, or leveraging a semaphore or something to throttle the number of calls you're making.

500 server error due to absence of async in controller? Need help removing an object from a list C#

I am trying to filter out all errors except one from a list of errors I send back to the front end. I realize this operation should be an async operation as my request is giving a 500 internal server error. I am new to C# and am having a hard time figuring out how to do so.
My code that gets invoked on the route request looks like:
public async Task<ActionResult> Index(ProfileParams profileParameters)
{
// ...... //
var user = await GenerateUser(Request.RequestContext);
var userState = await _userStateFactory.CreateAsync(user);
var stateTree = new BusinessProfileStateTreeModel
{
Global = await _globalStateFactory.CreateAsync(user),
Header = await _headerStateFactory.CreateAsync(user, null),
User = userState,
Modals = _modalsStateFactory.Create(),
Page = CreatePageState(),
BusinessProfile = _businessProfileReviewsStateFactory.Create(viewModel, customerReviewModel),
Analytics = await _analyticsStateFactory.CreateAsync(user, CreateDtmData(viewModel?.Categories?.PrimaryCategoryName, profileBbbInfo?.BbbName, viewModel), userState)
};
// trying to filter out errors here from the state tree alerts
var errors = filterErrorsAsync(stateTree.BusinessProfile.Display.Alerts.AllAlerts);
var metaData =
GenerateProfileMetaData(customerReviewModel.NumFound, viewModel.ProfileUrl.ToUrlString(), viewModel);
var serverSideModel =
GenerateServerSideModel(
viewModel,
metaData,
profileBbbInfo,
stateTree.Analytics.DtmData,
user);
return await ReduxViewAsync(stateTree.ToList(), serverSideModel);
}
}
The filterErrorsAsync method looks like:
private List<BPAlert> filterErrorsAsync(List<BPAlert> allAlerts)
{
foreach (BPAlert alert in allAlerts)
{
if (alert.AlertTypeId == (int)BusinessReportCustomTextType.CustomerReviews)
{
allAlerts.Clear();
allAlerts.Add(alert);
}
}
return allAlerts;
}
Can someone tell me how to achieve this correctly?
You can't loop a list and modify it at the same time. This is probably what is causing your 500 error.
It looks like you only want filter out certain errors from a list. If you want to keep your method as a loop you can do:
private List<BPAlert> filterErrorsAsync(List<BPAlert> allAlerts)
{
List<BPAlert> temp = new List<BPAlert>(); //copy into new list
foreach (BPAlert alert in allAlerts)
{
if (alert.AlertTypeId == (int)BusinessReportCustomTextType.CustomerReviews)
{
temp.Add(alert);
}
}
return temp;
}
If you want to be a little more modern you can also just use LINQ
private List<BPAlert> filterErrorsAsync(List<BPAlert> allAlerts)
{
return allAlerts.Where(alert => alert.AlertTypeId == (int)BusinessReportCustomTextType.CustomerReviews).ToList();
}
You're attempting to modify a list while enumerating it which won't work. Since you already know which kind of error you want to filter to, you can utilize LINQ's Where method to filter out the other errors, then use Take to get the first one.
private List<BPAlert> filterErrors(List<BPAlert> allAlerts)
=> allAlerts.Where(alert => alert.AlertTypeID == (int)BusinessReportCustomTextType.CustomerReviews)
.Take(1)
.ToList();
There isn't anything asynchronous happening in this method, so no need to mark it async.

Observable | Subscribe to get only the changed objects

I have a list of Objects that I get from Realtime-Database (Firebase) with a wrapper in C# Firebase.Xamarin
private List<Job> jobList = null;
which populates at first the application is loaded using the following code:
private async void PopulateList()
{
IReadOnlyCollection<Firebase.Xamarin.Database.FirebaseObject<Job>> items = await firebase
.Child("jobs")
.OnceAsync<Job>();
jobList = new List<Job>();
foreach (var item in items)
{
jobList.Add(
new Job
{
ID = item.Object.ID,
StartDate = item.Object.StartDate,
EndDate = item.Object.EndDate,
Description = item.Object.Description
});
}
SubscribeDbChanges();
}
I want to subscribe the DB to fire an event and change/add new Objects to the list and fire an event afterwords to show or notify the user for one time, a change has been occurred. For this purpose I am using the Observable i.e Reactive Rx.Net with All
in the following way:
private void SubscribeDbChanges()
{
Observable.All<FirebaseEvent<Job>>(
firebase
.Child("jobs")
.AsObservable<Job>(),
job => !jobList
.Contains(job.Object))
.Subscribe(jobItem =>
{
});
}
Is there any thing wrong with the code? Moreover, where should I create the event that a change has arrived?
First I would recommend getting rid of the foreach/Add, it is the work for Select
private async void PopulateList()
{
jobList = (await firebase
.Child("jobs")
.OnceAsync<Job>())
.Select(item =>
new Job
{
ID = item.Object.ID,
StartDate = item.Object.StartDate,
EndDate = item.Object.EndDate,
Description = item.Object.Description
});
SubscribeDbChanges();
}
Then I would use Where. How you use All is weird, it is an extension method and you call it like a usual static method. It is possible but it is now how it should be used. Here is the code with Where:
private void SubscribeDbChanges()
{
firebase
.Child("jobs")
.AsObservable<Job>()
.Where(job => !jobList.Contains(job.Object))
.Subscribe(jobItem =>
{
});
}
Thanks to #Alexey Zimarev for giving me a quick click, and the flowing code came into appearance:
First Of all took a ConcurrentDictionary that as a data member and initialized the Firebaseclient and than populated the list ;
FirebaseClient firebase;
private ConcurrentDictionary<string, Job> jobList ;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
firebase = new FirebaseClient("https://samplehosting-XXXX.firebaseio.com/");
PopulateList();
}
Inside this function I initialized the dictionary and done the UI work, at last set the listeners respectively to monitor the change in SubscribeToDbChanges
private async void PopulateList()
{
IReadOnlyCollection<Firebase.Xamarin.Database.FirebaseObject<Job>> items = await firebase
.Child("jobs")
.OnceAsync<Job>();
jobList = new ConcurrentDictionary<string, Job>();
foreach (var job in items)
{
while (!jobList.TryAdd(job.Object.ID, job.Object)) ;
}
list = FindViewById<ListView>(Resource.Id.listJobs);
list.ChoiceMode = ChoiceMode.Single;
HomeScreenAdapter ListAdapter = new HomeScreenAdapter(this, jobList);
list.Adapter = ListAdapter;
SubscribeToDbChanges();
}
Here i set the Insertion observer for the keys that are not available in the dictionary and than set the deletion observer for the keys that are available in the dictionary.
private void SubscribeToDbChanges()
{
firebase
.Child("jobs").AsObservable<Job>()
.Where(job => !jobList.ContainsKey(job.Object.ID) && job.EventType == Firebase.Xamarin.Database.Streaming.FirebaseEventType.InsertOrUpdate)
.Subscribe(job =>
{
if (job.EventType == Firebase.Xamarin.Database.Streaming.FirebaseEventType.InsertOrUpdate)
{
while (!jobList.TryAdd(job.Object.ID, job.Object)) ;
}
});
firebase
.Child("jobs").AsObservable<Job>()
.Where(job => jobList.ContainsKey(job.Object.ID) && job.EventType == Firebase.Xamarin.Database.Streaming.FirebaseEventType.Delete)
.Subscribe(job =>
{
Thread remove = new Thread(() =>
{
Job removed = null;
jobList.TryRemove(job.Object.ID, out removed);
});
remove.Start();
});
}
PS: Assumption is here, I assume that the objects that we were adding in the above question and answer were not getting compared because of there memory existence. Like a new object was getting instantiated the might be different from its clone already existing in the list. Please correct me if I am wrong with respect to Linq behavior. However the above given code works in my scenario with dictionary.

Why Does Await Not Appear to Prevent Second Operation on EF Context

Within an ASP.NET MVC Application I'm recieving the following error message for one of my controller methods that uses my Entity Framework context.
A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe.
I'm aware that you cannot run queries in parallel, and everything appears to be awaited properly. If I debug the program and step and inspect some of the data returned from EF then it works, probably because this forces the queries to complete.
EDIT If I place a breakpoint at the null check in the controller method and inspect the data of shipmentDetail the exception is NOT thrown.
Here's a snippit of the code:
Controller Method:
[Route("{id:int}/Deliveries")]
public async Task<ActionResult> DeliveryInfo(int id)
{
var shipmentDetail = await db.ShipmentDetails.SingleOrDefaultAsync(s => s.Id == id);
if (shipmentDetail == null)
return HttpNotFound(string.Format("No shipment detail found with id {0}", id));
var model = await DeliveryInfoModel.CreateModel(db, shipmentDetail);
return View("DeliveryInfo", model);
}
CreateModel Method:
public static async Task<DeliveryInfoModel> CreateModel(Context db, ShipmentDetail shipment)
{
DeliveryInfoModel model = new DeliveryInfoModel()
{
ShipmentInfo = shipment
};
//initialize processing dictionary
Dictionary<int, bool> boxesProcessed = new Dictionary<int, bool>();
List<DeliveryBoxStatus> statuses = new List<DeliveryBoxStatus>();
for (int i = 1; i <= shipment.BoxCount; i++ )
{
boxesProcessed.Add(i, false);
}
//work backwards through process
//check for dispositions from this shipment
if(shipment.Dispositions.Count > 0)
{
foreach (var d in shipment.Dispositions)
{
DeliveryBoxStatus status = new DeliveryBoxStatus()
{
BoxNumber = d.BoxNumber,
LastUpdated = d.Date,
Status = d.Type.GetDescription().ToUpper()
};
statuses.Add(status);
boxesProcessed[d.BoxNumber] = true;
}
}
//return if all boxes have been accounted for
if (boxesProcessed.Count(kv => kv.Value) == shipment.BoxCount)
{
model.BoxStatuses = statuses;
return model;
}
//check for deliveries
if(shipment.Job_Detail.Count > 0)
{
foreach (var j in shipment.Job_Detail.SelectMany(d => d.DeliveryInfos))
{
DeliveryBoxStatus status = new DeliveryBoxStatus()
{
BoxNumber = j.BoxNumber,
LastUpdated = j.Job_Detail.To_Client.GetValueOrDefault(),
Status = "DELIVERED"
};
statuses.Add(status);
boxesProcessed[j.BoxNumber] = true;
}
}
//check for items still in processing & where
foreach (int boxNum in boxesProcessed.Where(kv => !kv.Value).Select(kv => kv.Key))
{
//THIS LINE THROWS THE EXCEPTION
var processInfo = await db.Processes.Where(p => p.Jobs__.Equals(shipment.Job.Job__, StringComparison.InvariantCultureIgnoreCase) && p.Shipment == shipment.ShipmentNum && p.Box == boxNum)
.OrderByDescending(p => p.date)
.FirstOrDefaultAsync();
//process returned data
//...
}
model.BoxStatuses = statuses;
return model;
}
I'm not completely sure if it's because of the query made in the controller, or because of the queries made in the loop that aren't completing causing this behavior. Is there something I'm not understanding about when the queries are actually made/returned due to EF's laziness, or how async/await works in this situation? I have a lot of other methods & controllers that make async EF calls and haven't run into this previously.
EDIT
My context is injected into my controller using Ninject as my IoC container. Here's its config inside of NinjectWebCommon's RegisterServices method:
kernel.Bind<Context>().ToSelf().InRequestScope();
Avoid lazy loading when using async with Entity Framework. Instead, either load the data you need first, or use Include()'s to ensure the data you need is loaded with the query.
https://msdn.microsoft.com/en-gb/magazine/dn802603.aspx
Current State of Async Support
... Async
support was added to Entity Framework (in the EntityFramework NuGet
package) in version 6. You do have to be careful to avoid lazy
loading when working asynchronously, though, because lazy loading is
always performed synchronously. ...
(Emphasis mine)
Also:
https://entityframework.codeplex.com/wikipage?title=Task-based%20Asynchronous%20Pattern%20support%20in%20EF.#ThreadSafety

Categories

Resources