I have this action method that checks if an item exists, and if it does, it's to be removed. If it doesn't exist, it's to be added. It's like an on-off-switch for that particular item:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> FrontPageProduct(ViewModelFrontPageProduct frontPageProduct)
{
var fpp = new FrontPageProduct()
{
ProductCategoryId = frontPageProduct.ProductCategoryId,
ProductId = frontPageProduct.ProductId,
SortOrder = 0
};
bool exists = _context.FrontPageProducts
.Any(x => x.ProductCategoryId == frontPageProduct.ProductCategoryId
&& x.ProductId == frontPageProduct.ProductId);
if (exists)
{
var delete = (from d in _context.FrontPageProducts
where (d.ProductCategoryId == frontPageProduct.ProductCategoryId &&
d.ProductId == frontPageProduct.ProductId)
select d).FirstOrDefault();
_context.Remove(delete);
}
else
{
_context.Add(fpp);
}
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index), new { id = fpp.ProductCategoryId, tab = 2 });
}
Now, I feel this is a bit long winded. Is there a shorter, but still readable way of doing this?
You do not have to use Any to determine whether it exists. Basically load it using FirstOrDefault (I used async as I see you use async in save, you can also use it in FirstOrDefault). If it is found you have an instance and you can delete it without additional load:
var fpp = new FrontPageProduct()
{
ProductCategoryId = frontPageProduct.ProductCategoryId,
ProductId = frontPageProduct.ProductId,
SortOrder = 0
};
var fppDB = await _context.FrontPageProducts
.FirstOrDefaultAsync(x => x.ProductCategoryId == frontPageProduct.ProductCategoryId && x.ProductId == frontPageProduct.ProductId);
if (fppDB != null)
{
_context.Remove(fppDB);
}
else
{
_context.Add(fpp);
}
await _context.SaveChangesAsync();
Otherwise you can also use SQL stored procedure and call this one from EF. It will be more efficient.
Related
public async Task<bool> UnSaveCourseAsync(int courseId, string userId)
{
this.logger.LogInformation($"Enter into service layer course id {courseId} and user id {userId}");
var checkCourse = await this.lmsMasterRepository.GetExistsAsync<Playlist>(i => i.IsActive && !i.IsDeleted && i.Id == courseId).ConfigureAwait(false);
var checkSavedBy = await this.lmsMasterRepository.GetExistsAsync<User>(q => q.IsActive && !q.IsDeleted && q.Id == userId).ConfigureAwait(false);
bool courseresult = checkCourse ?? false;
bool userresult = checkSavedBy ?? false;
if (!courseresult)
{
throw new ArgumentException("Course is not Available");
}
if (!userresult)
{
throw new ArgumentException("User is not Available");
}
var checkSavedCourse = await this.lmsMasterRepository.GetOneAsync<SavedCourse>(i => i.PlaylistId == courseId && i.SavedBy == userId && i.IsActive == true && i.IsDeleted == false).ConfigureAwait(false);
if (checkSavedCourse != null && checkSavedCourse.SavedBy == userId && checkSavedCourse.PlaylistId == courseId)
{
checkSavedCourse.IsDeleted = true;
checkSavedCourse.IsActive = false;
await this.lmsMasterRepository.UpdateAsync<SavedCourse>(checkSavedCourse).ConfigureAwait(false);
return await this.lmsMasterRepository.SaveAsync().ConfigureAwait(false);
}
else
{
return false;
}
}
for this method how to do database single call .
there are three tables one is playlist from this table we will get the course is present in table or not .
2nd table is user from which we will get the user is exist or not .and the 3rd table from which we will search that course and the user is present in saved Course table or not . then how can we do database single call.
first it will check in playlist table that course is present or not .
2nd in user table it will check the user s valid or not .
and 3re table it will check that in saved video table the course and the user is there or not if the course is there with same user it will unsave the course other wise it will return false .
you can write a StoredProcedure that has an Update statement. for filtering the result you can add a subquery in your WHERE statement.
UPDATE table
SET col = new_value
WHERE other_col IN (
SELECT other_col
FROM other_table --You can add our joins here that checks user and...
WHERE conditional_col = 1
);
I have very weird problem
My code it works fine if I login and use then it save the preferences etc.
But problem starts when I login, do some selections, and logout and login as another user, then upon saving it also remembers the seelctions I had done wfor the other user, the last one and save that also.
How to prevent this?
private ApplicationDbContext db = new ApplicationDbContext();
...
public IHttpActionResult Add(UserPreferencesDto model)
{
model.UserId = User.Identity.GetUserId();
var userPreferences = db.UserPreferences.Where(u =>
u.UserId == model.UserId &&
u.Key == model.Key.Trim())
.FirstOrDefault();
List<int> StatesCollection = new List<int>();
var param = model.Value.Trim();
string[] paramSplitted = param.Split(',');
if (userPreferences != null)
{
if (string.IsNullOrEmpty(userPreferences.Value) == false)
{
var trimmedPreferenceValue = userPreferences.Value.Trim('[', ']');
if (string.IsNullOrEmpty(trimmedPreferenceValue) == false)
{
StatesCollection = trimmedPreferenceValue.Split(',')
.Select(s => Convert.ToInt32(s)).ToList<int>();
}
if (model.IsStateSelected == false && paramSplitted.Count() == 1
&& StatesCollection.Contains(int.Parse(param.Trim())))
{
StatesCollection = StatesCollection.Where(sa => sa != int.Parse(param)).ToList<int>();
userPreferences.Value = StatesCollection.Count > 0 ? JsonConvert.SerializeObject(StatesCollection) : "";
}
else if (model.IsStateSelected && paramSplitted.Count() == 1
&& !StatesCollection.Contains(int.Parse(param)))
{
StatesCollection.Add(int.Parse(param));
userPreferences.Value = JsonConvert.SerializeObject(StatesCollection);
}
}
else
{
StatesCollection.Add(int.Parse(param));
userPreferences.Value = JsonConvert.SerializeObject(StatesCollection);
}
}
else
{
if (model.IsStateSelected == true)
{
//string[] splittedStates = model.Value.Split(',');
int[] secRolesIds = Array.ConvertAll(paramSplitted, int.Parse);
model.Value = JsonConvert.SerializeObject(secRolesIds);
db.UserPreferences.Add(Mapper.Map<UserPreferences>(model));
}
}
db.SaveChanges();
return Ok();
}
Even if the preferences exist it goes to the last else.
SaveChanges() in entity framework saves ALL tracked changes.
You would have to explicitly discard changes or use untracked entities, only adding them when you wish to save.
https://learn.microsoft.com/en-us/ef/core/querying/tracking
I think you should make the userPreferences variable null before giving a value to it, this way you could prevent it to have a value from the last execution because you would ensure it became null because you forced it to be. By doing so if there is no result in the database when you try to assign a value to it it will remain null for sure and so it will enter to the if with the if (userPreferences != null) and don't go to the else.
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 basically trying to handle unique constraint validation in my .Net API. I have two unique key cosntraints on two fields in my table.
If you see my code below , I am trying to check if the record exists. I am looking at returning a boolean value if the record exist. is that possible. For the time being, I am returning null.
Is this the best way to do it.
[HttpPost]
[SkipTokenAuthorization]
[Route("api/classificationoverrides/create")]
public IHttpActionResult Create(ClassificationItemViewModelCreate model)
{
var mgrClassificationService = GetService<MGR_STRT_CLASSIFICATION>();
var isExists = mgrClassificationService.Where(x =>
x.MANAGERSTRATEGYID == model.ManagerStrategyId && x.PRODUCT_ID == model.ProductId).FirstOrDefault();
if (isExists == null)
{
var mgrClassficationOverride = new MGR_STRT_CLASSIFICATION();
if (model != null)
{
mgrClassficationOverride.PRODUCT_ID = model.ProductId;
mgrClassficationOverride.LEGACY_STRATEGY_ID = model.LegacyStrategyId;
mgrClassficationOverride.STRATEGY_ID = model.StrategyId;
mgrClassficationOverride.MANAGERSTRATEGY_TYPE_ID = model.ManagerStrategyTypeId;
mgrClassficationOverride.MANAGERSTRATEGYID = model.ManagerStrategyId;
mgrClassficationOverride = mgrClassificationService.Create(mgrClassficationOverride);
}
return Ok(mgrClassficationOverride);
}
else
{
return null;
}
}
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.