Cannot change values in EF despite using SaveChanges() - c#

I'm having trouble updating values in Entity Framework 6, I've looked thoroughly through the internet for answers but it seems I'm doing everything just fine, yet I can't seem to make it work.
It's worth mentioning that adding entities into the DB works just fine, meaning I can add products/users in my project to the DB, but not update them.
This is homework.
public bool ChangeAccountStatus(long userID, bool isUserActive)
{
User userToChange = GetUserById(userID); // Gets the user whose values I want to change.
using (var context = new ShopContext())
{
if (userToChange != null)
{
if (isUserActive)
{
userToChange.IsActive = false;
context.SaveChanges();
}
else
{
userToChange.IsActive = true;
context.SaveChanges();
}
return true;
}
return false;
}
}
I can make the update work if I use linq to find the user whose value I want to change, but I don't want too much code-duplication in my project, and I'm using the same linq (function GetUserById) in many other functions.
Do I need to use linq to access the user from the database, instead of using a function I created to avoid code-duplication?
This is the GetUserById function:
public User GetUserById(long userId)
{
using (var context = new ShopContext())
{
var userToFind = context.UsersTable
.Where((u) => u.Id == userId).FirstOrDefault();
if (userToFind != null)
return userToFind;
else
return null;
}
}

You are retrieving an entity from one context, then calling SaveChanges() on a different context. If you inline the method, it becomes more clear:
var userToChange;
using (var context = new ShopContext())
{
userToChange = context.UsersTable.Where((u) => u.Id == userId).FirstOrDefault();
}
using (var context = new ShopContext())
{
if (userToChange != null)
{
if (isUserActive)
{
userToChange.IsActive = false;
context.SaveChanges();
}
else
{
userToChange.IsActive = true;
context.SaveChanges();
}
return true;
}
return false;
}
The second context doesn't know anything about userToChange, because it isn't tracking it. You can tell it to though:
context.UsersTable.Attach(userToChange);
As an aside, you have some redundant code there - an if statement, which assigns a boolean to true or false can be simplified:
if (isUserActive)
userToChange.IsActive = false;
else
userToChange.IsActive = true;
// Equivalent to:
userToChange.IsActive = !isUserActive;
And the null check is not necessary:
if (userToFind != null)
return userToFind;
else
return null;
// Equivalent to:
return userToFind;

Related

C# FirstOrDefault() return null even with element existing

I have the following code that needs to return a single value. However, I keep on getting null (the field Notifications_Id = 0) even though it clearly exists in the database.
var role = _context.AssignedTickets.FirstOrDefault(a => a.Notifications_Id == incidence.Notifications_Id);
I am using Net Core 5
Below is my code
public async Task<ActionResult> EditTicket(int? id)
{
{
if (id == null)
{
return StatusCode(400);
}
Incidence incidence = await _context.Incidences.FindAsync(id);
if (incidence == null)
{
return StatusCode(401);
}
return View(incidence);
}
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult EditTicket(Incidence incidence)
{
var role = _context.AssignedTickets.FirstOrDefault(a => a.Notifications_Id == incidence.Notifications_Id);
if (role == null)
{
return StatusCode(404);
}
role.Status = "C";
role.ClosedOn = DateTime.Now;
if (ModelState.IsValid)
{
DateTime Timeup = (DateTime)role.ClosedOn;
DateTime Timedown = (DateTime)role.CreatedOn;
long DiffTicks = (Timedown - Timeup).Ticks;
role.TurnAroundTime = Math.Abs(DiffTicks).ToString();
_context.Entry(incidence).State = EntityState.Modified;
_context.SaveChangesAsync();
return RedirectToAction("Dashboard", "Agent");
}
return View(incidence);
}
Please use var role = _context.AssignedTickets.FirstOrDefault(a => a.Notifications_Id == 29); to get the object, and then compare whether the attributes inside match the database.
I read your post and all comments carefully. What everyone said is very reasonable, and the code in the post is not problematic.
So I think the problem is likely to be a problem with the string connecting to the database, that is, a different database is used.

SaveChanges not working on JsonResult method (UPDATE INFO)

The problem is basically the Save changes method is not working (updating), the method should receive 3 parameters, item id, user id and the object which contains the updated information from the UI, however the code seems to be something bad because the saveChanges() method is not working.
This is my code:
[HttpPost]
[AllowAnonymous]
public JsonResult UpdatePersonalData(int ItemId, int UserId, CND_PersonalData Item)
{
try
{
if (ModelState.IsValid)
{
using (var context = new DexusEntities())
{
CND_PersonalData PersonalData = context.CND_PersonalData.Where(d => d.Id == ItemId && d.UserId == UserId).SingleOrDefault();
if (PersonalData == null)
{
/// Display bad request
/// User does not exist and/or is not activated
List<RootObject> rootObj = new List<RootObject>();
rootObj.Add(new RootObject
{
msg = "User/Item not found in our DB",
code = "error_07"
});
HttpContext.Response.StatusCode = 404;
HttpContext.Response.TrySkipIisCustomErrors = true;
JsonRes.Message = rootObj;
return Json(JsonRes, JsonRequestBehavior.AllowGet);
}
else
{
PersonalData = Item;
context.SaveChanges();
context.ChangeTracker.DetectChanges();
List<RootObject> rootObj = new List<RootObject>();
rootObj.Add(new RootObject
{
msg = "Information stored/updated successfully",
code = "success_05"
});
HttpContext.Response.StatusCode = 200;
JsonRes.Message = rootObj;
return Json(JsonRes, JsonRequestBehavior.AllowGet);
}
}
}
else
{
List<RootObject> rootObj = new List<RootObject>();
JsonRes.Issue = "The model is not correct";
rootObj.Add(new RootObject
{
msg = "Model is not valid",
code = "error_03"
});
HttpContext.Response.StatusCode = 403;
HttpContext.Response.TrySkipIisCustomErrors = true;// Avoid issues in the HTTP methods
JsonRes.Message = rootObj;
return Json(JsonRes, JsonRequestBehavior.AllowGet);
}
}
catch (Exception ex)
{
string err = ex.ToString();
List<RootObject> rootObj = new List<RootObject>();
JsonRes.Issue = err;
rootObj.Add(new RootObject
{
msg = "Conflict with method, see issue description.",
code = "error_08"
});
HttpContext.Response.StatusCode = 400;// Bad request
HttpContext.Response.TrySkipIisCustomErrors = true;
JsonRes.Message = rootObj;
return Json(JsonRes, JsonRequestBehavior.AllowGet);
}
}
What's wrong with my code?
Thanks in advance.
As I can see you are not adding an item into the DbSet and calling SaveChanges after:
When adding an item you should put it into DbSet
context.CND_PersonalData.Add(item);
context.SaveChanges();
when you want to update just call SaveChanges after you update loaded object
var PersonalData= context.CND_PersonalData.Where(d => d.Id == ItemId && d.UserId == UserId).SingleOrDefault();
PersonalData.Name = item.Name;
PersonalData.Title = item.Title;
context.SaveChanges();
You can't just assign passed object to an entity you got from the DB, you need to change properties. If you do it as you did you didn't change values in the loaded object. So when you call SaveChanges nothing is changed. You need to change properties one by one.
If you don't want to do that then you can attach your item into the db by using Attach method on context.
context.Attach(item);
context.SaveChanges();
but you should be careful because if you load and track item with the same id as you are doing before checking if it is null:
CND_PersonalData PersonalData = context.CND_PersonalData.Where(d => d.Id == ItemId && d.UserId == UserId).SingleOrDefault();
if (PersonalData == null)
{
then you will get an error during the save because the context is already tracking item with the same ID, so you can remove that check and just check if it exists:
if (context.CND_PersonalData.Any(d => d.Id == ItemId && d.UserId == UserId))
{
and then execute your code

The underlying provider failed on Open / The operation is not valid for the state of the transaction

Here is my code
public static string UpdateEmptyCaseRevierSet() {
string response = string.Empty;
using (System.Transactions.TransactionScope tran = new System.Transactions.TransactionScope()) {
using (var db = new Entities.WaveEntities()) {
var maxCaseReviewersSetID = db.CaseReviewerSets.Select(crs => crs.CaseReviewersSetId).Max();
var emptyCHList = db.CaseHistories.Where(ch => ch.CaseReviewersSetID == null && ch.IsLatest == true && ch.StatusID != 100).ToList();
for(int i=0; i < emptyCHList.Count; i++) {
var emptyCH = emptyCHList[i];
var newCaseReviewerSET = new Entities.CaseReviewerSet();
newCaseReviewerSET.CreationCHID = emptyCH.CHID;
db.CaseReviewerSets.Add(newCaseReviewerSET);
emptyCH.CaseReviewerSet = newCaseReviewerSET;
}
db.SaveChanges();
}
tran.Complete();
}
return response;
}
The exception occures on "db.SaveChanges()"
I saw in another post with the same error message something about "it seems I cannot have two connections opened to the same database with the TransactionScope block." but I dont think that this has anything to do with my case.
Additionally the number of records to insert and update in total are 2700, witch is not that many really. But it does take quite a lot of time to complete the for statement (10 minutes or so). Since everything happening within the for statement is actually happening in the memory can someone please explane why is this taking so long ?
You can try as shown below using latest db.Database.BeginTransaction API.
Note : use foreach instead of for
using (var db = new Entities.WaveEntities())
{
using (var dbContextTransaction = db.Database.BeginTransaction())
{
try
{
var maxCaseReviewersSetID = db.CaseReviewerSets.Select(crs => crs.CaseReviewersSetId).Max();
var emptyCHList = db.CaseHistories.Where(ch => ch.CaseReviewersSetID == null && ch.IsLatest == true && ch.StatusID != 100).ToList();
foreach(var ch in emptyCHList) {
var newCaseReviewerSET = new Entities.CaseReviewerSet();
newCaseReviewerSET.CreationCHID = ch.CHID;
db.CaseReviewerSets.Add(newCaseReviewerSET);
}
db.SaveChanges();
dbContextTransaction.Commit();
}
catch (Exception)
{
dbContextTransaction.Rollback();
}
}
}

Database SubmitChanges() in C# with Linq not working

I am trying to update a record in my SQL database. I have 2 updates to different tables in the same database. 1 works fine and the other does not update on the SubmitChanges(). The code looks the same is there something i am missing?
public bool UpdateStockSizeInBomData(string drawingNumber, string stockSize)
{
using (var db = GetFordContext())
{
Data.BomData result = db.BomDatas.Where(b => b.sIdNoStk.Equals(drawingNumber)).FirstOrDefault();
if (null == result)
{
return false;
}
result.sDimensionsStk = stockSize;
db.SubmitChanges();
return true;
}
}
This one does not work. The database record comes in fine while debugging, and even changes but the update is never committed to the database. And there has been changes made.
public bool UpdateStockSizeInDrawingData(string jobNumber, string drawingNumber, string stockSize, string material)
{
using (var db = GetFordContext())
{
Data.DrawingData result = db.DrawingDatas.Where(dd => dd.GROB_TB_DrawingNumber.Equals(drawingNumber) && dd.Ford_Vendor_JobNumber.Equals(jobNumber)).FirstOrDefault();
if (null == result)
{
return false;
}
result.Ford_Det_StockSize = stockSize;
//result.Ford_Det_Material = material;
db.SubmitChanges();
return true;
}
}

Is Object created before or after method calls?

I have three different databases that I need to check that I am connected to. This is what I originally have, which works perfectly fine.
public async Task<ServiceAvailabilityDTO> ServiceAvailabilityStatus()
{
return new ServiceAvailabilityDTO
{
IsDb1Online = await IsDb1Available(),
IsDb2Online = IsDb2Available(),
IsDb3Online = await IsDb3Available()
};
}
private async Task<bool> IsDb1Available()
{
var count = await _db1Service.GetDbCount();
if (count > 0)
return true;
return false;
}
private bool IsDb2Available()
{
if (_db2Service.GetDbCount() > 0)
return true;
return false;
}
private async Task<bool> IsDb3Available()
{
var pong = await _db3Provider.PingDb();
if(pong.Success == true && pong.Version != null)
return true;
return false;
}
Now however, I need to log exception messages in my DTO for each DB check.
public async Task<ServiceAvailabilityDTO> ServiceAvailabilityStatus()
{
return new ServiceAvailabilityDTO
{
IsDb1Online = await IsDb1Available(),
IsDb2Online = IsDb2Available(),
IsDb3Online = await IsDb3Available(this) // This is an example. I want to pass the reference of **ServiceAvailabilityDTO** to **IsDb3Available**
};
}
private async Task<bool> IsDb3Available(ServiceAvailabilityDTO availability)
{
try
{
var pong = await _db3Provider.PingDb();
if(pong.Success == true && pong.Version != null)
return true;
return false;
}
catch (Exception e)
{
var exceptionMessage = e.Message;
if (e.InnerException != null)
{
// This is what I hope to put into the object reference
exceptionMessage = String.Join("\n", exceptionMessage, e.InnerException.Message);
availability.db3Exception = exceptionMessage ;
}
return false;
}
}
My question is;
Can I keep my return method the same as in the first example, and pass the object reference to each method to store the exception and still return my bool value.
Or does the object not get created until all of the method calls have happened, and then create the object with the returned values?
I know I could just create the object normally and pass it in each
method call, but it is specifically this way of doing it that has
inspired me to ask this question, purely to be informed and learn
from.
Thanks.
No, you cannot do it like this because in the context of what you're doing this does not refer to the object you're populating, it refers to the object containing the method you're calling.
public async Task<ServiceAvailabilityDTO> ServiceAvailabilityStatus()
{
return new ServiceAvailabilityDTO
{
IsDb1Online = await IsDb1Available(),
IsDb2Online = IsDb2Available(),
IsDb3Online = await IsDb3Available(this) // here 'this' does NOT ref to ServiceAvailabilityDTO
};
}
There is no keyword which does refer to ServiceAvailabilityDTO either, so you're left with creating the object, and passing it to each method. At this point, I dont think there is much point you returning the boolean either - you may as well set the boolean property in line
public async Task<ServiceAvailabilityDTO> ServiceAvailabilityStatus()
{
var sa = new ServiceAvailabilityDTO();
await CheckDb1Available(sa);
CheckDb2Available(sa);
await CheckDb3Available(sa);
return sa;
}
(Note I've renamed the methods from Is* to Check* as the former implies a return boolean, the latter implies something going on inline.)

Categories