Somewhat aside, that's not specifically a cast, its a conversion, and a conversion behaves slightly differently than a straight cast.
As
You can use the as operator to perform certain types of conversions between compatible reference types or nullable types...The as operator is like a cast operation. However, if the conversion isn't possible, as returns null instead of raising an exception.
I'm assuming that your Contact
is a class created by CrmSvcUtil e.g. public partial class Contact : Microsoft.Xrm.Sdk.Entity
, and service.Retrieve
is IOrganizationService.Retrieve which has a return type of Entity
.
Contact
is a derived class of the base class Entity
. You can't cast a base class to a more specific derived class (see Is it possible to assign a base class object to a derived class reference with an explicit typecast in C#?). If you tried to do a cast from Entity
to Contact
you would get an exception, and a conversion would return a null object.
Example with GeneratedCode from CrmSvcUtil included, but no actual connection to CRM.
var entity = new Entity();
Console.WriteLine($"Type of local entity: {entity.GetType()}");
Console.WriteLine($"Local entity as Contact is null? {entity as Contact == null}");
Output:
Type of local entity: Microsoft.Xrm.Sdk.Entity
Local entity as Contact is null? True
So given Retrieve
returns an Entity
, which can't be cast to Contact
, how does your line of code (var contact = service.Retrieve("contact", id, new ColumnSet()) as Contact;
) even work?
Well it's magic. Apparently if you include GeneratedCode from CrmSvcUtil within your application the Retrieve
function returns the specific derived classes instead of the generic Entity
.
Example with GeneratedCode from CrmSvcUtil included:
CrmServiceClient service = new CrmServiceClient(ConfigurationManager.ConnectionStrings["Crm"].ConnectionString);
Contact c = new Contact()
{
LastName = "Test"
};
Guid contactId = service.Create(c);
var response = service.Retrieve("contact", contactId, new ColumnSet());
Console.WriteLine($"Type of response from CRM: {response.GetType()}");
Console.WriteLine($"Response from CRM as contact is null? {response as Contact == null}");
Outputs:
Type of response from CRM: Contact
Response from CRM as contact is null? False
Example with no generated code included:
CrmServiceClient service = new CrmServiceClient(ConfigurationManager.ConnectionStrings["Crm"].ConnectionString);
Entity c = new Entity("contact");
c["lastname"] = "Test";
Guid contactId = service.Create(c);
var response = service.Retrieve("contact", contactId, new ColumnSet());
Console.WriteLine($"Type of response: {response.GetType()}");
Outputs:
Type of response: Microsoft.Xrm.Sdk.Entity
Back to your question. If you are including generated code in your project, given that Retrieve
is returning a Contact
anyway you are fine to just do a simple cast (e.g. (Contact)service.Retrieve(...)
) or conversion (as
). In terms of what what ToEntity
does, it's not actually doing a cast or conversion. It creates a new object and performs a shallow copy among some other things. So use it if meets your need, but you can probably get away without it.
Decomplied code:
public T ToEntity<T>() where T : Entity
{
if (typeof(T) == typeof(Entity))
{
Entity entity = new Entity();
this.ShallowCopyTo(entity);
return entity as T;
}
if (string.IsNullOrWhiteSpace(this._logicalName))
{
throw new NotSupportedException("LogicalName must be set before calling ToEntity()");
}
string text = null;
object[] customAttributes = typeof(T).GetCustomAttributes(typeof(EntityLogicalNameAttribute), true);
if (customAttributes != null)
{
object[] array = customAttributes;
int num = 0;
if (num < array.Length)
{
EntityLogicalNameAttribute entityLogicalNameAttribute = (EntityLogicalNameAttribute)array[num];
text = entityLogicalNameAttribute.LogicalName;
}
}
if (string.IsNullOrWhiteSpace(text))
{
throw new NotSupportedException("Cannot convert to type that is does not have EntityLogicalNameAttribute");
}
if (this._logicalName != text)
{
throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture, "Cannot convert entity {0} to {1}", new object[]
{
this._logicalName,
text
}));
}
T t = (T)((object)Activator.CreateInstance(typeof(T)));
this.ShallowCopyTo(t);
return t;
}