Entity Framework check for DbUpdateException before SaveChanges - entity-framework

You will get a DbUpdateException if you try to save a string with length of 500 to a column in Sql Server which is a nvarchar(255).
Is there any way to check for this error before calling SaveChanges()? Maybe when adding the entity to context?

Is there any way to check for this error before calling SaveChanges? Maybe when adding the entity to context?
Yes. It is possible by calling this method GetValidationErrors() on your DbContext like below but you will get the validation errors result only if you make use of data annoations attributes on your entity classes
var validationResults = dbContext.GetValidationErrors();
validationResults will contain a collection of DbEntityValidationResult so if empty then your tracked entities are valid. Then calling SaveChanges just after will not throw exception about data validation but you can still get some others exceptions which can be checked only on server side e.g. concurrency exception, unique or reference constraint exception, etc.

Related

EntityFramework Insert fails due to Trigger and OptimisticConcurrencyException is thrown, how to handle it?

I have a trigger in DB that forbids inserting duplicated data. When I enter duplicated data, it adds nothing to the table, OptimisticConcurrencyException is thrown and I am swallowing (ignoring) this exception. When I try to enter new correct object, EF tries to run both INSERTs and it fails again on the first one.
How can I recover from this, all examples are discussing failed UPDATES, is there anything about INSERT? As I have read creating new DatabaseContext will solve the problem, but I cannot do it that way.
Creating a new DatabaseContext is always the best choice (since Hibernate).
In your case you need to remove the entity that caused the error from the context.
((IObjectContextAdapter)context).ObjectContext.Detach(entity);
You can ask ObjectContext to ignore all the changes after the ignorable exception is thrown. This way the added entity is marked Unchanged; hence in the next SaveChanges call, context won't consider it to manipulate DB:
(yourContextObject as IObjectContextAdapter).AcceptAllChanges();

Looping gets "EntityReference can have no more than one related object" error

I am receiving this error: "A relationship multiplicity constraint violation occurred: An EntityReference can have no more than one related object, but the query returned more than one related object. This is a non-recoverable error."
It happens during a loop through a set of objects, in this case when it gets to the second one in the loop. The error seems to indicate that one of the reference properties of the object in the list I am looping through is throwing this error. I know that the data is correct on the database side, so I'm not sure what Entity Framework is seeing here that it doesn't like.
This is the fluent configuration in question:
modelBuilder.Entity<TemplateSubscriber>()
.HasRequired(r => r.Role)
.WithOptional()
.Map(m => m.MapKey("RoleID"))
.WillCascadeOnDelete(false);
This particular configuration allowed me to get the correct SQL generated in my DbMigration, and I am happy with the database, however something must be wrong with how EF sees this relationship, because it throws this error when it tries to load up a second "TemplateSubscriber" in a loop, and that error is seen specifically on trying to load the "Role" reference property.
The relationship is a one to many, with the relationship accessible only from the one side. I have no need to access a list of the templateSubscribers from within the role. So what I wanted was a foreign key relationship so that the templateSubscriber must reference an actual role.
What could be the issue here? And how can I see what the SQL statement is that is erroring out, I would probably be able to diagnose if I could see the SQL.
This was the answer:
https://stackoverflow.com/a/9269397/1296464
Needed to change WithOptional() to WithMany()
It didn't make a change in the DB, but something under the hood is better now.
I had a similar issue. Mine was a result of using LazyLoading and not having all my navigation properties set to Overridable (c# virtual). This error occurred when having one end of the navigation set as Overridable and the other not.

How to add items to an entity in the ADO.NET Entity Framework

I am developing an application in C# by using the ADO.NET Entity Framework.
In many examples on the web I see that in order to add an element, newProduct, to an entity, let's assume Product, it is used the following code:
context.Products.Add(newProduct);
The method Add, however, it is not a member of Products so I cannot use it. Maybe the EF used in the examples is LinqToSQL.
However in ADO.NET there is a method AddObject:
context.AddObject("Products", newProduct)
and it works but I don't like it for two reasons:
1) I try to avoid as much as possible magic strings unless they are really the only resort to implement a functionality
2) It gives void as a return type, how can I check whether the insert was good or not?
I believe there is another way to insert my entities. Anybody might help?
1) I try to avoid as much as possible magic strings unless they are
really the only resort to implement a functionality
If context is an ObjectContext (EF <= 4.0), you should normally have a member in your derived context which represents the ObjectSet<Product> with the name Products or similar. You can use then:
context.Products.AddObject(newProduct);
Even if you don't have such a set in your context there is another strongly-typed option:
context.CreateObjectSet<Product>().AddObject(newProduct);
2) It gives void as a return type, how can I check whether the insert
was good or not?
AddObject does not perfrom the INSERT into the database at all. It only puts the object into Added state in the objectContext. The real INSERT happens later in a single transaction when you call SaveChanges.
Nonetheless, AddObject can fail, maybe if you add two objects with the same key into the context (if your entity does not have autogenerated identities for the key), or for other reasons. If so, AddObject will throw an exception which you shouldn't catch because it usually indicates a serious problem or bug in your code.
SaveChanges returns an int. But this int does not indicate that SaveChanges and inserting your object was successful. It only counts the number objects which are in Added state (will cause INSERT statement), in Modified state (will cause UPDATE statement) and in Deleted state (will cause DELETE statement) in the object context before the SQL statements get exceuted.
Again, if any of the SQL statements (like your INSERT) was not successful, SaveChanges will throw an exception. The exception can indicate problems already on client side or it can tell you that a problem during a SQL operation occured - for example: For a failed INSERT the exception might give you a message that the INSERT failed because there was already a row with the key you want to insert in the database or that required non-nullable columns are not filled in the entity you want to insert, etc. Also exceptions due to concurrency issues are possible among a lot of other exception types.
You can check if SaveChanges succeeded by catching possible exceptions:
try
{
int numberOfObjects = context.SaveChanges();
}
catch (SomeExceptionType e)
{
// What now?
}
BTW: The context.Products.Add(...) you've seen was most likely an example where context is a DbContext (EF >= 4.1). DbContext is a simplified API to Entity Framework (which still uses the core ObjectContext under the covers). In this API the method to insert a new entity is indeed called Add (method of DbSet<T>) and not AddObject.

jpa concurrency CMT exception handling

I use EntityManager to save/update my entities in database and Hibernate as jpa provider. My transactions are handled by container.
The problem: I need to add an entity that might have been already stored in database, so an exception will be thrown. In this case I need to repeat insertion but with another value. But as long as an exception is thrown the session has gone bad and I need to create a new session and rollback the transaction. How can I do this when I'm using CMT? Or if there is another way to do this?
Thank you.
You could use the TransactionAttribute(REQUIRES_NEW) for your persistence method. If the bean invoking your method will catch an exception, it might do some changes and invoke the method once again.
This will rollback just the persistence-method transaction - not the invoking bean one.
However, remember that if your Use Case doesn't require you to do EntityManager#persistence(-), you might be interested in EntityManager#merge(-) operation. It will persist the entity if it doesn't already exist or update it if it already exists (the existence is checked based on the PK).
HTH.
You might want to use EntityManager#find(Class, PK) to check for an already persisted entity. A null result means there's no such entity ( ----> persist(.) ), otherwise you update with the merge(.) method.

Removing an entity, but using the same primary key to add a similar entity after the removal

Im trying to remove an entity which has a unique PK like : 80253
I remove this entity by doing the follow lines of code:
myEntityType1 = getEntityManager().find(MyEntityType1.class, 80253);
getEntityManager().remove(myEntityType1);
getEntityManager().flush();
These bits of code actually deletes the rows from my database and all its cascading objects properly, and Im very happy about this. Now the problem occurs when I now need to create a similar entity that uses the same primary key (which should now be gone right?).
MyEntityType2 myEntityType2 = new MyEntityType2 ();
myEntityType2.copyData(myEntityType1); //access the data from the other object
//and retrieves the id 80253. myEntityType2 now has 80253 as ID.
getEntitymanager().persist(myEntityType2);
Now this is where I get a unique constraint SQL error. Im trying to insert a ID which already exists and the changes are automatically rolled back (the old entity is no longer deleted). This happens after I see in my logger that toplink has deleted the records of the old entity.
Does anyone know how this happens, and why its not working? For the record Ive tried merging, closing, clearing of the entityManager, but nothing seems to work.
It seems to me that JPA might do some bad caching or something. I hope someone has a good answer for me! =)
Update: Theres no longer an issue with unique ID constraints, but I create a new subclass with the same primary key which has been deleted I get the following exception:
Exception Description: Trying to invoke [setApprovedWhen] on the object [null]. The
number of actual and formal parameters differs, or an unwrapping conversion has failed.
Internal Exception: java.lang.IllegalArgumentException: object is not an instance of
declaring class
It seems to me that it wont let me change the object into a different subclass?
EDIT:
Try with explicitly start and commit transaction.
Deleting an entity is as simple as
calling EntityManager method
remove(Object entity) as the following
example shows. The entity you delete
must be managed: that is, it must have
been previously read in the current
persistence context.
entityManager.getTransaction().begin();
myEntityType1 = getEntityManager().find(MyEntityType1.class, 80253);
getEntityManager().remove(myEntityType1);
entityManager.getTransaction().commit();
When the transaction is completed, or
you call EntityManager method flush(),
the entity will be deleted.
In a container managed persistence
context the transaction boundaries
will be controlled by the container.