The Microsoft Dynamics CRM Blog
News and views from the Microsoft Dynamics CRM Team

Issues When Updating Records Using the REST Endpoint for Web Resources and Silverlight

Issues When Updating Records Using the REST Endpoint for Web Resources and Silverlight

  • Comments 3

When you update records using Silverlight and the REST Endpoint for Web resources, unless you have worked around this issue you are updating every field even if the data hasn’t changed. This can lead to the following problems:

  • Potential for data loss
  • Auditing data does not correctly reflect actual changes
  • Event driven features such as Workflows (Processes) and Plug-ins execute when they are not needed

This post will describe the issue and provide a technique to avoid the problems.

Issue Description

The Microsoft Dynamics CRM SDK documents the following in the topic: Perform Basic Data Operations in Microsoft Dynamics CRM Using the REST Endpoint.

clip_image002

This behavior is a result of the way that a Silverlight application project generates proxies to work with an OData service. By default all properties defined for an entity are sent in an update request when you use the DataServiceContext.BeginSaveChanges Method. If you are working with an entity instance that was retrieved from a query it will contain all the current values and saving changes will update all the properties of the entity instance with those values even if they have not changed. If you instantiate a new entity instance and only update a few properties the update will overwrite other properties using the default null value for properties you haven’t set.

Technique to Avoid this Problem

To address this problem you need to provide some capability in your Silverlight application to track which properties have changed and only submit those properties when you save changes. Michael created the following solution that extends the Data Service Context to include change tracking for entity properties. I’ve found that it is relatively easy to apply this to an existing Silverlight application. Use the following steps to add and edit a new class file to your project:

Apply Data Service Context Extensions to A Silverlight Application Project:

1. Add a reference to System.Xml.Linq to your Silverlight application project if it doesn’t already exist

2. Create a new class file for your Silverlight application project. You might call it DataServiceContextExtensions.cs.

3. Paste the following code into the new file:

using System;
using System.Linq;
using System.Data.Services.Client;
using System.Reflection;
using System.Collections.Generic;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Xml.Linq;
namespace [[The Namespace of your Data Service Context]]
{
 partial class [[The name of your DataServiceContext Class]]
 {
  #region Methods
  partial void OnContextCreated()
  {
   this.ReadingEntity += this.OnReadingEntity;
   this.WritingEntity += this.OnWritingEntity;
  }
  #endregion
  #region Event Handlers
  private void OnReadingEntity(object sender, ReadingWritingEntityEventArgs e)
  {
   ODataEntity entity = e.Entity as ODataEntity;
   if (null == entity)
   {
    return;
   }
   entity.ClearChangedProperties();
  }
  private void OnWritingEntity(object sender, ReadingWritingEntityEventArgs e)
  {
   ODataEntity entity = e.Entity as ODataEntity;
   if (null == entity)
   {
    return;
   }
   entity.RemoveUnchangedProperties(e.Data);
  }
  #endregion
 }
 public abstract class ODataEntity
 {
  private readonly Collection<string> ChangedProperties = new Collection<string>();
  public ODataEntity()
  {
   EventInfo info = this.GetType().GetEvent("PropertyChanged");
   if (null != info)
   {

PropertyChangedEventHandler method = new PropertyChangedEventHandler

(this.OnEntityPropertyChanged);

    //Ensure that the method is not attached and reattach it
    info.RemoveEventHandler(this, method);
    info.AddEventHandler(this, method);
   }
  }
  #region Methods
  public void ClearChangedProperties()
  {
   this.ChangedProperties.Clear();
  }
  internal void RemoveUnchangedProperties(XElement element)
  {
   const string AtomNamespace = "http://www.w3.org/2005/Atom";
   const string DataServicesNamespace = "http://schemas.microsoft.com/ado/2007/08/dataservices";
   const string DataServicesMetadataNamespace = DataServicesNamespace + "/metadata";
   if (null == element)
   {
    throw new ArgumentNullException("element");
   }
   List<XElement> properties = (from c in element.Elements(XName.Get("content", AtomNamespace)
               ).Elements(XName.Get("properties", DataServicesMetadataNamespace)).Elements()
                                select c).ToList();
   foreach (XElement property in properties)
   {
    if (!this.ChangedProperties.Contains(property.Name.LocalName))
    {
     property.Remove();
    }
   }
  }

private void OnEntityPropertyChanged(object sender,

System.ComponentModel.PropertyChangedEventArgs e)

  {
   if (!this.ChangedProperties.Contains(e.PropertyName))
   {
    this.ChangedProperties.Add(e.PropertyName);
   }
  }
  #endregion
 }
}

4. In the file you created replace the following placeholders with the correct references for your project:

image

5. Update the Reference.cs file

a. In Visual Studio 2010, click the icon with the tooltip Show All Files at the top of the Solution Explorer window.

b. In Solution Explorer, expand the service reference you created and the Reference.datasvcmap file to view the Reference.cs file.

c. Use Find/Replace to change:

: global::System.ComponentModel.INotifyPropertyChanged

to

: ODataEntity, global::System.ComponentModel.INotifyPropertyChanged

Note: There should be one instance for each entity.

6. Recompile your project, update your Silverlight Web resource with the new .xap file and test it.

SDK Update

In next release of the SDK (version 5.05) the SDK topic Use the REST Endpoint with Silverlight Web Resources will contain steps include this method. Each of the Silverlight samples that update records will also be updated to include these extensions.

Cheers,

Jim Daly, Michael Scott



  • To be clear, this only applies when updating records using the REST endpoint and Silverlight. When using other web resources we will be interacting with the REST endpoint using XMLHttpRequest and JSON. I assume the MERGE operation is also used in this context? If that is the case, then the best practice for performing updates using the REST endpoint would be to first retrieve the entity, perform necessary updates, and then POST the entity in its entirety. The other alternative will be to create a new JSON object and POST only the changed and required fields. Please let me know if I am off base here.

  • top class

  • l3a0 -

    Sorry for the delay in responding. I don't get a notification when the blog post is updated...

    This only applies when using the REST endpoint and Silverlight. It has to do with the proxies generated by default in a Silverlight application project.

    With JSON, you use POST but you must also explictly add the X-HTTP-Method to "MERGE" using the XMLHttpRequest.setRequestHeader method to get the desired behavior.

    For more information see this SDK link: msdn.microsoft.com/.../gg328090.aspx

Page 1 of 1 (3 items)
Leave a Comment
  • Please add 1 and 4 and type the answer here:
  • Post