2017-05-17 10 views
4

Xrm Sdk definesa ToEntity<T>メソッド。私はいつもCRMから私の初期の結合エンティティを取得するためにそれを使用しました。今日、私はいくつかのコードを見直し、事業体がちょうどキャストなっていたことを見た:私もそれが可能であることを認識していませんでしたToEntity <T>キャストの代わりに使用する必要がありますか?

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

を。 ToEntityコールはもはや必要ですか?

答えて

3

これは、特にキャストではなく、そのコンバージョンとコンバージョンがストレートキャストとは少し異なる動作をします。

As

互換性の参照型またはnull許容型の間の変換の特定のタイプを実行するために演算子として使用することができ...としてオペレータがキャスト動作と同様です。ただし、変換が不可能な場合は、例外が発生する代わりにnullが返されます。

私はあなたのContactがCrmSvcUtilによって作成されたクラスであると仮定しています。 public partial class Contact : Microsoft.Xrm.Sdk.Entityおよびservice.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へのキャストを実行しようとすると例外が発生し、変換ではnullオブジェクトが返されます。

例: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}"); 

出力:Contactにキャストすることはできません

Type of local entity: Microsoft.Xrm.Sdk.Entity 
Local entity as Contact is null? True 

だから、与えられたRetrieve戻りEntity、コード(var contact = service.Retrieve("contact", id, new ColumnSet()) as Contact;)のあなたのラインにもどのように動作しますか?

これは魔法です。明らかに、アプリケーション内で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 

質問に戻るプロジェクトに生成コードを含める場合は、RetrieveContactを返していれば、単純なキャスト(例:(Contact)service.Retrieve(...))または変換(as)である。 ToEntityの意味では、実際にキャストや変換を行っていません。新しいオブジェクトを作成し、他のオブジェクトとの間でシャローコピーを実行します。あなたの必要を満たすならそれを使用しますが、おそらくそれなしで逃げることができます。

Decompliedコード:

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; 
} 
3

それは常にそのように働いた、あなたはIOrganizationServiceにEnableProxyTypes();をしなければならない理由ですhere

ColumnSet cols = new ColumnSet(new String[] { "name", "address1_postalcode", "lastusedincampaign", "versionnumber" }); 
Account retrievedAccount = (Account)_serviceProxy.Retrieve("account", _accountId, cols); 
Console.Write("retrieved "); 

からCRM 2011のサンプルコードを見てください。基本的に、すべての呼び出しは、Entityオブジェクトではなく、初期のバインドされた型を返します(もちろん、初期の境界はEntityから継承していますが、私の意味を知っています)。これは単にCRMからデータを取得するための機能です。

これは、あなたがまだそのようなことはできませんので、ToEntity <>()とは何の関係もありません:あなたはまだはあなたが(プラグインターゲットまたはPostImage中など)のエンティティを持っているのであれば

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

をToEntityを使用して初期バインディングに変換する必要があります。

UPDATE: 私はない何EnableProxyTypes深く掘ってチェック - それは単にそれが(それがどのように使用できるかの例では、hereを見つけることができる)レスポンスのシリアライズ/デシリアライゼーションのを処理するためにIDataContractSurrogate自身だ注入するDataContractSerializerOperationBehaviorクラスを使用しています。

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を使用してインスタンス化される:CRMの非直列化源に見ることによって、あなたは、逆シリアル化がどのように実装されるか自分で見ることができます。繰り返しますが、これはプロキシタイプを有効にしたIOrganizationServiceに対してのみ機能します(そして、覚えている限り、プロキシが同じアセンブリにある場合IOrganizationServiceはインスタンス化されます。これは、明示的に呼び出さなくてもデフォルトで有効になります。私は100%は確信していません)

+0

したがって、基本的にenable proxyタイプを呼び出すと、あなたのためにtoEntityが呼び出されますが、IOrganizationServiceリクエストに対してのみ呼び出されます。プラグインコンテキストには影響しませんか? – Daryl

+0

ToEntity関数(正確には私の更新された答えをチェックすることができます)を呼び出すのではなく、基本的に同じことを行います。これはプラグインコンテキストには影響しません。なぜなら、これを実行する唯一の方法は、早期にバインドされたエンティティをターゲットに注入することです。プラグインは多くの異なるエンティティに登録できるため不可能です。 –

関連する問題