Microsoft Dynamics 365 Blog

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


We're always looking for feedback and would like to hear from you. Please head to the Dynamics 365 Community to start a discussion, ask questions, and tell us what you think!