2

動作する次のコードがありますが、カスタム「プロジェクト」エンティティに関連付けられている「アカウント」エンティティを取得するためだけに複数のルックアップを実行していると思います。

初期結果セットをループせずに、入力する関連エンティティを指定することはできますか?

    public IList<new_project> GetAssociatedProjectsByPostcode(string postcode)
    {
        FilterExpression filter = new FilterExpression();
        filter.FilterOperator = LogicalOperator.And;
        filter.AddCondition(new ConditionExpression("new_project_zippostalcode", ConditionOperator.Equal, postcode));

        QueryExpression query = new QueryExpression();
        query.EntityName = new_project.EntityLogicalName;
        query.ColumnSet = new ColumnSet(true);
        query.Criteria = filter;

        OrganizationServiceCache serviceCache = new OrganizationServiceCache(MemoryCache.Default, base.CrmConnection);

        using (CachedOrganizationService service = new CachedOrganizationService(CrmConnection, serviceCache))
        using (XrmServiceContext xrmServiceContext = new XrmServiceContext(service))
        {
             //Run the query to return the project entities in a list
             IList<new_project> projects = service.RetrieveMultiple(query)
                                            .Entities
                                            .Select(item => item.ToEntity<new_project>())
                                            .ToList<new_project>();

             //Define the relationships we want populated
             Relationship accountRel = new Relationship("new_account_new_project");

             //We cannot call load property with tracking turned on 
             xrmServiceContext.MergeOption = MergeOption.NoTracking;

             //Loop through the original list and get our associations
             foreach (new_project np in projects)
                 xrmServiceContext.LoadProperty(np, accountRel);

             return projects;
        }            
    }

ServiceContext で同等のものを作成することは可能ですか?

Select Project.Name, Account.Name
From Project
Join Account ON Account.Id = Project.AccountId

編集 :

以下のJamesによって説明されているようにリンクエンティティを使用した後、プロジェクトとアカウントが1つのクエリに入力されたエンティティコレクションを生成する次のものがありますが、このフラットなデータセットを階層オブジェクト構造に転送することはできません.

        public IList<new_project> GetAssociatedProjectsByPostcode(string postcode)
    {
        FilterExpression filter = new FilterExpression();
        filter.FilterOperator = LogicalOperator.And;
        filter.AddCondition(new ConditionExpression("new_project_zippostalcode", ConditionOperator.BeginsWith, PostcodeUtility.RegionFromPostcode(postcode)));

        QueryExpression query = new QueryExpression();
        query.EntityName = new_project.EntityLogicalName;
        query.ColumnSet = new ColumnSet(true);
        query.Criteria = filter;

        query.LinkEntities.Add(new LinkEntity(new_project.EntityLogicalName, Account.EntityLogicalName, "new_associatedaccountid", "accountid", JoinOperator.Inner));
        query.LinkEntities[0].Columns = new ColumnSet(true);
        query.LinkEntities[0].EntityAlias = Account.EntityLogicalName;

        OrganizationServiceCache serviceCache = new OrganizationServiceCache(MemoryCache.Default, base.CrmConnection);

        using (CachedOrganizationService service = new CachedOrganizationService(CrmConnection, serviceCache))
        using (XrmServiceContext xrmServiceContext = new XrmServiceContext(service))
        {
            EntityCollection ec = service.RetrieveMultiple(query);

            //*****************************************************************
            //The entity collection is now populated with the accounts but they
            //are just additional key value pairs
            //e.g. (String)((AliasedValue)ec[0]["account.name"]).Value;
            //*****************************************************************

            //Turn the entity collection into our class structure
            IList<new_project> projects = ec.Entities
                                           .Select(item => item.ToEntity<new_project>())
                                           .ToList<new_project>();
            return projects;
        }
    }
4

2 に答える 2

2

ええ、これはかなり簡単です。いくつかのオプションがあります。

  1. FetchXml、これは xml 構文であり、tsql へのアプローチに似ています。すべての機能を備えているわけではありませんが、MSDNlink-entityに示されているように結合を行うことができます。

  2. QueryExpressionには、このMSDNLinkEntitiesのように使用できるプロパティがあります。

  3. ここに示すようにを発行してRetrieveRequest、を入力できます。RelatedEntitiesQuery


編集

したがって、エンティティ コレクションは、私が期待する結果 (アカウントとそれらのアカウントの値など) を返すように見えます - typeof(ec[0]) is Entity?

したがって、間違っているのはアーリーバウンドへの変換だけです。

私はlinqをcrmにあまり使用していないので、推測し始めていますが、このを見てください。

次のことが必要な場合があります。

  1. サービスで EnableProxyTypes を有効にします。明らかに、これは完全な早期バインド サポートに必要です。
  2. エンティティを事前バインド型にキャストします。

例(サンプルから):

_serviceProxy.EnableProxyTypes();

Account retrievedAccount = (Account)_serviceProxy.Retrieve("account", _accountId, cols);

retrievedAccount.Address1_PostalCode = "98052";

Retrieve は Entity (EntityCollection.Entities の型) を返すだけで、キャストはここで機能するようです。

于 2012-09-13T19:21:14.310 に答える
1

James Wood のコメントから編集... 子エンティティ 子エンティティ エンティティの子エンティティを取得するには、Retrieve Requestを使用します。ただし、機能させるには、親エンティティの GUID が必要です...一方、リンクされたエンティティの列コレクションに列を追加することで、子エンティティの値を含めることができます。エイリアス名を指定するか、デフォルトを受け入れることができます。

親エンティティからエイリアス値にアクセスするために、これらの拡張メソッドを広範囲に使用しました。

    // This needs to be placed in a public static class and it's namespace added as a using to whatever class you'd like to use it in

    /// <summary>
    /// Returns the Aliased Value for a column specified in a Linked entity
    /// </summary>
    /// <typeparam name="T">The type of the aliased attribute form the linked entity</typeparam>
    /// <param name="entity"></param>
    /// <param name="attributeName">The aliased attribute from the linked entity.  Can be preappeneded with the
    /// linked entities logical name and a period. ie "Contact.LastName"</param>
    /// <returns></returns>
    public static T GetAliasedValue<T>(this Entity entity, string attributeName)
    {
        string aliasedEntityName = SplitAliasedAttributeEntityName(ref attributeName);

        AliasedValue aliased;
        foreach (var attribute in entity.Attributes.Values)
        {
            aliased = attribute as AliasedValue;
            if(entity.IsAttributeAliasedValue(attributeName, aliasedEntityName, aliased))
            {
                try
                {
                    return (T)aliased.Value;
                }
                catch (InvalidCastException)
                {
                    throw new InvalidCastException(
                        String.Format("Unable to cast attribute {0}.{1} from type {2} to type {3}",
                                aliased.EntityLogicalName, aliased.AttributeLogicalName,
                                typeof(T).Name, aliased.Value.GetType().Name));
                }
            }
        }

        throw new Exception("Aliased value with attribute " + attributeName +
            " was not found!  Only these attributes were found: " + String.Join(", ", entity.Attributes.Keys));
    }

    /// <summary>
    /// Returns the Aliased Value for a column specified in a Linked entity, returning the default value for 
    /// the type if it wasn't found
    /// </summary>
    /// <typeparam name="T">The type of the aliased attribute form the linked entity</typeparam>
    /// <param name="entity"></param>
    /// <param name="attributeName">The aliased attribute from the linked entity.  Can be preappeneded with the
    /// linked entities logical name and a period. ie "Contact.LastName"</param>
    /// <returns></returns>
    public static T GetAliasedValueOrDefault<T>(this Entity entity, string attributeName)
    {
        T value;
        if (entity.HasAliasedAttribute(attributeName))
        {
            value = entity.GetAliasedValue<T>(attributeName);
        }
        else
        {
            value = default(T);
        }
        return value;
    }

    private static bool IsAttributeAliasedValue(this Entity entity, string attributeName, string aliasedEntityName, AliasedValue aliased)
    {
        bool value =
       (aliased != null &&
            (aliasedEntityName == null || aliasedEntityName == aliased.EntityLogicalName) &&
            aliased.AttributeLogicalName == attributeName);


        /// I believe there is a bug in CRM 2011 when dealing with aggregate values of a linked entity in FetchXML.
        /// Even though it is marked with an alias, the AliasedValue in the Attribute collection will use the 
        /// actual CRM name, rather than the aliased one, even though the AttributeCollection's key will correctly
        /// use the aliased name.  So if the aliased Attribute Logical Name doesn't match the assumed attribute name
        /// value, check to see if the entity contains an AliasedValue with that key whose attribute logical name 
        /// doesn't match the key (the assumed bug), and mark it as being the aliased attribute
        if (!value && aliased != null && entity.Contains(attributeName))
        {
            var aliasedByKey = entity[attributeName] as AliasedValue;
            if (aliasedByKey != null && aliasedByKey.AttributeLogicalName != attributeName && 
                Object.ReferenceEquals(aliased, aliasedByKey))
            {
                value = true;
            }
        }

        return value;
    }

    /// <summary>
    /// Returns the Aliased Value for a column specified in a Linked entity
    /// </summary>
    /// <typeparam name="T">The type of the aliased attribute form the linked entity</typeparam>
    /// <param name="entity"></param>
    /// <param name="attributeName">The aliased attribute from the linked entity.  Can be preappeneded with the
    /// linked entities logical name and a period. ie "Contact.LastName"</param>
    /// <returns></returns>
    public static bool HasAliasedAttribute(this Entity entity, string attributeName)
    {
        string aliasedEntityName = SplitAliasedAttributeEntityName(ref attributeName);
        return entity.Attributes.Values.Any(a =>
            entity.IsAttributeAliasedValue(attributeName, aliasedEntityName, a as AliasedValue));
    }

    /// <summary>
    /// Handles spliting the attributeName if it is formated as "EntityAliasedName.AttributeName",
    /// updating the attribute name and returning the aliased EntityName
    /// </summary>
    /// <param name="attributeName"></param>
    /// <param name="aliasedEntityName"></param>
    private static string SplitAliasedAttributeEntityName(ref string attributeName)
    {
        string aliasedEntityName = null;
        if (attributeName.Contains('.'))
        {
            var split = attributeName.Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
            if (split.Length != 2)
            {
                throw new Exception("Attribute Name was specified for an Alaised Value with " + split.Length +
                " split parts, and two were expected.  Attribute Name = " + attributeName);
            }
            aliasedEntityName = split[0];
            attributeName = split[1];
        }

        return aliasedEntityName;
    }
于 2012-09-20T20:40:02.003 に答える