Udostępnij za pośrednictwem


How to Apply Changes Made to a Detached Object without passing it or retrieving it in .NET Framework 3.5 ADO.NET Entity Framework

If you are using .NET Framework 3.5 ADO.NET Entity Framework inside a Web Service the solution proposed at MSDN in How to Apply changes to a Detached object may not be the best solution.

How to: Apply Changes Made to a Detached Object (Entity Framework)

Both approaches has the following disadvantages:

  • Pass original and updated item to apply changes (Traffic duplication)
  • Pass updated item and query for original item to apply changes (Extra access to BD to retrieve item)

What we want is to be able to update the item, using the values passed by the Web Service, without having the original item or querying the database for it.

This can happen in the following scenarios:

  • Internal entity model can be different from data contract types
  • Applications consuming service don't want to use Message Exchange Pattern
  • Don't retrieve item if it's a full update, only retrieve in partial update.

 

ADO.NET Entity Framework allows us to update an item if you have an EntityKey for the item, using generics we can encapsulate the key creation in the following function.

    1:          #region CreateEntityKey
    2:   
    3:          /// <summary>
    4:          /// Create Entity Key
    5:          /// </summary>
    6:          public static EntityKey CreateEntityKey<TDomainObjectContextKey, TDomainObjectKey>()
    7:          {
    8:              // Parent Keys
    9:              EntityKey _entityKey = new EntityKey();
   10:   
   11:              _entityKey.EntityContainerName = typeof( TDomainObjectContextKey ).Name;
   12:              _entityKey.EntitySetName = typeof( TDomainObjectKey ).Name;
   13:   
   14:              return _entityKey;
   15:          }
   16:   
   17:          #endregion

Since the object already exists in the Database what we need is to create a Relationship Key and add it to our item.

The following examples shows this method applied to a context ( LookupEntities ) that has two entities( LookupType, LookupValue), where a LookupType has many LookupValues.

We want to add a LookupValue to an existing LookupType. So create the EntityKey for the item and populate it with the parent item ID (foreign key) that is one of the fields of the entity.

Now that we have our EntityKey created we add it to the item, attach the item to the context and save the changes.

    1:          #region Update
    2:   
    3:          /// <summary>
    4:          /// Update a LookupValue
    5:          /// </summary>
    6:          /// <param name="context"></param>
    7:          /// <param name="item"></param>
    8:          /// <returns></returns>
    9:          public Int32 Update(
   10:              LookupEntities context,
   11:              LookupValue item )
   12:          {
   13:              #region Create Relationship Key
   14:   
   15:              // Create Relationship Key
   16:              EntityKey _relationshipKey = CreateEntityKey<LookupEntities, LookupType>();
   17:   
   18:              _relationshipKey.EntityKeyValues = new EntityKeyMember[2]{
   19:                  new EntityKeyMember("LookupTypeID",item.LookupTypeID),
   20:                  new EntityKeyMember("Culture",item.Culture)};
   21:   
   22:              // Create LookupType Reference
   23:              item.LookupTypeReference = new EntityReference<LookupType>();
   24:              item.LookupTypeReference.EntityKey = _relationshipKey;
   25:   
   26:              #endregion
   27:   
   28:              #region Attach Item
   29:   
   30:              // Attach Item
   31:              context.AttachTo(typeof(LookupValue).Name, item); 
   32:   
   33:              #endregion
   34:   
   35:   
   36:              #region Save changes
   37:   
   38:              // Save changes pessimistically. This means that changes 
   39:              // must be accepted manually once the transaction succeeds.
   40:              context.SaveChanges();
   41:   
   42:              #endregion
   43:   
   44:              return item.LookupValueID;
   45:          }
   46:   
   47:          #endregion

With this method we improve performance in applying changes to detached objects, avoiding traffic duplication and extra access to the database to maintain the context updated.

Pedro M. Pinheiro

Comments

  • Anonymous
    November 16, 2008
    I tried ur method and it is not working for me. No changes to the object are saved (using the NorthwindEF database): Product product = GetDetachedProductById(92); product.ProductName = "asdf"; UpdateDetachedProduct(product); where: void UpdateDetachedProductById(Products product)        {            using (NorthwindEFEntities entities = new NorthwindEFEntities())            {                EntityKey _categoryRelationshipKey = CreateEntityKey<NorthwindEFEntities, Categories>();                _categoryRelationshipKey.EntityKeyValues = new EntityKeyMember[1]{                    new EntityKeyMember("CategoryID",1)};                EntityKey _supplierRelationshipKey = CreateEntityKey<NorthwindEFEntities, Suppliers>();                _supplierRelationshipKey.EntityKeyValues = new EntityKeyMember[1]{                    new EntityKeyMember("SupplierID",1)};                product.CategoriesReference.EntityKey = _categoryRelationshipKey;                product.SuppliersReference.EntityKey = _supplierRelationshipKey;                entities.Attach(product);                entities.SaveChanges();                         }        }

  • Anonymous
    November 24, 2008
    This code does not work as stated.