Posted on 28. April 2011

Early bound Entity Types in Silverlight

One of the drawbacks of using Silverlight clients for CRM2011, is that there is no built in support for early bound entity types since you cannot use the Microsoft SDK assemblies. Really, there isn't anything that special going on with early bound support in the SDK assemblies – a behaviour is added to the WCF client that intercepts the entities after serialization and converts them to the early bound types.

Ironically, one of the things about Silverlight is that it needs early bound properties for binding purposes – below is the code you can use to easily simulate this early bound support in Silverlight.

1. You need to create the attributes to use to decorate your early bound entities.

[AttributeUsage(AttributeTargets.Property)]
public sealed class AttributeLogicalNameAttribute : Attribute
{
    public AttributeLogicalNameAttribute(string logicalName)
    {
        if (string.IsNullOrWhiteSpace(logicalName))
        {
            throw new ArgumentNullException("logicalName");
        }
        this.LogicalName = logicalName;
    }

    public string LogicalName
    { get; set; }

}

 

2. Create an early bound entity with the code to get/set the attribute values from the attribute collection – this is essentially the same as the SDK generated early bound types. For example, I'm using contact here:

public class Contact : Entity
    {
        [AttributeLogicalName("contactid")]
        public Guid ContactId
        {
            get
            {
                return this.GetAttributeValue<Guid>("contactid");
            }
            set
            {

                this.SetAttributeValue("contactid", value);

            }
        }

        [AttributeLogicalName("fullname")]
        public string FullName
        {
            get
            {
                return this.GetAttributeValue<string>("fullname");
            }
            set
            {

                this.SetAttributeValue("fullname", value);

            }
        }

        [AttributeLogicalName("firstname")]
        public string FirstName
        {
            get
            {
                return this.GetAttributeValue<string>("firstname");
            }
            set
            {

                this.SetAttributeValue("firstname", value);

            }
        }
        [AttributeLogicalName("lastname")]
        public string LastName
        {
            get
            {
                return this.GetAttributeValue<string>("lastname");
            }
            set
            {

                this.SetAttributeValue("lastname", value);

            }
        }
    }

3. Add extension methods to allow converting to the early bound types

public static class ProxyTypeSupport
    {
        public static void SetAttributeValue(this Entity thisEntity, string attributeLogicalName, object value)
        {
            if (string.IsNullOrWhiteSpace(attributeLogicalName))
            {
                throw new ArgumentNullException("attributeLogicalName");
            }
            thisEntity[attributeLogicalName] = value;
        }

        public static T ToEntity<T>(this Entity thisEntity) where T : Entity
        {
            if (typeof(T) == typeof(Entity))
            {
                Entity entity = new Entity();
                thisEntity.ShallowCopyTo(entity);
                return (entity as T);
            }
           
            T target = (T)Activator.CreateInstance(typeof(T));
            thisEntity.ShallowCopyTo(target);
            return target;
        }

        public static void ShallowCopyTo(this Entity thisEntity, Entity target)
        {
            if ((target != null) && (target != thisEntity))
            {
                target.Id = thisEntity.Id;
                target.LogicalName = thisEntity.LogicalName;
                target.EntityState = thisEntity.EntityState;
                target.RelatedEntities = thisEntity.RelatedEntities;
                target.Attributes = thisEntity.Attributes;
                target.FormattedValues = thisEntity.FormattedValues;

            }
        }
    }

 

4. Add code to retrieve, and convert the returned Entities into the early bound Contact Types.

service.BeginRetrieveMultiple(query,
        (IAsyncResult asyncResult) =>
        {
        // Convert results to early bound Entity
        EntityCollection results = service.EndRetrieveMultiple(asyncResult);
        ObservableCollection<Contact> contacts = new ObservableCollection<Contact>();

        foreach (Entity lateBoundEntity in results.Entities)
        {
            contacts.Add(lateBoundEntity.ToEntity<Contact>());
        }
                       
        this.Dispatcher.BeginInvoke(() =>
        {
            this.dataGrid1.ItemsSource = contacts;

        });
                          
        }, null);

 

This would also work in reverse to allow you to update values.

Note: The example is not using MVVM pattern so as to make is simpler for readers.

 

Download the code: CrmEarlyBoundTypesInSilverlight.zip (39.84 kb)

Usual disclaimers apply: THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.

Posted on 28. April 2011

The use of type 'Microsoft.Xrm.Sdk.Query.FilterExpression' as a get-only collection is not supported with NetDataContractSerializer.

A while back I had a problem with calling the CRM2011 WCF endpoint where the Microsoft SDK Proxy assembly was not available (e.g. Silverlight). When calling RetrieveMultiple I was getting the following exception:

"The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter http://schemas.microsoft.com/xrm/2011/Contracts/Services:request. The InnerException message was 'The use of type 'Microsoft.Xrm.Sdk.Query.FilterExpression' as a get-only collection is not supported with NetDataContractSerializer.Consider marking the type with the CollectionDataContractAttribute attribute or the SerializableAttribute attribute or adding a setter to the property."

The strange thing was that this appeared even when not using a FilterExpression in the query. After a bit of digging it turned out to be to do with the serialization of the ColumnSet object when AllColumns was set to true, but the Columns collection was empty.

The solution was to use partial classes to simulate the Microsoft SDK Proxy classes, and then set the Columns collection to a 'dummy' value when AllColumns was set to true.

partial class ColumnSet
{
    public ColumnSet()
    {
      this.ColumnsField = new ObservableCollection();
    }
    public ColumnSet(params string[] columns)
    {
      this.Columns = new ObservableCollection(columns);
    }
    public ColumnSet(bool allColumns)
    {
      this.AllColumnsField = allColumns;
      this.ColumnsField = new string[] { allColumns.ToString() };
    }
}

Then you can use:

query.ColumnSet = new ColumnSet("col1","col2","col3");
 

or

query.ColumnSet = new ColumnSet(true);

If you are not using the ObservableCollection in your proxy classes, use this version instead:

partial class ColumnSet
  {
    public ColumnSet()
    {
      this.ColumnsField = new ObservableCollection();
    }
    public ColumnSet(params string[] columns)
    {
      this.Columns = columns;
    }
    public ColumnSet(bool allColumns)
    {
      this.AllColumnsField = allColumns;
      this.ColumnsField = new string[] { allColumns.ToString() };
    }
  }

Hope this helps!

Posted on 8. April 2011

Silverlight Theme for CRM2011

Develop1 have a Silverlight 4 CRM 2011 theme available for the following user interface components:

  • Label Style
  • Text Box Style
  • Date Picker Style
  • Radio Button Style
  • Data Grid Style
  • Lookup Style (Coming soon)

Please contact us for more information.