I am trying to retrospectively write some unit tests for code I inherited.
One particular method is causing me problems within a Dynamics CRM 2011 plugin method with a LinkedEntity
FilterCondition
in a QueryExpression
Structure of Entities
Contact -(N:1 [Lookup])-> CustomEntity1 -(N:1 [Lookup])-> CustomEntity2
Aim of Method
The method I am trying to test creates a QueryExpression
that filters Contacts
based on attributes of CustomEntity1
, including the value of a LookupField
to CustomEntity2
.
Working Query Code
To test my understanding of the QueryExpression I have rewritten the code as below
QueryExpression query = new QueryExpression();
query.EntityName = "contact";
query.ColumnSet = new ColumnSet(true);
query.Criteria = new FilterExpression();
query.Criteria.FilterOperator = LogicalOperator.And;
FilterExpression filter = new FilterExpression(LogicalOperator.And);
FilterExpression filter1 = new FilterExpression(LogicalOperator.Or);
filter1.Conditions.Add(new ConditionExpression("new_datefield1", ConditionOperator.Null));
filter1.Conditions.Add(new ConditionExpression("new_datefield1", ConditionOperator.LessEqual, offset));
FilterExpression filter2 = new FilterExpression(LogicalOperator.Or);
filter2.Conditions.Add(new ConditionExpression("new_datefield2", ConditionOperator.Null));
filter2.Conditions.Add(new ConditionExpression("new_datefield2", ConditionOperator.LessEqual, offset));
filter.AddFilter(filter1);
filter.AddFilter(filter2);
query.Criteria.Filters.Add(filter);
// Create the link from the contact to the CustomEntity1 entity
LinkEntity linkHistory = new LinkEntity(Contact.EntityLogicalName, new_CustomEntity1.EntityLogicalName, Contact.AttributeNames.new_CustomEntity1Lookup, new_CustomEntity1.AttributeNames.Id, JoinOperator.Inner);
linkHistory.Columns = new ColumnSet(true);
linkHistory.EntityAlias = "custEnt1";
linkHistory.LinkCriteria = new FilterExpression();
linkHistory.LinkCriteria.FilterOperator = LogicalOperator.And;
#region code added to make the test work
// Create the CustomEntity2 condition
LinkEntity linkStatus = new LinkEntity(new_CustomEntity1.EntityLogicalName, new_CustomEntity2.EntityLogicalName, new_CustomEntity1.AttributeNames.new_CustomEntity2Lookup, new_CustomEntity2.AttributeNames.Id, JoinOperator.Inner);
linkStatus.Columns = new ColumnSet(true);
linkStatus.EntityAlias = "custEnt2";
linkStatus.LinkCriteria = new FilterExpression();
linkStatus.LinkCriteria.FilterOperator = LogicalOperator.And;
linkStatus.LinkCriteria.Conditions.Add(new ConditionExpression(new_CustomEntity2.AttributeNames.Id, ConditionOperator.Equal, indStatus.Id));
linkHistory.LinkEntities.Add(linkStatus);
#endregion
//some code removed for brevity
query.LinkEntities.Add(linkHistory);
This code returns a QueryExpression that is then used to run a test against, the tests pass when run with some test data set up in FakeXrmEasy. However, I need to make sure that my changes dont negatively impact the current code and logic so wanted to check my results against the current method before making any changes.
Non-Working Code
QueryExpression query = new QueryExpression();
query.PageInfo = new PagingInfo();
query.PageInfo.Count = fetchCount;
query.PageInfo.PageNumber = pageNumber;
query.PageInfo.PagingCookie = null;
// Setup the query for the contact entity
query.EntityName = Contact.EntityLogicalName;
// Specify the columns to retrieve
query.ColumnSet = new ColumnSet(true);
query.Criteria = new FilterExpression();
query.Criteria.FilterOperator = LogicalOperator.And;
FilterExpression filter1 = new FilterExpression();
filter1.FilterOperator = LogicalOperator.Or;
// Create the e2sds_lastcontactdate condition
ConditionExpression condition1 = new ConditionExpression();
condition1.AttributeName = Contact.AttributeNames.new_DateField1;
condition1.Operator = ConditionOperator.Null;
// Create the e2sds_lastcontactdate condition
ConditionExpression condition2 = new ConditionExpression();
condition2.AttributeName = Contact.AttributeNames.new_DateField1;
condition2.Operator = ConditionOperator.LessEqual;
condition2.Values.Add(offset);
FilterExpression filter2 = new FilterExpression();
filter2.FilterOperator = LogicalOperator.Or;
// Create the Last Third Party Contact condition
ConditionExpression ltpcCond1 = new ConditionExpression();
condition1.AttributeName = Contact.AttributeNames.new_DateField2;
condition1.Operator = ConditionOperator.Null;
// Create the Last Third Party Contactcondition
ConditionExpression ltpcCond2 = new ConditionExpression();
condition2.AttributeName = Contact.AttributeNames.new_DateField2;
condition2.Operator = ConditionOperator.LessEqual;
condition2.Values.Add(offset);
filter2.Conditions.AddRange(ltpcCond1, ltpcCond2);
query.Criteria.Filters.Add(filter1);
query.Criteria.Filters.Add(filter2);
// Create the link from the contact to the CustomEntity1 entity
LinkEntity linkHistory = new LinkEntity();
linkHistory.JoinOperator = JoinOperator.Natural;
linkHistory.LinkFromEntityName = Contact.EntityLogicalName;
linkHistory.LinkFromAttributeName = Contact.AttributeNames.new_CustomEntity1Lookup;
linkHistory.LinkToEntityName = new_CustomEntity1.EntityLogicalName;
linkHistory.LinkToAttributeName = new_CustomEntity1.AttributeNames.Id;
linkHistory.LinkCriteria = new FilterExpression();
linkHistory.LinkCriteria.FilterOperator = LogicalOperator.And;
#region this code throws a specified cast not valid exception
// Create the e2sds_statusid condition
ConditionExpression condition3 = new ConditionExpression();
condition3.AttributeName = new_CustomEntity1.AttributeNames.new_CustomEntity2LookupField;
condition3.Operator = ConditionOperator.Equal;
condition3.Values.Add(status.Id);
#endregion
linkHistory.LinkCriteria.Conditions.Add(condition3);
//removed code for brevity
query.LinkEntities.Add(linkHistory);
What I have tried
Unfortunately as the QueryExpression
is executed in a RetrieveMultiple
request I can't step into the code to find out what data is causing the invalid cast, however, commenting out the region thats marked in the non-working code means that the exception goes away.
Rewriting the code is an option - thats how I know the TestData works as the working QueryExpression code I wrote returns a set of data.
I don't know if the issue is with the QueryExpression or FakeXrmEasy at the moment so even providing methods to trace the root cause of the issue would be helpful if an actual resolution cannot be provided.
Note: The names of the custom entities, fields, and lookups have been changed for confidentiality reasons. If the names don't match up its likely a manual transposition error than code issues.
This is related to the way crmsvcutil generates proxy types. There might be differences between different versions.
Just updated the issue on GitHub