·
5 min read

How to get better performance from the MetadataService

You may have noticed that in 4.0, the MetadataService is considerably slower at retrieving data that it was in 3.0. In fact, you’ll still see the difference if you compare the 2006 MetadataService endpoint and the 2007 endpoint. Why is this?

The reason is that in 3.0, the MetadataService reads from the server’s MetadataCache, so it’s very quick. In 4.0 it reads from the database, so each request has to build up the objects you want. The reason we read from the database is to give you the option of retrieving either published or unpublished Metadata.

Here are some key differences between the MetadataService of 3.0 and 4.0, or the 2006 and 2007 endpoint:

Microsoft Dynamics CRM 3.0


  • Fast
  • Read Only
  • Returns only published metadata
  • Method based (ex: RetrieveEntityMetadata())

Microsoft Dynamics CRM 4.0


  • Slower
  • Returns more data

    • Ex: EntityMetadata now contains properties such as: ‘IsAvailableOffline’, ‘IsIntersect’, ‘CanTriggerWorkflow’, …
    • Ex: OneToManyMetadata contains the cascade behavior of the relationship.

  • Allows Read and Write operations
  • Can return either published or unpublished metadata

    • Each request message has a Boolean parameter called ‘RetrieveAsIfPublished’.
    • This parameter bears some explanation as the name can be confusing at first.

      • ‘RetrieveAsIfPublished = True’ means “return the Metadata including unpublished changes” or “as it would look if I published.”
      • ‘RetrieveAsIfPublished = False’ means “return only the currently published Metadata, ignoring any unpublished changes that exist.”

  • Uses the same Request/Response pattern as the CrmService (ex: RetrieveEntityRequest/RetrieveEntityResponse)

Some tips for dealing with performance:


  • Use the EntityItems flag to retrieve only what you need.

    • If you need to know the PrimaryKey of an entity, use the RetrieveEntityRequest and set the ‘EntityItems’ property to  ‘EntityItems.EntityOnly’. Then it won’t spend the time building up a list of Attributes or Relationships that you won’t use.
    • Note that this parameter existed in the 3.0 MetadataService, but since it was reading cached data, it didn’t provide much of an improvement. In 4.0, you’ll see a significant performance gain by using this property appropriately.

  • Use the appropriate Retrieve message to get only what you need.

    • Use RetrieveAttributeRequest or RetrieveRelationshipRequest instead of RetreiveEntityRequest to get just the object you need.

  • Build your own Metadata cache.

    • Retrieve the entire Metadata once, and then whenever your application needs it, read from your cached data instead of making a call to the server. Then use the RetrieiveTimestampRequest to poll the server to see if the metadata has changed since the last time you loaded it.  The name ‘RetrieveTimestamp’ is a bit misleading because it doesn’t return a DateTime, it returns a string that represents a hash of the entire metadata(ex: “-2127913662”). Whenever the published metadata changes, this hash is recalculated.

Here’s an example of what your MetadataCache might look like:


public class MetadataCache
{
      private bool MetadataHasChanged
      {
            get
            {
                  //get the most recent timestamp
                  RetrieveTimestampRequest req = new RetrieveTimestampRequest();
                  RetrieveTimestampResponse resp = (RetrieveTimestampResponse)MetadataService2007.Execute(req);

                  //compare it with the last timestamp I retrieved.
                  //Note: I’m not storing the new Timestamp because I’m not loading the Metadata.
                  return resp.Timestamp != _lastTimestamp;
            }
      }
private string _lastTimestamp = string.Empty;
      private CrmMetadata[] _metadata;
      private CrmMetadata[] Metadata
      {
            get
            {
                  //If the Metadata has changed since I last retrieved it, reload it.
                  if (MetadataHasChanged)
                  {
                        RetrieveAllEntitiesRequest req = new RetrieveAllEntitiesRequest();
                        req.MetadataItems = MetadataItems.All; //I want all of the Metadata.
                        req.RetrieveAsIfPublished = false; //I want the published Metadata.
                        RetrieveAllEntitiesResponse resp =  (RetrieveAllEntitiesResponse)MetadataService2007.Execute(req);
                        _metadata = resp.CrmMetadata; //set the Metadata cache.
                        _lastTimestamp = resp.Timestamp; //store the timestamp.
                  }
                  return _metadata;
            }
      }

      public EntityMetadata GetEntity(string entityName)
      {
            foreach (EntityMetadata entity in Metadata)
            {
                  if (entity.LogicalName == entityName)
                  {
                        return entity;
                  }
            }
            throw new ArgumentException(“The entity ” + entityName + ” was not found in the MetadataCache.”);
      }
//Add more methods to retrieve Attributes, Relationships, etc…
}


Cheers,


Mark Harrington