1

I'm using QueryExpression often but this far, it's been a straigh-forward get-this-from-that or put-this-in-that. Recently, I learned that there's something called LinkedEntity and I started looking for it. As an example I got inspired by a related question here on SO and I started to create an expression for getting all the members of a list given it's guid.

All the examples I've found follow the same pattern, though - as this example illustrates. From this question, I've learned that it's an obsolete approach (CRM 4.0). I've failed founding a more up-to-date example and I'm not sure how to design the linkage.

Anybody cares to provide a sample code?

Guid guid = ...;
QueryExpression request = new QueryExpression
{
  EntityName = "account",
  ColumnSet = new ColumnSet(true),
  LinkEntities= ???,                        // How to link the entities correctly?
  Criteria = new FilterExpression { ??? }   // How to filter for *guid* only?
};

I've created a fetch-XML linking two entities but I'm not clear on how to translate it to QueryExpression entity. I've got something like this. Any suggestions?

LinkEntity linkListToMember = new LinkEntity(
  "list", "listmember", "listid", "listid", JoinOperator.Natural);
LinkEntity linkMemberToContact = new LinkEntity(
  "listmember", "account", "entityid", "accountid", JoinOperator.Natural);
Community
  • 1
  • 1
  • Can you write a SQL statement for what you're trying to do? – Daryl Jan 14 '13 at 16:21
  • @Daryl Not precisely. I can draw some guidelines with double joins and such but darn if I can set it up in SQL without testing. And I'm not **that** good at either CRM or SQL... Is it all right if I explain my intention by words? –  Jan 14 '13 at 19:16
  • Are you looking for a listing of all the members of: (a) a specific marketing list, (b) any, but a single, marketing list, (c) a range of specified marketing lists or (d) all the marketing lists in the system? I'm creating an add-on to be deployed at the end of the month and I'll be blogging on this very subject, if you can wait a few days. (As always, my blog is somewhere in the cyberspace that has to do with words, pressing and my name.) – Konrad Viltersten Jan 14 '13 at 19:27
  • @Daryl I've seen people ask for the SQL code at numerous occasions. Is there a tool to convert *SQL* to *QueryExpression* or is it just to get definition of the nature of the question? (Such a tool would be mighty cool...) – Konrad Viltersten Jan 14 '13 at 19:28
  • @KonradViltersten I belive CRM provides a couple calls to convert Fetch XML to Query Expressions, and Query Expressions to Fetch XML, but none currently exists that I know of to convert SQL to Query Expression. I however wrote some extension methods to convert Query Expressions to SQL. The true reason I'd like the SQL is so I know exactly what the asker is attempting to do, and can provide the exact Query Expression for their needs. – Daryl Jan 14 '13 at 21:27
  • @Daryl Would you care to elaborate on the extension methods of yours? Sounds exciting. Don't mean to put you in a hot spot but such a tool might be of huge usability in some cases. If (when?) fully developed, it might prove to be a fast-lane ticket to MVP status, you know. :) – Konrad Viltersten Jan 14 '13 at 21:55
  • @CRMconfusee I'm assuming you're working with two entities, and you want to either pull data from both, or limit what is returned in both? If you could describe in words with your entity names, and the attributes (columns) for the entities that contain the data that should match, I'll give it a shot. :) – Daryl Jan 14 '13 at 22:05
  • @KonradViltersten I really don't have any hope that Microsoft will listen to anything I have say. And the only reason any developers should be using query expressions in my mind, is because they don't know Linq, or for some reason can't use the early bound types... Prehaps I'll blog about it sometime though... – Daryl Jan 14 '13 at 22:07
  • @Daryl In the last four months I've only had projects with late bound. Various reasons, same result. But I'm guessing that you're not feeling that your solution is braggable enough to be spread out. I do recon that and respect the decision. If you could blog about it, at least drawing some framework around it, it could definitely help others to get started and bring it further. – Konrad Viltersten Jan 14 '13 at 23:39
  • @KonradViltersten I've publish an extension method that you may enjoy: http://stackoverflow.com/a/15296034/227436 – Daryl Mar 08 '13 at 14:41

2 Answers2

1

A Link Entity serves as your SQL Join. Use the Constructor with the from and to entity and attribute names

public LinkEntity(
    string linkFromEntityName, // This is the Entity Logical Name of your Query Expression, or parent LinkEntity
    string linkToEntityName, // This is the Entity Logical Name of the entity you'd like to link to
    string linkFromAttributeName, // This is the attribute name on your from entity, containing your join key
    string linkToAttributeName, // This is the attribute name on your to entity, containing your join key
    JoinOperator joinOperator) // This is the type of Join you'd like to perform

Using the Link Entity, you can add Link Criteria to filter the results returned. You can also add Columns and return data from related entities.

Edit, an Addition to Konrad's Answer

If the 52 lines of code that Konrad lists seems too verbose, this will do the same exact thing, in 15 lines, using the extension methods defined here.

Guid guid = ...;
IOrganizationService service;

QueryExpression request = new QueryExpression("account")
{
  ColumnSet = new ColumnSet("name", "region"),
};
request.Criteria.AddCondition("name", ConditionOperator.NotNull);
request.Criteria.AddCondition("region", ConditionOperator.NotNull);

var listLink = request.AddLink("listmember", "accountid", "entityid").
  AddChildLink("list", "listid");

listLink.Columns.AddColumn("listname");
listLink.LinkCriteria.AddCondition("listid", ConditionOperator.Equal, guid);
Community
  • 1
  • 1
Daryl
  • 18,592
  • 9
  • 78
  • 145
  • +1 for shortness. Nice! Is the code tested or is it just a suggestion? – Konrad Viltersten Jan 15 '13 at 15:35
  • @KonradViltersten I haven't tested the exact code posted, but the extension methods have been thoroughly used in our production environment. Only the `AddChildLink` extension method is actually used in the code posted, and one could use the normal `AddLink` with the to and from entity names defined. – Daryl Jan 15 '13 at 15:53
0

Here's a method for getting all the members of a marketing list, given that you have its guid and a server connection. What you did with the conditions is right spot on but you need to jack the one into the other. On Saturday I'll put it with a larger description on my blog.

Guid guid = ...;
IOrganizationService service;

QueryExpression request = new QueryExpression
{
  EntityName = "account",
  ColumnSet = new ColumnSet("name", "region"),
  LinkEntities =
  {
    new LinkEntity 
    { 
      JoinOperator = JoinOperator.Inner, 
      LinkFromEntityName = "account", 
      LinkFromAttributeName = "accountid", 
      LinkToEntityName = "listmember", 
      LinkToAttributeName = "entityid",
      LinkCriteria = { },
      LinkEntities =
      {
        new LinkEntity
        {
          JoinOperator = JoinOperator.Inner,
          Columns = new ColumnSet("listname"),
          EntityAlias = "MarketingList",
          LinkFromEntityName = "listmember",
          LinkFromAttributeName = "listid",
          LinkToEntityName = "list",
          LinkToAttributeName = "listid",
          LinkCriteria = { Conditions = 
          {
            new ConditionExpression("listid", ConditionOperator.Equal, guid) 
          } }
        }
      }
    } 
  },
  Criteria = new FilterExpression
  {
    Filters =
    {
      new FilterExpression
      {
        FilterOperator = LogicalOperator.And,
        Conditions =
        {
          new ConditionExpression("name", ConditionOperator.NotNull),
          new ConditionExpression("region", ConditionOperator.NotNull)
        }
      }
    }
  }
};

Then, of course you need to execute the call.

EntityCollection result = service.RetrieveMultiple(request);

Finally, you might want to order and structure whatever you've got from the server. I'm using the following LINQ-to-Data expression.

IEnumerable<Member> output = result.Entities.Where(element 
  => Member.IsWellFormed(element)).Select(element
    => new Member(element));

More on the subject, see the blog.

Konrad Viltersten
  • 36,151
  • 76
  • 250
  • 438