InvalidOperationException: An operation is already in progress - c#

I am using Asp.net core Razor engine Entity Framework. I keep getting the error above and it from what I have read, it refers to the the db already being used for an operation. I am not sure why this would be happening. Is is because it is in a foreach loop? What would the workaround be? Here is my code
[HttpGet]
[Route("currentSession")]
public IActionResult CurrentSession()
{
var id = HttpContext.Session.GetInt32("Id");
if(id != null)
{
var user = _context.User.FirstOrDefault(x => x.Id == id);
ViewBag.User = user;
ViewBag.User_Id = id;
ViewBag.Auction = _context.Auction.AsEnumerable();
foreach(var item in ViewBag.Auction)
{
if(item.End_Date < DateTime.Now)
{
var seller_id = (int)item.Id_Of_Seller;
var seller = _context.User.FirstOrDefault(x => x.Id == seller_id); //this is the line that causes the error in the title
var bidder_id = (int)item.Id_Highest_Bid;
var buyer = _context.User.FirstOrDefault(x => x.Id == bidder_id); //this line also causes the same error
buyer.Wallet -= item.Bid;
seller.Wallet += item.Bid;
_context.Auction.Remove(item);
_context.SaveChanges();
}
}
return View("Home");
}
return RedirectToAction("LoginPage");
}

Can you try replacing AsEnumerable with ToList?
ViewBag.Auction = _context.Auction.ToList();

I added MultipleActiveResultSets=True to my sql server connection strings and this fixed the exception. No other changes were needed.
This fixed the async methods, the background tasks and IQueryable loops for me.

Related

EF Core update an existing entity

I'm using EF Core and .NET 6 and I would like to essentially upsert an entity to a table - a fairly simple ask.
I have the following code:
var countries = GetCountries();
using (var scope = scopeFactory.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
foreach (var c in countries)
{
// check if the country already exists.
var exists = dbContext.Countries.Where(d => d.Id == c.Id).FirstOrDefault();
// if already exists - update (rather than add).
if (exists != null)
{
exists.Name = c.Name;
exists.DisplayName = c.DisplayName;
... // omitted other prop updates.
dbContext.Countries.Update(exists);
}
else
{
dbContext.Countries.Add(c);
}
}
await dbContext.SaveChangesAsync();
}
I was wondering - is there was a more efficient way to update without manually looking up then updating (it's not very performant).
Preferably, I was hoping there was a Merge method in EF Core but can't find anything useful (that's free...). Rather than doing the manual lookup and update in this way.
I'm probably missing something very obvious here - thanks for any pointers in advance!
EF Core do not have Merge, or similar for Upsert.
You can improve performance of your query by selecting existng items in one batch. Also you do not need to call Update, just change properties.
var countries = GetCountries();
using (var scope = scopeFactory.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
var countyIds = countries.Select(c => c.Id);
var existing = (await dbContext.Countries.Where(d => countyIds.Contains(d.Id))
.ToListAsync())
.ToDictionary(c => c.Id);
foreach (var c in countries)
{
// check if the country already exists.
if (existing.TryGetValue(c.Id, out var toUpdate))
{
// if already exists - update (rather than add).
toUpdate.Name = c.Name;
toUpdate.DisplayName = c.DisplayName;
... // omitted other prop updates.
}
else
{
dbContext.Countries.Add(c);
}
}
await dbContext.SaveChangesAsync();
}
The answer by user19087368 seems the most straight forward. I tested it on .NET 6 and it worked perfectly - adapted a little bit to my usecase:
Here is my version of it with the "guid" as the primary key:
public async Task CreateOrUpdateApplication(Application application)
{
var itemExists = _dbContext
.Application
.Any(i => i.ApplicationGuid == application.ApplicationGuid);
_dbContext.Entry(application).State = itemExists ?
EntityState.Modified : EntityState.Added;
await _dbContext.SaveChangesAsync();
}
public void InsertOrUpdate(Entity entity)
{
using (var context = new dbContext.Countries())
{
context.Entry(entity).State = entity.Id == 0 ?
EntityState.Added :
EntityState.Modified;
context.SaveChanges();
}
}

Remove certain properties from object upon query EF Core 2.1

I'm trying to null or remove the ID entirely of all the queried IsoDataTables before returning them to frontend. The idea is that it should behave (in this case) as a template and I don't want it returning the id's back to me, nor do I want them to be removed in the frontend.
var applicationType = await _context.ApplicationType
.Include(m => m.IsoTableData)
.AsNoTracking()
.FirstOrDefaultAsync(m => m.Id == id);
if (applicationType == null)
{
return NotFound();
}
if (applicationType.IsoTableData != null)
{
foreach (IsoTableData isoTableData in applicationType.IsoTableData)
{
// error since it a not nullable primary key
isoTableData.Id = null;
}
}
return Ok(applicationType);
I have found a workaround in which I duplicate the objects and return them (without saving to DB) but I'm looking for a more elegant solution.
The way I did it was create a copy constructor (or basically, a new instance of an object) with the desired fields; I chose a copy constructor as this logic is recurent in other places as well. Another similar solution is creating a DTO object, but I don't need it here. Any improvements?
//in IsoFileApplicationType.cs
public IsoFileApplicationType(IsoFileApplicationType isoFileApplicationType)
{
Id = null
FullName = isoFileApplicationType.FullName;
Name = isoFileApplicationType.Name;
(...)
foreach (IsoTableData isoTableData in isoFileApplicationType.IsoTableData)
{
IsoTableData.Add(IsoTableData(isoTableData));
}
}
//in IsoTableData.cs
public IsoTableData(IsoTableData isoTableData)
{
Id = null;
Data = isoTableData.Name;
Age = isoTableData.Age;
(...)
}
// in CRUD controller
var applicationType = await _context.ApplicationType
.Include(m => m.IsoTableData)
.AsNoTracking()
.FirstOrDefaultAsync(m => m.Id == id);
if (applicationType == null)
{
return NotFound();
}
IsoFileApplicationType newIsoFileApplicationType = IsoFileApplicationType(applicationType);
return Ok(newIsoFileApplicationType);

How to delete a row from database using lambda linq?

I want to perform a delete operation to unfriend a user in a certain android application I'm developing. The following method returns "Done" but the data doesn't delete from the table. What is the problem here?
public string deleteFriend(int user, int friend) {
int i = db.Friends.Where(x => x.Person.Id == user && x.Person1.Id == friend).Select(x => x.Id).First();
var rec=db.Friends.ToList();
rec.RemoveAll(x=>rec.Any(xx=>xx.Id==i));
db.SaveChanges();
return "Done";
}
I'm working with Entity frame work LINQ.
Try something like this:
var friend = db.Friends.Where (x=>x.person.Id == user && x.Person1.Id == friend).FirstorDefault();
if (friend != null)
{
db.friends.Remove(friend);
db.SaveChanges()
}
if you have got multiple records than you can get rid of firstOrDefault and add .ToList() in the end.
And than use db.friends.RemoveRange(friend)
it is much cleaner and hope this helps
Thanks
using where id in (1,2,3)
List<int> ids = new List<int>(){1,2,3};
var table1 = _context.table1.Where(x => ids.Contains(x.Id)).ToList();
if (table1 != null && table1.Count > 0) {
_context.table1.RemoveRange(table1 );
_context.SaveChanges();
}

MVC 3 Entity Framework Empty Result Set

I am working on an application in which it returns an excel report with each employee having multiple roles and their expected and actual hours for each role. For instance, I may be listed as a developer for one and a BA for another column!. The query it is using is returning an empty result set.
public ActionResult ExpectedVsActual()
{
try
{
ProjectTotalsReportViewModel model = new ProjectTotalsReportViewModel();
Employee currentUser = DataHelper.GetEmployee(User, db);
model.AvailableEmployees = db.Employees.OrderBy(e => e.LastName).ThenBy(e => e.FirstName).ToList();
if (currentUser.SecurityRoleCode == Constants.SECURITY_ROLE_CODE_ADMIN)
{
model.AvailableProjects = (db.Projects.Any() ? db.Projects.Where(p => p.ProjectCategoryCode == Constants.PROJECT_CATEGORY_DIRECT).OrderBy(p => p.ProjectName).Distinct().ToList() : new List<Project>());
model.ProjectRate = (db.EmployeeProjectRates.Any() ? db.EmployeeProjectRates.Where(epr => epr.EmployeeID == epr.EmployeeID).OrderBy(epr => epr.EmployeeID).Distinct().ToList(): new List<EmployeeProjectRate>());
}
else
{
model.AvailableProjects = (db.EmployeeProjectRates.Any(epr => epr.EmployeeID == currentUser.EmployeeID && epr.ProjectRoleCode == Constants.PROJECT_ROLE_PROJECT_MANAGER) ? db.EmployeeProjectRates.Where(epr => epr.EmployeeID == currentUser.EmployeeID && epr.ProjectRoleCode == Constants.PROJECT_ROLE_PROJECT_MANAGER).Select(epr => epr.Project).OrderBy(p => p.ProjectName).Distinct().ToList() : new List<Project>());
}
model.SelectedEmployeesForCheckBox = model.AvailableEmployees;
model.SelectedProjectsForCheckBox = model.AvailableProjects;
ViewBag.InitialLoad = true;
return PartialView("_ExpectedVsActual", model);
}
catch
{
return null;
}
}
[HttpPost]
public ActionResult ExpectedVsActual(ProjectTotalsReportViewModel Model)
{
try
{
if (ModelState.IsValid)
{
ViewBag.InitialLoad = false;
DataHelper.TimeFrame selectedTimeFrame = DataHelper.TimeFrame.Weekly;//Need to remove after adding selection for time frame
SMCContext db = new SMCContext();
ViewBag.TimeFrameSelectList = DataHelper.GetTimeFrameSelectList();
List<ProjectTimeFrame> timeFrames = new List<ProjectTimeFrame>();
ProjectViewModel Project = new ProjectViewModel();
timeFrames = DataHelper.GetProjectTimeFrames(Model.StartDate.Value, Model.EndDate.Value, selectedTimeFrame);
Project.EmployeeProjectRates = Project.EmployeeProjectRates.OrderBy(epr => epr.Employees.Single(e => e.Value == epr.EmployeeID.ToString())).ToList();
resultFile = currentPackage.GetAsByteArray();
return File(resultFile, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "Expected Vs Actual.xlsx");
}
#endregion
}
else
{
return PartialView("_ExpectedVsActual", Model);
}
}
catch (Exception e)
{
throw e;
}
}
The bulk of all the formatting and creation of the file I left out, it is not related to the empty result. The result should look like this
It's failing for you on at least one of three points:
Verify the data in your database reflects what you're searching for. The quickest way to do this is through some sort of profiler that can capture your EF query as it is transformed and show you the SQL statement. linqpad is a great tool to do this with.
Verify your mappings. You might not have your relationships mapped correctly, producing no data to yield.
Verify your own constraints. It looks like you have permission-based lookups. Place a breakpoint after the current user is fetched and make sure it has the permission to match the permission you're looking for (Constants.SECURITY_ROLE_CODE_ADMIN) in your conditional.

Reuse query in many object context

I was working this weekend on parallelizing a section of code using Tasks to run all the queries I needed for a dashboard page.
What I have now is many copy/paste methods with almost exactly the same query and a different line at the very end of the method.
Is there a way to write a query against one object context then detach it and pass to a method?
I want to do something like this:
using(DbContext db = new DbContext)
{
var query = db.cars.where(x => x.make == "Ford");
int handleCounts = getDoorHandleCounts(query);
}
public int getDoorHandleCounts(type? query)
{
using(DbContext db = new DbContext())
{
return query.where(x => x.partType == "DoorHandle").Count();
}
}
Any ideas?
Keep in mind all my count() methods are launched from a Task array so they'll be running in parallel. I need a new object context to run each count query on.
I've done some googling and thought about trying to use a pre-compiled query and call it from different object context's, but my real query is kind of complex with allot of if blocks to determine the where condition. Can you compile a query that isn't really simple?
You can't detach and attach a query from/to a context. However, you could reuse the first expression:
Expression<Func<Car,bool>> InitialSelection()
{
return x => x.make == "Ford";
}
public int GetDoorHandleCounts()
{
using(DbContext db = new DbContext())
{
return db.Cars()
.Where(InitialSelection())
.Where(x => x.partType == "DoorHandle").Count();
}
}
And in your task:
int handleCounts = getDoorHandleCounts();
This only works if the initial query is "simple", i.e. does not contain joins and predicates on joined sets that you should repeat over and over in each getX method.
As an alternative, you could initialize a context and feed it to a method that returns a query:
public IQueryable<Car> GetInitialQuery(DbContext db)
{
return db.Cars().Join(....)
.Where(x => x.make == "Ford")
.Where(....);
}
public int GetDoorHandleCounts()
{
using(DbContext db = new DbContext())
{
return GetInitialQuery(db)
.Where(x => x.partType == "DoorHandle").Count();
}
}
Maybe I'm misunderstanding the question, but wouldn't this do what you're looking for?
using(DbContext db = new DbContext)
{
var carsResult = db.cars.where(x => x.make == "Ford");
int handleCounts = getDoorHandleCounts(carsResult);
}
public int getDoorHandleCounts(IEnumerable<Car> result)
{
return result.where(x => x.partType == "DoorHandle").Count();
}
Edit: never mind, I only now saw your mention of the Task array.
Change
public int getDoorHandleCounts(type? query)
to
public int getDoorHandleCounts(IQueryable<cars> query)
And replace cars with the of objects the query will return.
Edit
I would just recommend passing value you're looking to filter on, like this:
{
...
int handleCounts = getDoorHandleCounts("Ford");
}
public int getDoorHandleCounts(string Make)
{
using(DbContext db = new DbContext())
{
return db.cars.where(x => x.make == Make && x.partType == "DoorHandle").Count();
}
}

Categories