locked
undo changes in entity framework RRS feed

  • Question

  • Hi,

    i develop a project at vs2012 EXPRESS(c#,entity framework ver:5.0).

    assume i have 2 tables in SQLSERVER as

    table1 {column1(varchar(5))  ,primarykey1(int)}

    table2  {column2(varchar(5))  ,primarykey2(int)}

    and when i run the code(create table1 and table2 objects and insert them DB in a transaction):

    Entities ent =new Entities(); private void button_click() { try { using(var ts=new transactionscope()) { table1 t1=new table1(); t1.column1="123456";//exception raises due to length limit t1.primarykey1=1; ent.table1.add(t1); ent.savechanges();

    table2 t2=new table2(); t2.column2="123456"; t2.primarykey2=1; ent.table2.add(t2); ent.savechanges();

    ts.complete(); } } catch(exception ex) {messagebox.show(ex.message)} }


    now,when the use click on the button, it creates table1 object but first ent.savechanges raises an exception becouse of the value(123456) assigned to column1. So we cannot reach to transaction complete part, thus nothing inserted to DB.

    But if user click the button again. i got the below. 

    [System.InvalidOperationException] = {"The changes to the database were committed successfully, but an error occurred while updating the object context. The ObjectContext might be in an inconsistent state. Inner exception message: AcceptChanges cannot continue because the object's key values c...

    and also i realized that the EntityValidationErrors occured by  the first button click are still there. So i think i have to reset or undo changes of entities instance after first click. But i found that most easiest way for it create entities for every button click

    like 

    private void button_click() {

    Entities ent =new Entities();

    instead of creating it once,create for every button_click event.

    İs this the best solution,how can i undo changes and make it as if entities instance just created in an easy way?(i saw something like

    db.Entry<table1>(t1).Reload(); 

    )

    but i could not figure out how can i use it.

    and also i want the learn what is the meanning of that exception( {"The changes to the database were committed successfully, but....) why it occurs?

    Thank you already

    Karynch

    t1.column1="123456";
    t1.primarykey1=1;
    ent.table1.add(t1);
    ent.savechanges();
    Tuesday, February 19, 2013 8:00 AM

Answers

  • Entity Framework maintains an internal collection of all the objects that have been queried by it or added to it. The ObjectStateManager.

    When you add the object to the context the first time it is first added to the ObjectStateManager, then when you call SaveChanges EF attempts to commit anything in the ObjectStateManager to the database. This is EFs implementation of the unit of work pattern, you make all your changes then commit the context when you're ready.

    In your first run EF first adds the entity to the ObjectStateManager with a state of Added. When you call SaveChanges EF commits the entity to the database and then calls AcceptAllChanges on the ObjectStateManager which will set the entity to the state of Unchanged, since the entity now matches what is in the database.

    You now rollback your transaction without EF knowing anything about it.

    In the second run the same thing happens, except this time when EF tries to update the OSM it realizes that there is already an entity in the OSM with the same key as the one it just successfully inserted into the database, which it knows shouldn't be possible. So the OSM will throw saying that the keys conflict and it doesn't know how to resolve it. At this point you have two entities in the OSM, one in the unchanged state and the other in the added state.

    In the third run you add a third entity to the OSM, now you have two entities in the added state, so when you call SaveChanges EF will do an insert for both of them and the database will throw a key violation exception which is passed back to you and halts the operations.

    The resolution to all of this is usually to not share the context between these operations, or re-create it in the exception handler. Does that make sense?


    We are seeing a lot of great Entity Framework questions (and answers) from the community on Stack Overflow. As a result, our team is going to spend more time reading and answering questions posted on Stack Overflow. We would encourage you to post questions on Stack Overflow using the entity-framework tag. We will also continue to monitor the Entity Framework forum.

    Friday, February 22, 2013 1:23 AM

All replies

  • i want to change the code as below,please ignore what i have written above.  In DB i have a table1 which has a just one column which is also primary key.

    at the first click nothing happens.

    at the second click i got message:

    [System.InvalidOperationException] = {"The changes to the database were committed successfully, but an error occurred while updating the object context. The ObjectContext might be in an inconsistent state. Inner exception message: AcceptChanges cannot continue because the object's key values c...

    at the third i got message:

    violation of primarykey constraint...

    at the end ,naturally nothing is written to DB.

    Why do we get these errors,how can we get rid of them?

    Entities ent =new Entities();
    
    private void  button_click()
    
    {
    
    try
    
    {
    
    using(var ts=new transactionscope())
    
    {
    
    table1 t1=new table1();
    
    t1.primarykey1=1;
    ent.table1.add(t1);
    ent.savechanges();
    return;//it is for the sake of simplify
    ts.complete();
    }
    
    }
    
    catch(exception ex)
    
    {messagebox.show(ex.message)}
    
    }

    Tuesday, February 19, 2013 9:00 PM
  • On 2/19/2013 4:00 PM, karynch wrote:

    i want to change the code as below,please ignore what i have written above.  In DB i have a table1 which has a just one column which is also primary key.

    at the first click nothing happens.

    at the second click i got message:

    [System.InvalidOperationException] = {"The changes to the database were committed successfully, but an error occurred while updating the object context. The ObjectContext might be in an inconsistent state. Inner exception message: AcceptChanges cannot continue because the object's key values c...

    at the third i got message:

    violation of primarykey constraint...

    at the end ,naturally nothing is written to DB.
    Why do we get these errors,how can we get rid of them?

    Entities ent =new Entities();
    
    private void    button_click()
    
    {
    
    try
    
    {
    
    using(var ts=new transactionscope())
    
    {
    
    table1 t1=new table1();
    
    t1.primarykey1=1;
    ent.table1.add(t1);
    ent.savechanges();
    return;//it is for the sake of simplify
    ts.complete();
    }
    
    }
    
    catch(exception ex)
    
    {messagebox.show(ex.message)}
    
    }



    What is the primaykey defined as? Is it an Identity column with auto increment? If that is the case, then you can't set t1.primarykey1=1 because SQL Server is going to set that key automatically and return that new key to t1.primarykey1. If you want to add t1 as a new object to the database in this situation, then t1.primarykey=0 tells EF that the object is to be added, and the key to the object will be assigned by SQL Server and populated back to t1.primarykey. The property will be 0 when you did the "new" and you don't touch the key property period when doing an add.

    On the other hand, if not using an Identity column type for the primaykey and it's just an int property, then how do you know that a record doesn't already exist in the database table with a primary-key of 1?

    If you try to add an object with primary-key of 1 and there is already an object in the database or in virtual memory with a key of 1, then what do you think is going to happen, other than, an exception is going to be thrown?

    Tuesday, February 19, 2013 10:15 PM
  • i am not using it as an identity column,just an int property. let assume ther is no record which  has a primarykey valued 1.

    let me tell you what i know(or think i know) what is going on at the background.

    when we create a new table1 object and add it to ent;we insert a record virtually not to insert into DB.

    So at the second click we create a new object with same primary key and insert it to ent.

    At that point ,system controls primarykey constaint violation. 

    if it controls it virtually; -in my opinion- it has to give an exception which says primary key violation occured in the contex not in the DB.

    if it controls it from DB; becouse of there is no record at DB yet;it does not give an exception.

    when i really confused is at the third click;

    although i apply same code(create new record with same primary key and add it);the excption mesage changes which says

    violation of pkey constaint... So i wonder why the message changed??

    And not to get an exception how can i remove the changes i did(add a new object)? so at the second or third clicks i do not get any exception,becouse either virtually or in real DB ,all is clear.

    Wednesday, February 20, 2013 7:39 AM
  • On 2/20/2013 2:39 AM, karynch wrote:

    <snipped>

    Maybe you need to do an AcceptAllChanges() while in a transactional state.

    <http://msdn.microsoft.com/en-us/library/system.data.objects.objectcontext.acceptallchanges.aspx>

    Wednesday, February 20, 2013 1:59 PM
  • i tried  the code =>    ((IObjectContextAdapter)ent).ObjectContext.AcceptAllChanges();

    before return statement

    and

    before: table1 t1=new table1();

    and in a finally block ,but the result is same.

    i think i need something undo the move which added an object to entity.

    when anything is wrong and cannot complete transaction , i should undo the changes i did.

    Wednesday, February 20, 2013 11:19 PM
  • On 2/20/2013 6:19 PM, karynch wrote:

    i tried  the code =>    ((IObjectContextAdapter)ent).ObjectContext.AcceptAllChanges();

    before return statement
    and

    before:    table1 t1=new table1();and in a finally block ,but the result is same.i think i need something undo the move which added an object to entity.when anything is wrong and cannot complete transaction , i should undo the changes i did.

    Maybe you just need to use the backdoor on EF and do it yourself or use one of the other commands.
    <http://blogs.msdn.com/b/alexj/archive/2009/11/07/tip-41-how-to-execute-t-sql-directly-against-the-database.aspx>

    <http://blogs.msdn.com/b/alexj/archive/2009/03/26/index-of-tips.aspx>

    Wednesday, February 20, 2013 11:44 PM
  • Entity Framework maintains an internal collection of all the objects that have been queried by it or added to it. The ObjectStateManager.

    When you add the object to the context the first time it is first added to the ObjectStateManager, then when you call SaveChanges EF attempts to commit anything in the ObjectStateManager to the database. This is EFs implementation of the unit of work pattern, you make all your changes then commit the context when you're ready.

    In your first run EF first adds the entity to the ObjectStateManager with a state of Added. When you call SaveChanges EF commits the entity to the database and then calls AcceptAllChanges on the ObjectStateManager which will set the entity to the state of Unchanged, since the entity now matches what is in the database.

    You now rollback your transaction without EF knowing anything about it.

    In the second run the same thing happens, except this time when EF tries to update the OSM it realizes that there is already an entity in the OSM with the same key as the one it just successfully inserted into the database, which it knows shouldn't be possible. So the OSM will throw saying that the keys conflict and it doesn't know how to resolve it. At this point you have two entities in the OSM, one in the unchanged state and the other in the added state.

    In the third run you add a third entity to the OSM, now you have two entities in the added state, so when you call SaveChanges EF will do an insert for both of them and the database will throw a key violation exception which is passed back to you and halts the operations.

    The resolution to all of this is usually to not share the context between these operations, or re-create it in the exception handler. Does that make sense?


    We are seeing a lot of great Entity Framework questions (and answers) from the community on Stack Overflow. As a result, our team is going to spend more time reading and answering questions posted on Stack Overflow. We would encourage you to post questions on Stack Overflow using the entity-framework tag. We will also continue to monitor the Entity Framework forum.

    Friday, February 22, 2013 1:23 AM