应该使用 ToEntity<T> 而不是演员表吗?
Should ToEntity<T> Be Used Instead Of A Cast?
Xrm Sdk defines一个ToEntity<T>
方法。我一直用它从 CRM 获取我早期绑定的实体。今天我正在审查一些代码,看到实体刚刚被转换:
var contact = service.Retrieve("contact", id, new ColumnSet()) as Contact;
我什至不知道这是可能的。甚至还需要 ToEntity 调用吗?
顺便提一下,这不是特定的转换,而是转换,转换的行为与直接转换略有不同。
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.
我假设您的 Contact
是由 CrmSvcUtil 创建的 class,例如public partial class Contact : Microsoft.Xrm.Sdk.Entity
,service.Retrieve
是 IOrganizationService.Retrieve,return 类型为 Entity
。
Contact
是基数 class Entity
的派生 class。您不能将基础 class 转换为更具体的派生 class(请参阅 Is it possible to assign a base class object to a derived class reference with an explicit typecast in C#?)。如果您尝试执行从 Entity
到 Contact
的强制转换,您将得到一个异常,并且转换将 return 一个空对象。
包含来自 CrmSvcUtil 的生成代码的示例,但与 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}");
输出:
Type of local entity: Microsoft.Xrm.Sdk.Entity
Local entity as Contact is null? True
所以给定 Retrieve
returns 一个 Entity
,它不能转换为 Contact
,你的代码行 (var contact = service.Retrieve("contact", id, new ColumnSet()) as Contact;
) 甚至工作?
嗯,这很神奇。显然,如果您在应用程序中包含来自 CrmSvcUtil 的 GeneratedCode,Retrieve
函数 return 是特定派生的 classes 而不是通用 Entity
.
包含来自 CrmSvcUtil 的生成代码的示例:
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}");
输出:
Type of response from CRM: Contact
Response from CRM as contact is null? False
不包含生成代码的示例:
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()}");
输出:
Type of response: Microsoft.Xrm.Sdk.Entity
回到你的问题。如果您在项目中包含生成的代码,鉴于 Retrieve
正在 returning 一个 Contact
无论如何您可以只做一个简单的转换(例如 (Contact)service.Retrieve(...)
)或转换(as
)。就 ToEntity
的作用而言,它实际上并没有进行转换或转换。它创建一个新对象并执行浅表复制等一些事情。因此,如果满足您的需要,请使用它,但没有它您可能会逃脱。
反编译代码:
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;
}
它总是那样工作,请查看来自 here
的 CRM 2011 示例代码
ColumnSet cols = new ColumnSet(new String[] { "name", "address1_postalcode", "lastusedincampaign", "versionnumber" });
Account retrievedAccount = (Account)_serviceProxy.Retrieve("account", _accountId, cols);
Console.Write("retrieved ");
这就是您必须对 IOrganizationService 执行 EnableProxyTypes();
的原因。基本上,如果您这样做,所有调用都将 return 早期绑定类型,而不是 Entity 对象(当然,早期绑定是从 Entity 继承的,但您知道我的意思)。这只是一个关于从 CRM 获取数据的功能。
这与 ToEntity<>() 无关,因为您仍然不能这样做:
var account = new Entity("account");
var earlyBoundAccount = account as Account; //this will result in NULL
因此,如果您有实体(例如在插件目标或 PostImage 中),您仍然必须使用 ToEntity 将其转换为早期绑定。
更新:
我深入挖掘并检查了 EnableProxyTypes 的作用——它只是使用 DataContractSerializerOperationBehavior class to inject it's own IDataContractSurrogate
to handle serialization/deserialization of the response (example how it can be used can be found here)。通过查看 CRM 的反序列化源,您可以亲眼看到反序列化是如何实现的:
object IDataContractSurrogate.GetDeserializedObject(object obj, Type targetType)
{
bool supportIndividualAssemblies = this._proxyTypesAssembly != null;
OrganizationResponse organizationResponse = obj as OrganizationResponse;
if (organizationResponse != null)
{
Type typeForName = KnownProxyTypesProvider.GetInstance(supportIndividualAssemblies).GetTypeForName(organizationResponse.ResponseName, this._proxyTypesAssembly);
if (typeForName == null)
{
return obj;
}
OrganizationResponse organizationResponse2 = (OrganizationResponse)Activator.CreateInstance(typeForName);
organizationResponse2.ResponseName = organizationResponse.ResponseName;
organizationResponse2.Results = organizationResponse.Results;
return organizationResponse2;
}
else
{
Entity entity = obj as Entity;
if (entity == null)
{
return obj;
}
Type typeForName2 = KnownProxyTypesProvider.GetInstance(supportIndividualAssemblies).GetTypeForName(entity.LogicalName, this._proxyTypesAssembly);
if (typeForName2 == null)
{
return obj;
}
Entity entity2 = (Entity)Activator.CreateInstance(typeForName2);
entity.ShallowCopyTo(entity2);
return entity2;
}
}
所以基本上来自 KnownProxyTypes 的类型是通过实体逻辑名称获得的,并使用 Activator
实例化。再次 - 这仅适用于您为其启用代理类型的 IOrganizationService(据我所知,如果代理在同一个程序集中 IOrganizationService 被实例化,默认情况下启用,即使您没有明确调用它,但这个我不是 100% 确定)
Xrm Sdk defines一个ToEntity<T>
方法。我一直用它从 CRM 获取我早期绑定的实体。今天我正在审查一些代码,看到实体刚刚被转换:
var contact = service.Retrieve("contact", id, new ColumnSet()) as Contact;
我什至不知道这是可能的。甚至还需要 ToEntity 调用吗?
顺便提一下,这不是特定的转换,而是转换,转换的行为与直接转换略有不同。
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.
我假设您的 Contact
是由 CrmSvcUtil 创建的 class,例如public partial class Contact : Microsoft.Xrm.Sdk.Entity
,service.Retrieve
是 IOrganizationService.Retrieve,return 类型为 Entity
。
Contact
是基数 class Entity
的派生 class。您不能将基础 class 转换为更具体的派生 class(请参阅 Is it possible to assign a base class object to a derived class reference with an explicit typecast in C#?)。如果您尝试执行从 Entity
到 Contact
的强制转换,您将得到一个异常,并且转换将 return 一个空对象。
包含来自 CrmSvcUtil 的生成代码的示例,但与 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}");
输出:
Type of local entity: Microsoft.Xrm.Sdk.Entity
Local entity as Contact is null? True
所以给定 Retrieve
returns 一个 Entity
,它不能转换为 Contact
,你的代码行 (var contact = service.Retrieve("contact", id, new ColumnSet()) as Contact;
) 甚至工作?
嗯,这很神奇。显然,如果您在应用程序中包含来自 CrmSvcUtil 的 GeneratedCode,Retrieve
函数 return 是特定派生的 classes 而不是通用 Entity
.
包含来自 CrmSvcUtil 的生成代码的示例:
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}");
输出:
Type of response from CRM: Contact
Response from CRM as contact is null? False
不包含生成代码的示例:
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()}");
输出:
Type of response: Microsoft.Xrm.Sdk.Entity
回到你的问题。如果您在项目中包含生成的代码,鉴于 Retrieve
正在 returning 一个 Contact
无论如何您可以只做一个简单的转换(例如 (Contact)service.Retrieve(...)
)或转换(as
)。就 ToEntity
的作用而言,它实际上并没有进行转换或转换。它创建一个新对象并执行浅表复制等一些事情。因此,如果满足您的需要,请使用它,但没有它您可能会逃脱。
反编译代码:
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;
}
它总是那样工作,请查看来自 here
的 CRM 2011 示例代码ColumnSet cols = new ColumnSet(new String[] { "name", "address1_postalcode", "lastusedincampaign", "versionnumber" });
Account retrievedAccount = (Account)_serviceProxy.Retrieve("account", _accountId, cols);
Console.Write("retrieved ");
这就是您必须对 IOrganizationService 执行 EnableProxyTypes();
的原因。基本上,如果您这样做,所有调用都将 return 早期绑定类型,而不是 Entity 对象(当然,早期绑定是从 Entity 继承的,但您知道我的意思)。这只是一个关于从 CRM 获取数据的功能。
这与 ToEntity<>() 无关,因为您仍然不能这样做:
var account = new Entity("account");
var earlyBoundAccount = account as Account; //this will result in NULL
因此,如果您有实体(例如在插件目标或 PostImage 中),您仍然必须使用 ToEntity 将其转换为早期绑定。
更新:
我深入挖掘并检查了 EnableProxyTypes 的作用——它只是使用 DataContractSerializerOperationBehavior class to inject it's own IDataContractSurrogate
to handle serialization/deserialization of the response (example how it can be used can be found here)。通过查看 CRM 的反序列化源,您可以亲眼看到反序列化是如何实现的:
object IDataContractSurrogate.GetDeserializedObject(object obj, Type targetType)
{
bool supportIndividualAssemblies = this._proxyTypesAssembly != null;
OrganizationResponse organizationResponse = obj as OrganizationResponse;
if (organizationResponse != null)
{
Type typeForName = KnownProxyTypesProvider.GetInstance(supportIndividualAssemblies).GetTypeForName(organizationResponse.ResponseName, this._proxyTypesAssembly);
if (typeForName == null)
{
return obj;
}
OrganizationResponse organizationResponse2 = (OrganizationResponse)Activator.CreateInstance(typeForName);
organizationResponse2.ResponseName = organizationResponse.ResponseName;
organizationResponse2.Results = organizationResponse.Results;
return organizationResponse2;
}
else
{
Entity entity = obj as Entity;
if (entity == null)
{
return obj;
}
Type typeForName2 = KnownProxyTypesProvider.GetInstance(supportIndividualAssemblies).GetTypeForName(entity.LogicalName, this._proxyTypesAssembly);
if (typeForName2 == null)
{
return obj;
}
Entity entity2 = (Entity)Activator.CreateInstance(typeForName2);
entity.ShallowCopyTo(entity2);
return entity2;
}
}
所以基本上来自 KnownProxyTypes 的类型是通过实体逻辑名称获得的,并使用 Activator
实例化。再次 - 这仅适用于您为其启用代理类型的 IOrganizationService(据我所知,如果代理在同一个程序集中 IOrganizationService 被实例化,默认情况下启用,即使您没有明确调用它,但这个我不是 100% 确定)