none
Update Does not affect the database RRS feed

  • Question

  • Sorry but here is another update question in a disconnected context.

    I have read some threads and googled around ... but did not get a way around that...

    Maybe someone could have a look at this.

     

    I use linq for DataAccess.

    I have different classes as BO's.

     

    For Example Objects.

     

    Contact

    ID

    Name

    LastName

    List<Address>

    Timestamp

     

    Address

    ID

    Street

    City

    Zip

     

    Then I have almost the same linq-to-sql classes

     

    dbContact

    and dbAddress

     

    I have a Method that Maps the Contact to the dbContact

     

    and my update szenario looks like this.

     

    public void SaveContact(Contact contact)

    {

    try

    {

    using (var dbx = new DataContext())

    {

    dbContact c = MapContact(contact);

    // see if Contact exists

    var existingContact =dbx.dbContacts.SingleOrDefault(x => x.RecordGUID == contact.ID);

    // if exists attach to update

    if (existingContact != null)

    {

    existingContact = c;

    //dbx.dbContacts.Attach(c, existingContact);

    }

    else

    {

    // insert or update

    dbx.dbContacts.InsertOnSubmit(c);

    }

    // submit changes to the Database

    dbx.SubmitChanges();

    }

    }

    catch (Exception ex)

    {

    throw;

    }

    }

     

    This update does not effect the database the changes from c are not submitted even thogh the existingContact is changed... and no exception is thrown nor any other error...

     

    so what does...

    existingContact = c;

    to the context and how could I handle something like that?

    //dbx.dbContacts.Attach(c, existingContact);

    the commented line does not work.. it says something like cannot add a row which already exists or something...

     

    Any help would be great...

     

    Regards

    F.

     

    Friday, July 25, 2008 3:38 PM

All replies

  • Let's break down your code for fetching an object and updating it.

     

    dbContact c = MapContact(contact);

    // see if Contact exists

    var existingContact =dbx.dbContacts.SingleOrDefault(x => x.RecordGUID == contact.ID);

     

    Ok, so now you have a contact from the database in memory.

     

    // if exists attach to update

    if (existingContact != null)

    {

    existingContact = c;

     

    Now you REPLACE the in memory contact with the one from your UI tier. You're not changing the pointer that the context knows about to the new object, but throwing that one away. The context still only knows about old "existingContact"

     

    //dbx.dbContacts.Attach(c, existingContact);

     

    Since c and existingContact are the same thing, and the context doesn't know about either any more, you can't attach it. You need to eliminate the "existingContact=c" line and use the following for your attach:

     

    dbx.dbContacts.Attach(c, existingContact);

     

    Realize when doing this that you are loosing some concurrency checks because you are not really checking the original value, but the current database value which may have changed after the user originally fetched their working object. One work around for this is to use a timestamp for a rowversion.

     

    Jim Wooley

    www.ThinqLinq.com

    Friday, July 25, 2008 6:23 PM
    Moderator
  • Hi Jim,

    Thanks for the reply

     

    I have tried

    "dbx.dbContacts.Attach(c, existingContact);"

    without "existingContact = c;"

     

    And I got an exception:

    System.Data.Linq.DuplicateKeyException: Cannot add an entity with a key that is already in use

     

    -------

    "existingContact = c;" without the Attach part does not throw but will also not update the db.

     

    ----

    I went further and implemented a method to check if a contact exists in a different context

     

    public bool CheckIfContactExists(dbContact c)

    {

    using (var ctx = new kontakteDataContext())

    {

    var existingContact = ctx.dbContacts.SingleOrDefault(x => x.RecordGUID == c.RecordGUID);

    if (existingContact != null)

    {

    return true;

    }

    else

    {

    return false;

    }

    }

    }

     

    this is called in SaveContact in a different Context...

     

    public void SaveContact(Contact contact)

    {

    try

    {

     

    using (var dbx = new kontakteDataContext())

    {

    dbContact c = MapContact(contact);

    // if exists attach to update

    if (CheckIfContactExists(c))

    {

    dbx.dbContacts.Attach(c);

    //dbx.dbContacts.Attach(c, existingContact);

    }

    else

    {

    // insert or update

    dbx.dbContacts.InsertOnSubmit(c);

    }

    // submit changes to the Database

    dbx.SubmitChanges();

    }

    }

    catch (Exception ex)

    {

    throw;

    }

     

    }

    this does also not effect the db...

    --------------------------------------------

     

    this is very confusing...

    I have also integrated a timestamp col in the db... when I try this

    dbx.dbContacts.InsertOnSubmit(c);

    I got following exception

    {System.Data.Linq.ChangeConflictException: Row not found or changed

    even when I used the approach with checking the contact in a different context...

     

    I am still confused...   

    Friday, July 25, 2008 7:34 PM
  • It sounds like the existing object is still attached to another context (even if that context is no longer in scope). In that case, you can't attach it to a new context. Also, on your checking to see if the object exists, don't use yet another context. You are likely getting the contexts confused in terms of their identity and change tracking infrastructures.

     

    Jim Wooley

    www.ThinqLinq.com

    Friday, July 25, 2008 7:40 PM
    Moderator
  • ok, since another context is not a good idea I switched back to my first approach checking in the same context...

    ...

    in the same context...

    dbx.dbContacts.Attach(c,true);

    [System.Data.Linq.DuplicateKeyException] = {"Cannot add an entity with a key that is already in use."}

     

    ... what should I do to get this to work ... maybe a total different approach any idea?

     

    I have tried

     

    existingContact = c;

    dbx.dbContacts.Attach(c,true);

    dbx.dbContacts.Attach(c, existingContact);

     

    even querying in a different context... but no chance ... how is this possible .. I have a timespan col included

     

     

    How can I solve this...

    Friday, July 25, 2008 8:23 PM
  • If you have a timestamp with the rowversion set on the mapping then you should use the attach(c,true) and not worry about the existing contact at all.

     

    At this point, the question seems to be either how you are fetching the records, and what you are doing with them between fetching and trying to attach. If you are using a different context to fetch and update, you will either need to fetch them without the tracking enabled or serialize the object.

     

    You may want to check to make sure that the ID value is retained properly prior to the attach. Make sure it still has a value and didn't revert to a default value.

     

    Jim

    www.ThinqLinq.com

     

    Friday, July 25, 2008 8:33 PM
    Moderator
  •  Jim Wooley wrote:

    If you have a timestamp with the rowversion set on the mapping then you should use the attach(c,true) and not worry about the existing contact at all.

    >>>> I have tried this gives me a {System.Data.Linq.DuplicateKeyException: Cannot add an entity with a key that is already in use. With timestamp and row version

     

     

    At this point, the question seems to be either how you are fetching the records, and what you are doing with them between fetching and trying to attach. If you are using a different context to fetch and update, you will either need to fetch them without the tracking enabled or serialize the object

    >>> right now I have some unit test I fetch them as and then they are mapped to my BO (not the linq-to-sql classes) I do some changes on the BO and then I pass them to my repository SaveContact method... then I map my BO on a new created dbContact (linq-to-sql class) and check with the id (guid) if the contact already exists, if not I am inserting the newly generated and mapped contact... if it's in the db ... there comes my problem... see above... No serialization is used (yet) because I am not at that point for implementing wcf...

     

     

    You may want to check to make sure that the ID value is retained properly prior to the attach. Make sure it still has a value and didn't revert to a default value.

    >>> I checked the the id's in debugger and it stil looks good...

     

    Jim

    www.ThinqLinq.com

     

     

    Thanks for taking the time...

    Friday, July 25, 2008 8:58 PM
  • >>> right now I have some unit test I fetch them as and then they are mapped to my BO (not the linq-to-sql classes) I do some changes on the BO and then I pass them to my repository SaveContact method... then I map my BO on a new created dbContact (linq-to-sql class) and check with the id (guid) if the contact already exists, if not I am inserting the newly generated and mapped contact... if it's in the db ... there comes my problem... see above... No serialization is used (yet) because I am not at that point for implementing wcf...

     

    Ok, there's your problem. You are trying to attach an object to a second context when it is still associated with another context. To use attach, you need to either fetch it with dc.ObjectTrackingEnabled = false, or fully serialize the object before trying to attach it to the second context.

     

    Jim

    www.ThinqLinq.com

    Friday, July 25, 2008 9:05 PM
    Moderator
  • Hi Jim,

     

    thanks for your response I thought about it and tryed reworked my code .... but....

    this will break the concept I am using... I try to work along this series of screencasts ( http://blog.wekeroad.com/mvc-storefront/ ) and try to adopt the way he uses IQueryable in the Repository...

    the way he is using it discussed here (http://weblogs.asp.net/fredriknormen/archive/2008/04/24/what-purpose-does-the-repository-pattern-have.aspx) ... when I put a using block around it to dispose my context ... the Iqueryable stuff doesn't work since it is dettached from the context and cannot work anymore (exception)... what do you think of this way...

     

    F.

     

    Saturday, July 26, 2008 6:48 PM