I have a project that uses Entity Framework. While calling SaveChanges on my DbEntityValidationException, I get the following exception:
System.Data.Entity.Validation.DbEntityValidationException: 'Validation
failed for one or more entities. See 'EntityValidationErrors' property
for more details.'
This is all fine and dandy, but I don't want to attach a debugger every time this exception occurs. More over, in production environments I cannot easily attach a debugger so I have to go to great lengths to reproduce these errors.
How can I see the details hidden within the DbEntityValidationException?
private void btnCreateLetter_Click(object sender, EventArgs e)
{
using (TransactionScope TS = new TransactionScope())
{
try
{
Letter TblLetter = new Letter();
TblLetter.Subject = txtSubject.Text.Trim();
TblLetter.Abstract = txtAbstract.Text.Trim();
TblLetter.Body = ckeCKEditor.TextEditor.Text.Trim();
{
var LastLetterID = (from Letter in Database.Letters orderby Letter.LetterID descending select Letter).First();
TblLetter.LetterNO = PublicVariable.TodayDate.Substring(0, 4).Substring(2, 2) + PublicVariable.gDetermineJobLevel + "/" + (LastLetterID.LetterID + 1);
}
TblLetter.CreateDate = lblCreateDate.Text;
TblLetter.UserID = PublicVariable.gUserID;
if (rdbClassification.Checked == true)
{
TblLetter.SecurityType = 1;
}
else if (rdbConfidential.Checked == true)
{
TblLetter.SecurityType = 2;
}
else if (rdbSeries.Checked == true)
{
TblLetter.SecurityType = 3;
}
if (rdbActionType.Checked == true)
{
TblLetter.UrgencyType = 1;
}
else if (rdbInstantaneous.Checked == true)
{
TblLetter.UrgencyType = 2;
}
else if (rdbAnnie.Checked == true)
{
TblLetter.UrgencyType = 3;
}
TblLetter.ArchivesType = 1;
if (rdbFollowHas.Checked == true)
{
TblLetter.FollowType = 1;
}
else if (rdbFollowHasnoot.Checked == true)
{
TblLetter.FollowType = 2;
}
if (rdbAttachmentHas.Checked == true)
{
TblLetter.AttachmentType = 1;
}
else if (rdbAttachmentHasnot.Checked == true)
{
TblLetter.AttachmentType = 2;
}
TblLetter.ReadType = 1;
TblLetter.LetterType = 1;
TblLetter.DraftType = 1;
if (rdbResponseDeadlineHas.Checked == true)
{
TblLetter.AnswerType = 1;
TblLetter.AnswerReadLine = String.Format("{0:yyyy/MM/dd}", Convert.ToDateTime(pdpSetResponseDeadline.Value.Year.ToString() +
"/" + pdpSetResponseDeadline.Value.Month.ToString() + "/" + pdpSetResponseDeadline.Value.Day.ToString()));
}
else if (rdbResponseDeadlineHasnot.Checked == true)
{
TblLetter.AnswerType = 2;
}
Database.Letters.Add(TblLetter);
Database.SaveChanges();
if (rdbAttachmentHas.Checked == true)
{
if (lblPath.Text != "")
{
FileStream ObjectFileStream = new FileStream(lblPath.Text, FileMode.Open, FileAccess.Read);
int Lenght = Convert.ToInt32(ObjectFileStream.Length);
byte[] ObjectData;
ObjectData = new byte[Lenght];
string[] strPath = lblPath.Text.Split(Convert.ToChar(#"\"));
ObjectFileStream.Read(ObjectData, 0, Lenght);
ObjectFileStream.Close();
AttachFile TableAttachFile = new AttachFile();
TableAttachFile.FileSize = Lenght / 1024;
TableAttachFile.FileName = strPath[strPath.Length - 1];
TableAttachFile.FileData = ObjectData;
TableAttachFile.LetterID = TblLetter.LetterID;
Database.AttachFiles.Add(TableAttachFile);
Database.SaveChanges();
}
}
TS.Complete();
MessageBox.Show("saved", "ok", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (InvalidCastException ex)
{
MessageBox.Show(ex.ToString());
MessageBoxIcon.Error);
return;
}
}
}
You have to get the validation errors from the db.SaveChanges() method of the DatabaseContext object -- you can't get them where you are.
You can either modify the SaveChanges() method of your database context and wrap it in a try-catch block, or (since the class is partial) you can extend the partial class within your application and just override the SaveChanges() method.
There is a nice blog post about this called Easy way to improve DbEntityValidationException of Entity Framework here.
The essence of it is something like this:
public partial class NorthwindEntities
{
public override int SaveChanges()
{
try
{
return base.SaveChanges();
}
catch (DbEntityValidationException ex)
{
// Retrieve the error messages as a list of strings.
var errorMessages = ex.EntityValidationErrors
.SelectMany(x => x.ValidationErrors)
.Select(x => x.ErrorMessage);
// Join the list to a single string.
var fullErrorMessage = string.Join("; ", errorMessages);
// Combine the original exception message with the new one.
var exceptionMessage = string.Concat(ex.Message, " The validation errors are: ", fullErrorMessage);
// Throw a new DbEntityValidationException with the improved exception message.
throw new DbEntityValidationException(exceptionMessage, ex.EntityValidationErrors);
}
}
}
The blogger explains:
That’s it! The rest of your code will automatically use the overridden
SaveChanges so you don’t have to change anything else. From now on,
your exceptions will look like this:
System.Data.Entity.Validation.DbEntityValidationException: Validation
failed for one or more entities. See 'EntityValidationErrors' property
for more details. The validation errors are: The field PhoneNumber
must be a string or array type with a maximum length of '12'; The
LastName field is required.
The DbEntityValidationException also contains the entities that caused
the validation errors. So if you require even more information, you
can change the above code to output information about these entities.
As mention, you need to check on your EntityValidationError when it throws the exception.
You should fix that validation error, instead of asking bypass this exception.
Normally these errors are table allow length, data type, column does not allow null and etc. There will be exact fiend name mention in your exception too.
Related
I have a list in a view with values that need to be updated.
When calling the http method, I am going thru the list components and looking into the database for a item with the right Id and updating the Grade value. My problem is that I can't return the right item from the database, it always returns null. I have checked with debugging, the item from the list with the Id that I am looking for has the right value, but I can't return the item from db.
I have also tried with the sql raw query and it gives me the same error.
This is my code - the var exam is always null :
public ActionResult UpdateExams(ExamsList examList)
{
var examVM = new ExamViewModel
{
Professor = _context.Professor.ToList()
};
examVM.ExamsList = examList;
if (!ModelState.IsValid)
{
var GradesList = new List<int>();
for (int i = 5; i <= 10; i++)
{
GradesList.Add(i);
}
var GradesListSL = new SelectList(GradesList);
return PartialView("ExamsTable", examList);
}
try
{
for (int i = 0; i < examList.ExamDetails.Count; i++)
{
var exam = _context.Exam.Single(e => e.Id == examList.ExamDetails[i].Id);
// var exam = _context.Database.SqlQuery<Exam>(#"SELECT Id as Id,Grade as Grade
//FROM Exam
//WHERE Id={0})
//", examList.ExamDetails[i].Id).ToList();
exam.Grade = examList.ExamDetails[i].Grade == 0 ? exam.Grade : examList.ExamDetails[i].Grade;
}
// _context.SaveChanges();
TempData["InsertingExam"] = "Success!";
// return RedirectToAction("Create", "Exams");
return Json(new { redirectTo = Url.Action("Edit", "Exams") });
}
catch (System.Data.Entity.Infrastructure.DbUpdateException ex) //DbContext
{
string exception = ex.StackTrace + ex.Message;
ModelState.AddModelError("Error", exception);
return View(examVM);
throw;
}
catch (Exception ex)
{
string exception = ex.StackTrace + ex.Message;
ModelState.AddModelError("Error", exception);
return View(examVM);
throw;
}
finally
{
_context.SaveChanges();
}
}
The problem was that I was trying to do the database operation with the view model class.
After I added creating the model class from the view model everything worked just fine.
My objects will not save no matter what I do they will fetch and get info and make a new record but not update.
This is the code that details with getting existing patient and then modifying the record setting the state then calling save change this is cracking my head the last three hours what is going wrong. I was told you had to change the entity state of an object before it would no if to save but when i try to attach it it says its already attached
Appointment _appointment = new Appointment();
int errorCount = 0;
Patient _patient = SourceDal.getPatientByPatientNewId(Convert.ToInt32(txtPatientId.Text));
_patient.SSN = txtSSN.Text;
_patient.FirstName = txtPatientFirstName.Text;
_patient.LastName = txtPatientLastName.Text;
_patient.Middle = txtPatientMiddle.Text;
_patient.AddressOne = txtPatientAddressOne.Text;
_patient.City = txtPatientCity.Text;
_patient.State = txtPatientState.Text;
_patient.ZipCode = txtPatientZip.Text;
_patient.HomePhone = txtPatientHomePhone.Text;
_patient.WorkPhone = txtPatientWorkPhone.Text;
_patient.CellPhone = txtPatientCellPhone.Text;
if (rBtnHomePhone.Checked == true)
// _patient.ApptPhone = txtPatientHomePhone.Text;
if (rBtnHomePhone.Checked == true)
// _patient.ApptPhone = txtPatientHomePhone.Text;
if (rBtnWorkPhone.Checked == true)
// _patient.ApptPhone = txtPatientWorkPhone.Text;
_patient.BirthDate = dtBirthDate.DateTime;
_patient.emailAddress = txtPatientEmail.Text;
_patient.Race = Convert.ToInt32(dpRace.SelectedValue);
_patient.Ethnicity =Convert.ToInt32(dpEthnicity.SelectedValue);
_patient.Language = Convert.ToInt32(dpLanguages.SelectedValue);
if (dpGender.Text == "")
{
dpGender.Focus();
errorCount = 1;
lblGenderRequired.Text = "* Gender is required.";
}
else
{
errorCount = 0;
lblGenderRequired.Visible = false;
}
_patient.Gender = "M";
_patient.PatientID = txtPatientId.Text;
SourceDal.SourceEntities.Patients.Attach(_patient);
SourceDal.SourceEntities.Patients.Context.ObjectStateManager.ChangeObjectState(_patient, EntityState.Modified);
SourceDal.SourceEntities.SaveChanges();
The error I get is
An unhandled exception of type 'System.InvalidOperationException' occurred in System.Data.Entity.dll
Additional information: An entity object cannot be referenced by multiple instances of IEntityChangeTracker.
Edit 2:
Code to show my function getPaitnetByPatineyNewId
public Patient getPatientByPatientNewId(int newId)
{
Patient patient = new Patient();
if (newId == -1)
{
patient = new Patient();
}
else
{
patient = SourceEntities.Patients
.Where(w => w.ID == newId)
.FirstOrDefault();
}
return patient;
}
I think you have some issues with proper separation of concerns within your DAL, but for the short solution, you should only add (and not attach) if it's a new entity
if (_patent.PatentId == 0)
{
_patient.PatientID = txtPatientId.Text; // If you're using an identity column, remove this line. I would also strongly suggest not letting the user change this...
SourceDal.SourceEntities.Patients.Add(_patient);
}
For Anyone else the above scenarios did not work for me so this is what I had to do. I put a flag on my forms isUpdate and check that on the save button then if save call similar to below then if add just call savechanges and its now working thank you for everyone help hope this help someone.
public void SaveProviders(Provider _providers)
{
try
{
using (var ctx = new SMBASchedulerEntities(this.Connectionstring))
{
ctx.Providers.Add(_providers);
ctx.Entry(_providers).State = System.Data.Entity.EntityState.Modified;
ctx.SaveChanges();
}
}
catch (DbEntityValidationException e)
{
foreach (var eve in e.EntityValidationErrors)
{
Console.WriteLine("Entity of type \"{0}\" in state \"{1}\" has the following validation errors:",
eve.Entry.Entity.GetType().Name, eve.Entry.State);
foreach (var ve in eve.ValidationErrors)
{
Console.WriteLine("- Property: \"{0}\", Error: \"{1}\"",
ve.PropertyName, ve.ErrorMessage);
}
}
throw;
}
}
Im developing a web application in c# and it uses entity framework to make a connection to a sql server database. Everything works fine but there is a weird problem when im trying to add an entity, the program throws an exception when the changes in the DB are been committed, so none of them can be saved.
This is the controller code
[HttpPost]
public ActionResult AddEditEntidadFinanciera(AddEditEntidadFinancieraViewModel model)
{
try
{
using (var TransactionScope = new TransactionScope())
{
if (!ModelState.IsValid)
{
TryUpdateModel(model);
PostMessage(MessageType.Error, i18n.ValidationStrings.DatosIncorrectos);
return View(model);
}
sgTabEntidadFinanciera EntidadFinanciera;
sgTabReglaLimite regla;
if (model.EntidadFinancieraId.HasValue)
{
EntidadFinanciera = context.sgTabEntidadFinanciera.Find(model.EntidadFinancieraId.Value);
regla = context.sgTabReglaLimite.FirstOrDefault(x => x.fk_sgTabReglaLimite_sgTabEntidadFinanciera == EntidadFinanciera.PK_EntidadFinanciera);
if (regla == null)
{
regla = new sgTabReglaLimite();
regla.sgTabEntidadFinanciera = EntidadFinanciera;
context.sgTabReglaLimite.Add(regla);
}
}
else
{
EntidadFinanciera = new sgTabEntidadFinanciera();
regla = new sgTabReglaLimite();
regla.sgTabEntidadFinanciera = EntidadFinanciera;
EntidadFinanciera.fk_sgTabEntidadFinanciera_sgTabEstado = ConstantHelpers.ESTADO_SISTEMA_ENTIDAD_ACTIVO;
context.sgTabEntidadFinanciera.Add(EntidadFinanciera);
context.sgTabReglaLimite.Add(regla);
}
EntidadFinanciera.vRazonSocial = model.RazonSocial;
EntidadFinanciera.vDireccion = model.Direccion;
EntidadFinanciera.vRuc = model.Ruc;
EntidadFinanciera.vAcronimo = model.Acronimo;
EntidadFinanciera.fk_sgTabEntidadFinanciera_sgTabServidor = 1;
EntidadFinanciera.vFtp = model.Ftp;
EntidadFinanciera.vPasswordFtp = model.PasswordFtp;
EntidadFinanciera.vRazonSocial = model.RazonSocial;
EntidadFinanciera.vUsuarioFtp = model.UsuarioFtp;
EntidadFinanciera.vVcBin = model.VcBin;
EntidadFinanciera.vVcIPBancaDesarrollo = model.VcIPBancaDesarrollo;
EntidadFinanciera.vVcMascaraCuentaExterna = model.VcMascaraCuentaExterna;
EntidadFinanciera.vVcMascaraCuentaInterna = model.VcMascaraCuentaInterna;
EntidadFinanciera.iLimite = model.Limite;
if (model.Imagen != null && !String.IsNullOrEmpty(model.Acronimo))
{
var fileName = Guid.NewGuid().ToString().Substring(0, 4) + Path.GetFileName(model.Imagen.FileName);
var path = Path.Combine(Server.MapPath(ConstantHelpers.GetRutaImagenLogoEntidadFinanciera()), fileName);
if (!String.IsNullOrEmpty(EntidadFinanciera.vRuta))
{
System.IO.File.Delete(Server.MapPath(EntidadFinanciera.vRuta));
model.Imagen.SaveAs(path);
EntidadFinanciera.vRuta = ConstantHelpers.GetRutaImagenLogoEntidadFinanciera() + fileName;
}
}
//Here is when the exception is thrown
context.SaveChanges();
TransactionScope.Complete();
PostMessage(MessageType.Success);
return RedirectToAction("LstEntidadFinanciera");
}
}
catch (Exception ex)
{
ExceptionGlobokas exceptionGlobokas = new ExceptionGlobokas(ex.Message, log4net.Core.Level.Alert, ex);
InvalidarContext();
PostMessage(MessageType.Error);
model.Fill(CargarDatosContext(), model.EntidadFinancieraId);
TryUpdateModel(model);
return View(model);
}
}
This is the output after the error message is shown
Output
I already checked that my PK has identity enabled in my database, also i verify if there is a problem with entity framework versions in App.config and Web.config, so im not sure what is causing the problem.
EDIT
Here is the InnerException message
InnerExceptionImage
And this is the Stacktrace
at System.Data.Entity.Internal.InternalContext.SaveChanges()
at System.Data.Entity.Internal.LazyInternalContext.SaveChanges()
at System.Data.Entity.DbContext.SaveChanges()
at Globokas.SG.Controllers.AdminController.AddEditEntidadFinanciera(AddEditEntidad>FinancieraViewModel model) in D:\TFS_HL\Globokas.SG\Globokas.SG\Globokas.SG\Controllers\AdminController.cs:li>ne 2436
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();
}
}
}
I have a loop where I call stored procedure with different parameter value.
Next call cmd.ExecuteNonQuery();
I use transaction to save all or rollback, and checkBox2 - save always.
I found one problem and I can't find solution.
After first problem when catch block is fired transaction object loses its connection.
t.connection is null!
Everything is good but transaction object is without connection at start it has!
try
{
while (!sr.EndOfStream)
{
strLine.Remove(0, strLine.Length);
//c = sr.ReadLine();
while (c != "-")
{
c = sr.ReadLine();
strLine.Append(c );
if (sr.EndOfStream) break;
}
//strLine.Append("Nowa pozycja");
try
{
cmd.Parameters["#s"].Value = strLine.ToString();
cmd.Parameters["#Return_value"].Value = null;
cmd.ExecuteNonQuery();
}
catch
{
if (cmd.Parameters["#Return_value"].Value == null)
{
cmd.Parameters["#Return_value"].Value = -100;
}
if (((int)cmd.Parameters["#Return_value"].Value == 100) || (checkBox2.Checked))
{
if ((int)cmd.Parameters["#Return_value"].Value != 100)
{
MessageBox.Show("Są błedy! " + cmd.Parameters["#s"].Value);
};
}
}
if (!checkBox2.Checked)
{
if ((Int32)cmd.Parameters["#Return_value"].Value != 100)
{
break;
}
}
c = "";
}
textBox1.Text = strLine.ToString();
}
catch
{
// t.Rollback();
// t = null;
textBox1.Text = strLine.ToString();
textBox1.Visible = true;
MessageBox.Show("Wystąpiły problemy w czasie importu " + cmd.Parameters["#s"].Value);
//return;
}
finally
{
if (cmd.Parameters["#Return_value"].Value == null)
{
cmd.Parameters["#Return_value"].Value = -100;
}
if (((int)cmd.Parameters["#Return_value"].Value==100)||(checkBox2.Checked))
{
t.Commit();
if ((int)cmd.Parameters["#Return_value"].Value!=100)
{
MessageBox.Show("Transakcja zapisana ale w pliku były błedy! " + cmd.Parameters["#s"].Value);
};
}
else
{
if (t!=null) {t.Rollback();}
MessageBox.Show("Transakcja odrzucona!");
}
conn2.Close();
aFile.Close();
}
Ran into a similar issue. In my case it was happening for a specific SqlException. Most exceptions would be caught and handled just fine, but whenever I got a conversion error (such as trying to convert a string to a number) it would automatically end the transaction.
To fix this, I had to implement data checking (good idea anyway) prior to building/submitting the command object. Hope this helps others seeing this weird error.
I also met this odd problem (converting nvarchar to integer exception).
In my solution, I rebuild the transacton if found the underlying connection is null. But it's a dirty work.