how to ignore errors in SaveChange EF4 - c#

Please see to example1. If some of the data will be entered incorrectly, EF4 will not survive nor any record.
The question: whether as a force to ignore an error in one record and continue on.
example1:
foreach (var tag in split)
{
context.NameToResourcer.AddObject(new NameToResourcer()
{
id_resource = resource.id,
name = tag
});
}
context.NameToResourcer.AddObject(new NameToResourcer()
{
id_resource = resource.id,
name = ExtractDomainNameFromURL(resource.url)
});
try
{
context.SaveChanges();
}
catch (UpdateException ex)
{
}
catch (Exception ex)
{
throw;
}
example2 alternative:
foreach (var tag in split)
{
try
{
context.NameToResourcer.AddObject(new NameToResourcer()
{
id_resource = resource.id,
name = tag
});
context.SaveChanges();
}
catch (UpdateException ex)
{
}
}
try
{
context.NameToResourcer.AddObject(new NameToResourcer()
{
id_resource = resource.id,
name = ExtractDomainNameFromURL(resource.url)
});
context.SaveChanges();
}
catch (UpdateException ex)
{
}

Context behaves like unit of work. It means that when you modify data and store them with the single call to SaveChanges you are telling EF that you want atomic operation - either all changes are successfully saved or all changes are rolled back. EF use a transaction internally to support this behavior. If you don't want this behavior you cannot save all data with single call to SaveChanges. You must use separate call for each atomic set of data.

One possible solution is to disable validation on saving.But I don't recommend it.
db.Configuration.ValidateOnSaveEnabled = false;

Related

Getting all errors on an SaveChanges() within EF Core

All,
I need to show all validation errors within EF Core. So for example
I get data by doing this :
foreach (var item in FRFT)
{
var FinancialReportingFrameworkType = new FinancialReportingFrameworkType { FinancialReportingFrameworkTypeId = item.Frfid, Acronym = item.Frf1, Name = item.Frfdescription };
_clDBContext.FinancialReportingFrameworkType.Add(FinancialReportingFrameworkType);
}
Then I want to push this data into another table but there may be some vlaidation errors. I need to track the errors.
If I do a try .. catch block, I only recieve the first error but there maybe an error on every single row I push into the databse.
_clDBContext.SaveChanges();
}
catch (DbUpdateException e)
{
if (e.InnerException != null)
{
// to be chnged to an POST HTML error code
Console.WriteLine(e.InnerException.Message);
return new OkObjectResult(e.InnerException.Message);
}
I need to do some pre-Save Changes valdiation.
Any ideas ?

How to skip invalid fields on SaveChanges

Is it possible to skip invalid values when I'm saving an entity with SaveChanges and save only valid fields?
It's pretty simple to skip on entities level, but I'm not able to find a way of doing that on fields level.
Muhammad's answer gave me great idea. The solution is pretty simple:
try
{
context.Entry(objInDB).State = EntityState.Modified;
context.SaveChanges();
}
catch (Exception ex)
{
var exception = ex as DbEntityValidationException;
if (exception != null)
{
exception.EntityValidationErrors.ToList().ForEach(error =>
{
error.ValidationErrors.ToList().ForEach(validationError =>
{
error.Entry.Property(validationError.PropertyName).IsModified = false;
});
});
context.SaveChanges();
}
}

aggregate exceptions with entity framework in dll

At work we are looking to move to an ORM (still using an access database with ADO!) I started building with entity framework and everything was going smoothly until I separated it off into it's own .dll (so we could have the website/crm/production/barcoding systems all using the same database logic).
The issue comes with handling the DbEntityValidationExceptions, my initial test code (which worked)
public override int SaveChanges(System.Data.Objects.SaveOptions options)
{
try{return base.SaveChanges(options);}
catch (DbEntityValidationException dbEx)
{
foreach (var validationErrors in dbEx.EntityValidationErrors)
{
foreach (var validationError in validationErrors.ValidationErrors)
{
Trace.TraceInformation("Class: {0}, Property: {1}, Error: {2}",
validationErrors.Entry.Entity.GetType().FullName,
validationError.PropertyName,
validationError.ErrorMessage);
}
}
//handle here
throw;
}
}
but this doesn't get caught now and i'm left handling a generic threadException. Is there any way to access the original data (cast the threadException?) or is there a different approach I should take. I think I just need a push in the right direction and I can figure something out.
Regards, Pete
UPDATE:
Hmm bit of an issue calling the GetValidationErrors. I notice that my database Context has the baseClass of ObjectContext and not DbContext (So I can't call the ValidateEntity). I'm using Entity Framework 5 with default code generation enabled - using a database first approach if that helps.
check for validation errors before calling SaveChanges. Something like...
var errors = context.GetValidationErrors();
if(errors.Any())
{
//handle validation errors
}
else
{
context.SaveChanges();
}
from memory, so the exact syntax may not be correct.
this is what I ended up going with in the end (i'll probably end up fleshing it out as I learn more about EF)
public List<DbEntityValidationException> vErrors = new List<DbEntityValidationException>();
public int DbChanges = 0;
public bool SaveChanges()
{
try
{
this.vErrors = (List<DbEntityValidationException>)base.GetValidationErrors();
if (this.vErrors.Count == 0)
{
this.DbChanges = base.SaveChanges();
return true;
}
}
catch (Exception Ex)
{
this.vErrors.Add(new DbEntityValidationException(string.Format("General Error: {0}", Ex.GetType().ToString())));
}
return false;
}
and from code
using(Db db = new Db())
{
//changes
if(db.SaveChanges)
{
//some message using db.DbChanges
}
else
{
//handle errors in db.vErrors
}

Handling duplicate insertion

So I've got this piece of code which, logically should work but Entity Framework is behaving unexpectedly.
Here:
foreach (SomeClass someobject in allObjects)
{
Supplier supplier = new Supplier();
supplier.primary_key = someobject.id;
supplier.name = someobject.displayname;
try
{
sm.Add(supplier);
ro.Created++;
}
catch (Exception ex)
{
ro.Error++;
}
}
Here's what I have in sm.Add()
public Supplier Add(Supplier supplier)
{
try
{
_ctx.AddToSupplier(supplier);
_ctx.SaveChanges();
return supplier;
}
catch (Exception ex)
{
throw;
}
}
I can have records in allObjects that have the same id. My piece of code needs to support this and just move on to the next and try to insert it, which I think should work.
If this happens, an exception is throw, saying that records with dupe PKs cannot be inserted (of course). The exception mentions the value of the PK, for example 1000.
All is well, a new supplier is passed to sm.Add() containing a PK that's never been used before. (1001)
Weirdly though, when doing SaveChanges(), EF will whine about not being able to insert records with dupe PKs. The exception still mentions 1000 even though supplier contains 1001 in primary_key.
I feel this is me not using _ctx properly. Do I need to call something else to sync it ?
Found it, had to change something in the Add() method:
public Supplier Add(Supplier supplier)
{
try
{
_ctx.AddToSupplier(supplier);
_ctx.SaveChanges();
return supplier;
}
catch (Exception ex)
{
_ctx.Supplier.Detach(supplier);
throw;
}
}

How to rollback a transaction in Entity Framework

string[] usersToAdd = new string[] { "asd", "asdert", "gasdff6" };
using (Entities context = new Entities())
{
foreach (string user in usersToAdd)
{
context.AddToUsers(new User { Name = user });
}
try
{
context.SaveChanges(); //Exception thrown: user 'gasdff6' already exist.
}
catch (Exception e)
{
//Roll back all changes including the two previous users.
}
Or maybe this is done automatically, meaning that if error occurs, committing changes are canceled for all the changes.
is it?
OK
I created a sample a application like the example from the the question and afterwords I checked in the DB and no users were added.
Conclusion: ObjectContext.SaveChange it's automatically a transaction.
Note: I believe transactions will be needed if executing sprocs etc.
I believe (but I am no long time expert in EF) that until the call to context.SaveChanges goes through, the transaction is not started. I'd expect an Exception from that call would automatically rollback any transaction it started.
Alternatives (in case you want to be in control of the transaction) [from J.Lerman's "Programming Entity Framework" O'Reilly, pg. 618]
using (var transaction = new System.Transactions.TransactionScope())
{
try
{
context.SaveChanges();
transaction.Complete();
context.AcceptAllChanges();
}
catch(OptimisticConcurrencyException e)
{
//Handle the exception
context.SaveChanges();
}
}
or
bool saved = false;
using (var transaction = new System.Transactions.TransactionScope())
{
try
{
context.SaveChanges();
saved = true;
}
catch(OptimisticConcurrencyException e)
{
//Handle the exception
context.SaveChanges();
}
finally
{
if(saved)
{
transaction.Complete();
context.AcceptAllChanges();
}
}
}

Categories

Resources