locked
UpdateObject() / AddObject issues RRS feed

  • Question

  • I just got done reading Danny's excellent article here and I just tried to incorporate that logic into my own ObjectDataSource.

    When I call db.AddObject(), the entity's ID gets reset to 0.  This doesn't seem right at all. :)  Is there something I could be overlooking?

    Thanks for any help!

    (Have I mentioned yet that this is AWESOME STUFF?  I haven't been this excited about programming in years!)

    Mike

    Thursday, August 31, 2006 6:37 AM

Answers

  • Yeah, that's messy.  Our CTP has missing functionality.

    AddObject assumes you're adding an object to the context to add that object to the database.

    AddExistingObject (that you need that we don't yet support) assumes you're adding an object to the context that already exists in the database.

    AddObject (when ClientAutoGenerated keys are on) intentionally wipes over your key values.  AddExistingObject would not wipe over your key values.   For now you could encapsualte your workaround above in a helper function or turn ClientAutoGenerated keys off.


    This posting is provided "AS IS" with no warranties, and confers no rights.

    Monday, September 11, 2006 11:11 PM

All replies

  • I'm really glad that you are so excited and engaged in what we are building.  That's a huge encouragement when we're in crunch mode trying to get this thing done.  ;-)

    Could you share a subset of your .csdl file (the part describing the type you are adding) plus the code right around where you are calling AddObject?  I'd certainly like to look into this to see what's happening.

    - Danny

    Thursday, August 31, 2006 12:06 PM
  • I'm actually just using the Northwind csdl that was released in the CTP.  I'm creating a simple CRUD manager for entities and ObjectDataSource.  So the only thing that's "different" is that the entity gets passed into "Update" through ObjectDataSource after it news up the Product (that's the entity I'm using as a test).

    <EntityType Name="Product" Key="ProductID">

    <Property Name="ProductID" Type="Int32" Nullable="false" StoreGenerated="true" ClientAutoGenerated="true" />

    <Property Name="ProductName" Type="String" Nullable="false" MaxLength="40" ConcurrencyMode="fixed"/>

    <!-- this is temporarily set to non-nullable to avoid an issue with overload

    resolution in the compilers -->

    <!--<Property Name="CategoryID" Type="Int32" Nullable="false" />-->

    <Property Name="UnitPrice" Type="Decimal" Nullable="true" Precision="10" Scale="2" ConcurrencyMode="fixed"/>

    <NavigationProperty Name="Category" Relationship="Self.Category_Product" FromRole="Product" ToRole="Category"/>

    </EntityType>

     

    And here is the code:

     

    [DataObjectMethod( DataObjectMethodType.Update )]

    public void Update( T entity )

    {

    U context = ObjectContextHelper.CreateContext<U>();

    // step 1: Add the object to the cache.

    context.AddObject( entity );

    // step 2: Get the cache entry and AcceptChanges so

    // that it's no longer in the Added state.

    CacheEntry entry = context.Cache.GetCacheEntry( entity.Key );

    entry.AcceptChanges();

    // step 3: Tell the cache which properties are modified.

    // NOTE: This also sets the overall state to Modified.

    entry.SetModified( "ProductName" );

    entry.SetModified( "ProductID" );

     

    // step 4: Set the original values of the concurrency

    // tokens. In this case we “refresh” from the

    // server so that the current values will over-

    // write whatever is on the server.

    context.Refresh( RefreshMode.ClientWins, entity );

    // step 5: Do the SaveChanges.

    context.SaveChanges();

    }

     

    You'll notice I'm using generics for the Entity and ObjectContext type.  ObjectContextHelper is a class that pretty's up the connection string (allows support for "~", etc) before it creates the context with the specified type.

     

    Here is the error I get:

    The following entities have have not been refreshed because they were not in the store.
    'EntitySet=Products;ProductID=-1'

    Thursday, August 31, 2006 1:45 PM
  • Ok.  Found the problem.

    The problem is that even though the values for the ProductID is set, the actual Key property is null.  If I put the following line in before the AddObject is called:

    entity.Key =

    context.CreateKey( typeof( T ),

    new KeyValuePair<string, object>[] { new KeyValuePair<string, object>( "ProductID", 3 ) } );

    Then it works.  However, that sure is messy.  ;)  Is there a way that you can get an entity to resolve its own key and set it to itself?

    Thursday, August 31, 2006 3:33 PM
  • Yeah, that's messy.  Our CTP has missing functionality.

    AddObject assumes you're adding an object to the context to add that object to the database.

    AddExistingObject (that you need that we don't yet support) assumes you're adding an object to the context that already exists in the database.

    AddObject (when ClientAutoGenerated keys are on) intentionally wipes over your key values.  AddExistingObject would not wipe over your key values.   For now you could encapsualte your workaround above in a helper function or turn ClientAutoGenerated keys off.


    This posting is provided "AS IS" with no warranties, and confers no rights.

    Monday, September 11, 2006 11:11 PM
  • Sorry I never got back to this thread Mike.  Ben is right that the basic issue you have encountered is the fact that we don't currently have an API explicitly designed to "attach" an object to the context when you know that it already exists in the store.  The blog posting I made a few weeks ago on this topic gave a mechanism for simulating this attach by doing an add and then calling accept changes to put the entry into "unchanged" state instead of added, but as Ben has noted if you have your key set for ClientAutoGen, then the add method will overwrite your key values unless the EntityKey property is filled out.  This scenario is on our list to look into, and we would probably address it by adding an additional method, maybe called AddExistingObject that would do the equivalent of add and accept changes in a single step (and would never overwrite your key values).

    - Danny

    Tuesday, September 12, 2006 4:24 AM