Thursday, June 07, 2007 4:39 AM
richard
Microsoft Dynamics CRM Web Services: How to join entities to filter QueryExpression results
There just isn't much information out there about Microsoft Dynamics CRM Web Services... at least not when compared to other technologies. And with Silverlight and Surface and PopFly burning up the MS blogosphere, it's kind of understandable.
Still, there are those of us who need to get things done in Microsoft Dynamics CRM... and so here's my little contribution.
It's probably not obvious what I was trying to accomplish from the title of this post. It's very simple, though. Let's say I want to get a list of customers from the CRM filtered by criteria in a table that is several 'joins' away. So in SQL using the sample AdventureWorks DB it looks like this:
SELECT
* FROM Sales.Customer c
INNER JOIN Sales.CustomerAddress ca
ON c.CustomerId = ca.CustomerId
INNER JOIN Person.Address a
ON ca.AddressId = a.AddressId
WHERE City = 'Montreal'
So now my CRM Web Services goal is more clear. I had this requirement and there wasn't much I could find about doing it, but I managed to cobble together this sample. Keep in mind the code I'm going to show you is *not* representative of our CRM or its custom fields, it's just how you would do it, mirroring the AdventureWorks SQL snippet above.
First thing I found handy was a nice built-in entity and relationship list the CRM comes with. Navigate to http://YourCrmServerName/sdk/list.aspx to get to the Microsoft CRM MetaData browser with all the entities you can connect to from your CRM's web service. (The service URL, btw should be http://YouCrmServername//mscrmservices/2006/crmservice.asmx). Here's my real-but-pseudo code showing how to do this many-to-many join scenario to filter results.
CrmService cs = new CrmService();
cs.Credentials = new System.Net.NetworkCredential("user", "password", "domain");
QueryExpression customerQuery = new QueryExpression();
customerQuery.EntityName = EntityName.customer.ToString();
// Prepare the first 'INNER JOIN' to the many to many table:
LinkEntity innerJoinOne = new LinkEntity();
// LinkFromEntityName == same as our QueryExpression.EntityName
innerJoinOne.LinkFromEntityName = EntityName.customer.ToString();
// attribute is like the 'ON' clause
innerJoinOne.LinkFromAttributeName = "customerId";
innerJoinOne.LinkToEntityName = EntityName.customeraddress.ToString();
// don't assume this is the same as the primary key table
// look it up in the metadata browser!
innerJoinOne.LinkToAttributeName = "customerId";
// Prepare the second join, to the table that holds the criteria
// field we want to specify
LinkEntity innerJoinTwo = new LinkEntity();
// here LinkFromEntityName should == same as the LinkToEntityName in our
// first LinkEntity
innerJoinTwo.LinkFromEntityName = EntityName.customeraddress.ToString();
innerJoinTwo.LinkFromAttributeName = "addressId";
innerJoinTwo.LinkToEntityName = EntityName.address.ToString();
linkinnerJoinTwoTwo.LinkToAttributeName = "addressId";
// now make the CRM WS equivalent of our 'WHERE' clause:
ConditionExpression where = new ConditionExpression();
where.Operator = ConditionOperator.Equal;
where.AttributeName = "city";
where.Values = new object[] { "Montreal" };
// in this case, we attach the ConditionExpression inside
// a FilterExpression, then to the LinkCriteria property
// on the 2nd LinkEntity
FilterExpression filter = new FilterExpression();
filter.Conditions = new ConditionExpression[] { condition };
innerJoinTwo.LinkCriteria = filter;
// Then you attach the 2nd LinkEntity, to the first one
innerJoinOne.LinkEntities = new LinkEntity[] { innerJoinTwo };
// Then the first one to the QueryExpression.
customerQuery.LinkEntities = new LinkEntity[] { innerJoinOne };
BusinessEntityCollection customerEntities = cs.RetrieveMultiple(customerQuery);
I hope that makes some sense, and saves some of the frustration I had in getting there. If there's anything unclear, please comment and I'll try to answer.
Later,
Richard