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.