Reputation: 4598
I'm using fetchXML to join two entities and can only get the results for the first list. I'm probably making a silly mistake, but this has me mucking around for hours.
var query = @"
<fetch version='1.0' mapping='logical' distinct='true'>
<entity name='contact'>
<all-attributes />
<link-entity name='new_custom' from='new_contactid' to='contactid' link-type='inner'>
<all-attributes />
</link-entity>
</entity>
</fetch>
";
var fe = new FetchExpression(query);
var result = service.RetrieveMultiple(fe);
// This contact entity is properly filled
var contact = result.Entities.First().Cast<Contact>();
// This relationship is null, that's unexpected!
var custom = contact.new_contact_new_custom;
// Also null (not sure if this should work)
var related = contact.GetRelatedEntity<new_custom>("new_contact_new_custom", null);
I do see that the correct data was retrieved in the Attributes
and FormattedValues
properties on the Contact entity. But why isn't the relationship set up correctly? And how can I cast this data to the correct "custom" entity type?
Upvotes: 4
Views: 3394
Reputation: 71565
This is kind of a late answer, but the question helped me get on the right track so I thought I'd add my piece. Like Henk says in the comments to his answer, you can implement a relatively simple extension method to parse out the fields of a linked entity into another entity, then cast that as an early-bound type. All you need to know is the alias you used for the link-entity
element. Here's my implementation (EDIT: Added FormattedValues):
public static T ToRelatedEntity<T>(this Entity rawEntity, string relatedAlias)
where T:CrmEntity, new()
//CrmEntity is a base class I insert into my generated code
//to differentiate early-bound classes; you can use Entity
{
var result = new Entity(new T().LogicalName);
foreach(var attribute in rawEntity.Attributes
.Where(kvp=>kvp.Key
.StartsWith(relatedAlias + ".")))
{
var newName = attribute.Key.Replace(relatedAlias + ".", String.Empty);
result[newName] = ((AliasedValue)attribute.Value).Value;
}
foreach(var formattedValue in rawEntity.FormattedValues
.Where(kvp=>kvp.Key
.StartsWith(relatedAlias + ".")))
{
var newName = formattedValue.Key.Replace(relatedAlias + ".", String.Empty);
result.FormattedValues[newName] = formattedValue.Value;
}
return result.ToEntity<T>();
}
//usage
var query = new FetchExpression(fetchXml);
var response = service.RetrieveMultiple(query);
var contact = response.Entities[0].ToEntity<Contact>();
var newCustom = response.Entities[0].ToRelatedEntity<new_custom>("nc");
Very basic, but it seems to work quite well as long as you're not aliasing any of the fields you're retrieving, and you're retrieving enough information to populate a valid instance of the object (Id, primary field, etc). It's much better than trying to instantiate the related object with an initializer (especially since many fields like StateCode
are read-only).
Upvotes: 3
Reputation: 7918
First: are you sure your query returns early-bound objects? In my example below I assume you get Entity objects (late-bound), but for this example it would not really make much of a difference.
The query's response is basically a plain table of entity objects of the type you requested, in this case "contact". Fields of joined entities can be added to this table too, but they are wrapped in AliasedValue
objects. Their column names are prefixed by a LinkEntity
alias in order to prevent name collisions.
Your line var contact = result.Entities.Cast<Contact>();
suggests that variable contact
is of type Contact
, but it is in fact an IEnumerable<Contact>
. (Using var
is not always that helpful.) For this reason I suspect your last two lines do not even compile.
In the example below a fetch-query is sent to the Organization.svc endpoint. (Note the alias attribute.) Then field "new_name" of entity "new_custom" is taken from the first row of the query's resultset.
var fetchXml = @"
<fetch version='1.0' mapping='logical' distinct='true'>
<entity name='contact'>
<all-attributes />
<link-entity name='new_custom' alias='nc' from='new_contactid' to='contactid' link-type='inner'>
<all-attributes />
</link-entity>
</entity>
</fetch>
";
var query = new FetchExpression(fetchXml);
var response = service.RetrieveMultiple(query);
var contact = response.Entities[0];
var customField = contact.GetAttributeValue<AliasedValue>("nc.new_name");
string name = customField != null ? (string)customField.Value : null;
Upvotes: 5