应该使用ToEntity <t>而不是演员?

时间:2017-05-17 12:00:23

标签: c# dynamics-crm-online

Xrm Sdk defines一个ToEntity<T>方法。我一直用它来从CRM中获取早期绑定的实体。今天我正在审查一些代码并看到实体刚刚被投射:

var contact = service.Retrieve("contact", id, new ColumnSet()) as Contact;

我甚至都不知道这是可能的。是否还需要ToEntity调用?

2 个答案:

答案 0 :(得分:6)

除此之外,这不是特定的演员,它是转换,转换的行为与直接演员的行为略有不同。

As

  

您可以使用as运算符在兼容的引用类型或可空类型之间执行某些类型的转换... as运算符就像转换操作。但是,如果转换不可能,则返回null而不是引发异常。

我假设您的Contact是由CrmSvcUtil创建的类,例如public partial class Contact : Microsoft.Xrm.Sdk.Entityservice.RetrieveIOrganizationService.Retrieve,其返回类型为Entity

Contact是基类Entity的派生类。您不能将基类强制转换为更具体的派生类(请参阅Is it possible to assign a base class object to a derived class reference with an explicit typecast in C#?)。如果您尝试从Entity转换为Contact,则会出现异常,转换将返回空对象。

包含CrmSvcUtil的GeneratedCode示例,但没有与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

因此{@ 1}}返回Retrieve,而Entity无法投放到Contact,您的代码行(var contact = service.Retrieve("contact", id, new ColumnSet()) as Contact;)如何工作?< / p>

这是它的神奇之处。显然,如果在应用程序中包含CrmSvcUtil的GeneratedCode,Retrieve函数将返回特定的派生类而不是通用Entity

包含来自CrmSvcUtil的GeneratedCode的示例:

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正在返回Contact,那么您只需进行简单的转换(例如(Contact)service.Retrieve(...))或转换({{1} })。就as做什么而言,它实际上并没有进行演员表或转换。它创建一个新对象并在其他一些东西中执行浅拷贝。因此,如果符合您的需要,请使用它,但如果没有它,您可能会离开。

反编译代码:

ToEntity

答案 1 :(得分:5)

它始终如此工作,请查看来自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();的原因。基本上如果你这样做,所有调用都将返回早期绑定类型,而不是Entity对象(当然早期绑定是从Entity继承的,但你知道我的意思)。这只是关于从CRM获取数据的一项功能。

这与ToEntity&lt;&gt;()无关,因为你仍然无法做到这样的事情:

var account = new Entity("account");
var earlyBoundAccount = account as Account; //this will result in NULL

因此,如果您有实体(例如在插件目标或PostImage中),您仍然必须使用ToEntity将其转换为早期绑定。

更新: 我深入挖掘并检查了EnableProxyTypes的作用 - 它只是使用DataContractSerializerOperationBehavior类来注入它自己的IDataContractSurrogate来处理响应的序列化/反序列化(例如如何使用它可以找到{ {3}})。通过查看反序列化的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%肯定的)