So I'm currently building a Web Api with .NET, and using async calls with entity framework.
On my PUT endpoint for a controller, I'm trying to get whether the user already belongs to another level, or if he's in the DB at all, here's the controller code:
[HttpPut]
public async Task<IHttpActionResult> PutCommittee(CommitteeViewModel committee)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (!User.IsInRole("Dean") && !User.IsInRole("Chair"))
return Unauthorized();
var user = await db.Users.Where(u => u.Cn == User.Identity.Name).FirstOrDefaultAsync();
if (user == null) { return BadRequest("Your user does not exist"); }
if (User.IsInRole("Dean"))
{
var college = await db.Colleges.Where(c => c.Dean.Cn == user.Cn).FirstOrDefaultAsync();
if (college == null) { return BadRequest("You're not a Dean of any college"); }
committee.Name = _utils.getCollegeCommitteeName(college);
}
else
{
var department = await db.Departments.Where(d => d.Chair.Cn == user.Cn).FirstOrDefaultAsync();
if (department == null) { return BadRequest("You're not a Chair of any college"); }
committee.Name = _utils.getDepartmentCommitteeName(department);
}
var model = await db.Commitees.Where(c => c.Name == committee.Name).FirstOrDefaultAsync();
if (model == null)
return BadRequest("You have no committee");
var tuple = await getUsers(committee);
model.Users = tuple.Item1;
if (model.Users == null)
return BadRequest(tuple.Item2);
db.Entry(model).State = EntityState.Modified;
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
throw;
}
return StatusCode(HttpStatusCode.NoContent);
}
And Here's the function that checks for the users:
private async Task<Tuple<List<User>, string>> getUsers(CommitteeViewModel committee)
{
string error = "";
List<User> users = new List<User>();
var tuple = new Tuple<List<User>, string>(users, error);
var role = await db.Roles.Where(r => r.Name == "Committee").FirstOrDefaultAsync();
foreach (UserViewModel u in committee.Users)
{
var user = await db.Users.Where(us => us.Cn == u.Cn).FirstOrDefaultAsync();
if (user != null)
{
if (user.Role.Name == "Chair" || user.Role.Name == "Dean")
{
error = "User " + user.Name + " is a " + user.Role.Name + " and cannot be member of a review committee";
return tuple;
}
users.Add(user);
}
else
{
user = _loginProvider.generateUser(u.Cn, role);
db.Users.Add(user);
await db.SaveChangesAsync();
users.Add(user);
}
}
return tuple;
}
I'm using a tuple since async method don't support OUT parameters, in case there's an error.
So the problem is that when I delete a user in my front-end (and then send a put request with the updated array), and I debug step by step, it does delete it, but when I don't, if I put a breakpoint at the try block, the variable model.Users contains the previous array (the original from the model), and this only happens when I delete a user from an array, and the weird thing is that it also happened when I wrote the code synchronously
Thank you for your help.
Just remember... Async methods running back server, so if you have proccess that need run first you need to replace async method for a simple one. Trying it and I´m sure you can solve it.
Related
public async Task<IActionResult> LeaveForm([Bind("ReqNo,ReqId,ActionTp,EmpNo,Remark,ActionDt,Ndays,ReqDt,Status,CreatedTm,CreatedBy,DeptId,ActionTaken,ActionRemark")] Request requests, Noti noti)
{
if (ModelState.IsValid)
{
try
{
var empno = _context.Users.Where(x => x.UserName == User.Identity.Name).Select(m => m.EmpNo).Single();
requests.ReqId = Convert.ToByte(1);
requests.ActionTp = 1;
requests.Status = "Pending";
requests.CreatedTm = DateTime.Now;
_context.Add(requests);
// Notification
var admin = _context.Users.Where(x => x.UserTp == 1).Select(x => x.EmpNo).ToListAsync();
noti.CreatedDate = DateTime.Now;
noti.FromUserId = empno;
noti.ToUserId = admin;
noti.NotiHeader = "Requested";
noti.NotiBody = "LeaveForm";
noti.IsRead = false;
noti.Url = "../HandleRequest/index";
_context.Add(noti);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
catch (Exception)
{
Notify("Something went wrong", notificationType: NotificationType.error);
return RedirectToAction("index", "Home");
throw;
}
}
Notify("Something went wrong", notificationType: NotificationType.error);
return View(requests);
}
here var admin is a list of items
And I need to add that list of items to which I had mentioned
Here my need is to sent the same request to different users in the list at a time
Hi I have Controller method as below
[HttpPost]
public JsonResult Post(string vehiclesString, string Entity, int EntityId, ApplicationUser CurrentUser)
{
//https://stackify.com/understanding-asp-net-performance-for-reading-incoming-data/
List<Vehicle> vehicles = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Vehicle>>(vehiclesString);
InputFieldController c = new InputFieldController();
var errors = new List<string>();
try
{
//let's get model for each of the input field
InputFieldController icController = new InputFieldController();
List<InputField> fields = icController.GetNamesValues("VehicleField", -1, "Vehicle", 0);
foreach (Vehicle vehicle in vehicles)
{
//convert array of strings into array of input fields
if (fields.Count != vehicle.ValueStrings.Count)
{
throw new Exception("Vehicle columns mismatch. Expected "
+ fields.Count + " fields, but received " + vehicle.ValueStrings.Count);
}
for (int i = 0; i < fields.Count; i++)
{
InputField field = fields[i];
string cell = vehicle.ValueStrings[i];
if ((cell != null || cell != String.Empty) && (field.Type == "radio" || field.Type == "dropdown"))
{
var f = field.InputDropdowns.Where(x => x.Name == cell).FirstOrDefault();
if (f != null)
{
field.InputValue.InputDropdownId = f.InputDropdownId;
}
else
field.InputValue.InputDropdownId = null;
}
else
{
field.InputValue.Value = cell;
}
vehicle.Values.Add(field);
}
vehicle.Blob = Newtonsoft.Json.JsonConvert.SerializeObject(vehicle.Values);
Vehicle v = new Vehicle();
if (vehicle.VehicleId == 0)
{
v = this.DomainLogicUnitOfWork.VehicleManager.Create(vehicle, Entity, EntityId);
}
}
JsonResult data = Json(new
{
success = true,
});
List<Vehicle> vehiclesList = this.DomainLogicUnitOfWork.VehicleManager.List(Entity, EntityId);
if (vehiclesList != null)
foreach (Vehicle v in vehiclesList)
{
if ((v != null) && (v.Blob != null))
v.Values = Newtonsoft.Json.JsonConvert.DeserializeObject<List<InputField>>(v.Blob);
}
//Task task = Task.Run(async () => await this.DomainLogicUnitOfWork.VehicleInfoManager.CreateOrUpdate(Entity, EntityId));
/*
* Here I have to call the this.DomainLogicUnitOfWork.VehicleInfoManager.CreateOrUpdate(string Entity, int EntityId) asynchronously
* but return the data without waiting for the CreateOrUpdate to complete
*/
System.Web.Hosting.HostingEnvironment.QueueBackgroundWorkItem(async cancellationToken =>
{
await this.DomainLogicUnitOfWork.VehicleInfoManager.CreateOrUpdate(vehiclesList, Entity, EntityId);
});
return data;
}
catch (Exception ex)
{
LogHandler.LogError(9000, "Error updating input fields", ex);
errors.Add("Error 9000:" + ex.Message);
return Json(new
{
error = ex.Message
});
}
}
And I have CreateOrUpdate method defined as below in VehicleInfoManager class
public async Task CreateOrUpdate(string Entity, int EntityId)
{
//do some stuff
var task = Task.Run(() => Test(Entity, EntityId));
//do other stuff
await task;
//some more stuff
}
And Test method is as follows
private void Test(string Entity, int EntityId)
{
List<VehicleInfo> addList; List<VehicleInfo> updateList;
try
{
this.GetAddAndUpdateList(Entity, EntityId, out addList, out updateList);
if ((addList != null) && (addList.Count > 0))
using (var cont = this.UnitOfWork.Context)
{
foreach (var a in addList)
{
cont.VehicleInfos.Add(a);
}
cont.SaveChanges();
}
if ((updateList != null) && (updateList.Count > 0))
using (var cont = this.UnitOfWork.Context)
{
foreach (var a in updateList)
{
var aa = cont.VehicleInfos?.Where(x => x.VehicleInfoId == a.VehicleInfoId)?.FirstOrDefault();
aa.Address_City = a.Address_City;
aa.Address_Country = a.Address_Country;
aa.Address_StateCode = a.Address_StateCode;
aa.Address_Street1 = a.Address_Street1;
aa.Address_Street2 = a.Address_Street2;
aa.Address_Zip = a.Address_Zip;
aa.ChassisYear = a.ChassisYear;
aa.EngineFamilyName = a.EngineFamilyName;
aa.Entity = a.Entity;
aa.EntityId = a.EntityId;
aa.InputFieldEntity = a.InputFieldEntity;
aa.InputFieldEntityId = a.InputFieldEntityId;
aa.InputFieldGroup = a.InputFieldGroup;
aa.LicensePlate = a.LicensePlate;
aa.Manufacturer = a.Manufacturer;
aa.ModelYear = a.ModelYear;
aa.PurchasedDate = a.PurchasedDate;
aa.RegHoldClearBy = a.RegHoldClearBy;
aa.RegHoldClearDate = a.RegHoldClearDate;
aa.RegHoldComment = a.RegHoldComment;
aa.RegHoldSet = a.RegHoldSet;
aa.RegHoldSetBy = a.RegHoldSetBy;
aa.RegHoldSetDate = a.RegHoldSetDate;
aa.TrailerPlate = a.TrailerPlate;
aa.UpdatedBy = a.UpdatedBy;
aa.UpdatedDate = a.UpdatedDate;
aa.VehicleId = a.VehicleId;
aa.VehicleOperator = a.VehicleOperator;
aa.VehicleOwner = a.VehicleOwner;
aa.VIN = a.VIN;
}
cont.SaveChanges();
}
}
catch (Exception ex)
{
ARB.Logging.LogHandler.LogError(9001, "CreateOrUpdate(string Entity, int EntityId) in class VehicleInfoManager", ex);
throw ex;
}
}
What I want is, I want two things here
the Post method to call or start the CreateOrUpdate method as background call but instead of waiting until the CreateOrUpdate method finishes, it should return the result data to UI and continue the big task CreateOrUpdate in the background.
Is there anyway to start the background method CreateOrUpdate after sometime like (10 mins etc) post method returns to UI, if it can't be done, its OK we don't have to worry but just asking if there is anyway to trigger this from within the same application
When I implemented it in the above way, even after using System.Web.Hosting.HostingEnvironment.QueueBackgroundWorkItem I am still getting the null http context at the following location
user = System.Web.HttpContext.Current.User.Identity.Name; url = System.Web.HttpContext.Current.Request.Url.ToString();
System.Web.HttpContext.Current is coming out as null.
and the application is breaking,
I chaned my async call to the following to use HostingEnvironment.QueueBackgroundWorkItem, still the same htt current context is coming out as null, any help please
System.Web.Hosting.HostingEnvironment.QueueBackgroundWorkItem(async cancellationToken =>
{
await this.DomainLogicUnitOfWork.VehicleInfoManager.CreateOrUpdate(Entity, EntityId);
});
Thank you
System.Web.HttpContext.Current is maintained by the ASP.NET's SynchronizationContext.
When you start a new Task, the code will be executing on another thread pool thread without a SynchronizationContext and the value of System.Web.HttpContext.Current is not safe to use, whatever its value.
When the execution of the action method (Post) ends, the request will end and the HttpContext instance will be invalid, even if you mange to get a reference to it.
Also, there is no guarantee that that code you posted to the thread pool will run to complete, since it's out of ASP.NET control and ASP.NET won't be aware of it if IIS decides, for some reason, to recycle the application pool or the web application.
If you to post background work, use HostingEnvironment.QueueBackgroundWorkItem. Beware if its constraints.
I am using EF Core in an ASP.NET MVC web app.
It is an online restaurant. I want to let the user to change the quantity of a cart item (only adding more in this point) and by that to update his cartItemPrice and also his cartTotalPrice (a cart can contain few cart items and every cart item can contain few dishes).
After testing the code, it is working fine, only if I call AddOne method one at the time, the cartItemPrice and the CartTotalPrice are both update as should be.
The problem starts when I call the method many times (by clicking fast.. double clicking for instance causing the problem).
The problem causes the cartItemPrice to change sometimes, and sometimes not while the CartTotalPrice changes always.
I used this tag for calling the AddOne method from the View/Carts :
<div class="quantity">
<a asp-action="AddOne" asp-controller="Carts" asp-route-id="#ci.Id" class="add quantity-btn col-3">+</a>
</div>
I tried to use async and await and also without them and both ways gave the same bad result.
The functions from the Controllers/CartsController:
async/await:
public async Task AddToTotalPrice(double price)
{
var user = GetCurrentUserName();
var cart = await _context.Cart.FirstOrDefaultAsync(s => s.UserName == user);
cart.CartTotalPrice += price;
await _context.SaveChangesAsync();
}
public async Task AddOne(int id)
{
var cartitem = await _context.CartItem.Include(d => d.Dish).FirstOrDefaultAsync(s => s.Id == id);
var u = GetCurrentUserName();
var c = _context.Cart.FirstOrDefault(p => p.UserName == u);
if (cartitem != null)
{
if (cartitem.CartId != c.Id)
{
return;
}
cartitem.Quantity += 1;
cartitem.cartItemPrice = cartitem.Dish.Price * cartitem.Quantity;
await AddToTotalPrice(cartitem.Dish.Price);
await _context.SaveChangesAsync();
}
}
Without async/await:
public void AddToTotalPrice(double price)
{
var user = GetCurrentUserName();
var cart = _context.Cart.FirstOrDefault(s => s.UserName == user);
cart.cartTotalPrice += price;
_context.SaveChanges();
}
public void AddOne(int id)
{
var cartitem = _context.CartItem.Include(d => d.Dish).FirstOrDefault(s => s.Id == id);
var u = GetCurrentUserName();
var c = _context.Cart.FirstOrDefault(p => p.UserName == u);
if (cartitem != null)
{
if (cartitem.CartId != c.Id)
{
return;
}
cartitem.Quantity += 1;
cartitem.cartItemPrice = cartitem.Dish.Price * cartitem.Quantity;
AddToTotalPrice(cartitem.Dish.Price);
_context.SaveChanges();
}
}
What is wrong here and if it possible to fix?
Thanks.
While server processes one request another request is doing the same thing and you have data race.
You can solve this by two ways:
Transaction
public async Task AddToTotalPrice(double price)
{
var user = GetCurrentUserName();
var cart = await _context.Cart.FirstOrDefaultAsync(s => s.UserName == user);
cart.CartTotalPrice += price;
}
public async Task AddOne(int id)
{
using var tran = _context.Database.BeginTransactionAsync();
var cartitem = await _context.CartItem.Include(d => d.Dish).FirstOrDefaultAsync(s => s.Id == id);
var u = GetCurrentUserName();
var c = _context.Cart.FirstOrDefault(p => p.UserName == u);
if (cartitem != null)
{
if (cartitem.CartId != c.Id)
{
return;
}
cartitem.Quantity += 1;
cartitem.cartItemPrice = cartitem.Dish.Price * cartitem.Quantity;
await AddToTotalPrice(cartitem.Dish.Price);
await _context.SaveChangesAsync();
}
await tran.CommitAsync();
}
Concurrency Token
Add to Cart.CartTotalPrice attribute [ConcurrencyCheck] as specified in documentation.
And catch DbUpdateConcurrencyException and restart updating price again. Handling Concurrency Conflicts
I am trying to delete over 5000 records from the database using .netCore 2.1. I have the following method which works fine however it takes way too long.
public async Task<bool> deleteAdhocDetails(int[] id)
{
var status = false;
for (var x = 0; x < id.Length; x++)
{
var existingReward = await _context.AdhocRewardInfo
.Where(d => d.RowID == id[x])
.FirstOrDefaultAsync<AdhocRewardInfo>();
if ((existingReward != null) && (existingReward.HaloRewardCode != null))
{
try
{
//removing existingReward
_context.AdhocRewardInfo.Remove(existingReward);
await _context.SaveChangesAsync();
status = true;
}
catch (Exception e)
{
throw e;
}
}
}
return status;
}
I am currently using EFCore.BulkExtensions for inserting records and it works nicely. I tried using BulkDelete but it didn't seem to make a difference. I also tried to use Z.EntityFramework.Extensions.EFCore but couldn't get that to work too. I should also mention that I am kinda new to this. If someone could please point me in the right direction I would appreciate it. Thanks
In the adhoc.service.ts:
deleteAdhocRecipients(id: number[]): Promise<boolean> {
return this.http.put<boolean>(this.baseUrl + 'deleteAdhocDetails', id)
.toPromise();
}
and in the .ts file:
this.general
.load(this.aService.deleteAdhocCampaign(this.adhocForm.get('create.id').value))
.then(
y => {
if (deleteArr.length > 0) {
this.general.load(this.aService.deleteAdhocRecipients(deleteArr))
.then(
f => this.router.navigate(['/adhoc-campaign/lookup'])
);
} else {
this.router.navigate(['/adhoc-campaign/lookup']);
}
}
);
I optimized your c# code
public async Task<bool> deleteAdhocDetails(int[] id){
try{
var deletedRecords= await _context.AdhocRewardInfo.Where(d =>
id.Contains(d.RowID))
foreach (var item in deletedRecords)
{
_context.AdhocRewardInfo.Remove(item );
}
await _context.SaveChangesAsync();
return true;
}catch (Exception e){
return false;
}
}
You can remove select of object and then do only one saveChanges
public async Task<bool> deleteAdhocDetails((int id, object haloRewardCode)[] id)
{
try
{
for (var x = 0; x < id.Length; x++)
{
var existingReward = new AdhocRewardInfo() { id = id[x].id, HaloRewardCode = id[x].haloRewardCode };
if ((existingReward != null) && (existingReward.HaloRewardCode != null))
{
//removing existingReward
_context.AdhocRewardInfo.Remove(existingReward);
}
}
}
catch (Exception e)
{
throw;
}
await _context.SaveChangesAsync();
return true;
}
I would suggest to use linq2db.EntityFrameworkCore (disclaimer: I'm one of the creators).
And Delete will be fast as possible:
var deletedCount = await _context.AdhocRewardInfo
.Where(d => id.Contains(d.RowID))
.DeleteAsync();
I think the fastest way will be using sql query string
public async Task<bool> deleteAdhocDetails(int[] id)
{
var ids= string.Join(",",id);
var rows= await _context.Database.ExecuteSqlRawAsync(
"DELETE FROM AdhocRewardInfo WHERE Id IN ("+ ids + ")");
return rows>0;
}
I have two dialogs called from root dialog based on the prompt response.
Both dialogs internally prepare request and call a service class. Weird thing is one dialog resumes with response from service but the other though receives response from service is not resuming.
Below method works fine and resumes the call at if (searchResult != null && searchResult.Item1 != null)
public virtual async Task MessageRecievedAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
var message = await result;
if (message.Text == "quit")
{
context.Done<object>(null);
}
else
{
try
{
var srm = new CoveoRestSearchService.CoveoSearchRequestModel
{
Query = message.Text,
EnableDidYouMean = true,
QuerySource = CoveoRestSearchService.Constants.SWEATERSSOURCE
};
var searchResult = await csp.PerformSearch(srm);
if (searchResult != null && searchResult.Item1 != null)
{
await CardUtil.showHeroCard(message, searchResult.Item1);
}
else
{
await context.PostAsync($"No search results for {message.Text} found");
}
await PostSearchUsageAnalyticsToCoveo(context, srm, searchResult.Item2);
}
catch (Exception e)
{
Debug.WriteLine($"Error when searching : {e.Message}");
}
finally {
context.Wait(MessageRecievedAsync);
}
}
}
This below one though looks identical is not resuming the if (response != null)
public virtual async Task MessageRecievedAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
var message = await result;
if (message.Text == "quit")
{
context.Done<object>(null);
}
else
{
try
{
var currentDate = DateTime.UtcNow;
string toDate = currentDate.ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
string fromDate = currentDate.AddDays(-7).ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
MetricsQuery = MetricsQuery.Replace("{fromISODate}", fromDate);
MetricsQuery = MetricsQuery.Replace("{toISODate}", toDate);
MetricsQuery = MetricsQuery.Replace("{term}", message.Text);
var response = await cuawc.MetricSearchUsageToCoveoAsync(MetricsQuery
.Replace(" ", "%20")
.Replace("!=", "!%3D")
.Replace("==", "%3D%3D")
.Replace(":", "%3A"));
if (response != null)
{
var message_from_bot = context.MakeMessage();
}
//message_from_bot.Attachments = new List<Attachments>();
await context.PostAsync("Please enter the search term for metrics");
}
catch (Exception e)
{
Debug.WriteLine($"Error when pulling metrics : {e.Message}");
}
finally
{
context.Wait(MessageRecievedAsync);
}
}
}
Struggling from past 2 days to figure what is wrong!!