diff --git a/CHANGELOG.md b/CHANGELOG.md
index c8da62a3..01778839 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+## [2.3.3]
+
+### Changed
+
+ - Resolves an issue with query evaluation and MultiOptionSets when using late bound entities or if type information is not present. - https://github.com/DynamicsValue/fake-xrm-easy/issues/66
+
## [2.3.2]
### Changed
diff --git a/src/FakeXrmEasy.Core/Extensions/XmlExtensionsForFetchXml.cs b/src/FakeXrmEasy.Core/Extensions/XmlExtensionsForFetchXml.cs
index 8668d083..ddc3a6b9 100644
--- a/src/FakeXrmEasy.Core/Extensions/XmlExtensionsForFetchXml.cs
+++ b/src/FakeXrmEasy.Core/Extensions/XmlExtensionsForFetchXml.cs
@@ -958,12 +958,16 @@ public static object GetConditionExpressionValueCast(string value, IXrmFakedCont
{
throw new Exception(string.Format("When trying to parse value for entity {0} and attribute {1}: {2}", sEntityName, sAttributeName, e.Message));
}
-
}
}
}
-
+#if FAKE_XRM_EASY_9
+ if(op == ConditionOperator.ContainValues || op == ConditionOperator.DoesNotContainValues)
+ {
+ return GetValueBasedOnType(typeof(OptionSetValueCollection), value);
+ }
+#endif
// Try parsing a guid
Guid gOut = Guid.Empty;
if (Guid.TryParse(value, out gOut))
diff --git a/src/FakeXrmEasy.Core/FakeXrmEasy.Core.csproj b/src/FakeXrmEasy.Core/FakeXrmEasy.Core.csproj
index 1d6ec444..a1016df1 100644
--- a/src/FakeXrmEasy.Core/FakeXrmEasy.Core.csproj
+++ b/src/FakeXrmEasy.Core/FakeXrmEasy.Core.csproj
@@ -9,7 +9,7 @@
net452
net452
FakeXrmEasy.Core
- 2.3.2
+ 2.3.3
Jordi Montaña
Dynamics Value
FakeXrmEasy Core
diff --git a/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.ContainsValues.cs b/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.ContainsValues.cs
index dfa1ade6..39321eb6 100644
--- a/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.ContainsValues.cs
+++ b/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.ContainsValues.cs
@@ -1,8 +1,8 @@
#if FAKE_XRM_EASY_9
-
using System.Collections.Generic;
using System.Linq.Expressions;
using FakeXrmEasy.Extensions;
+using Microsoft.Xrm.Sdk;
namespace FakeXrmEasy.Query
{
@@ -10,7 +10,7 @@ public static partial class ConditionExpressionExtensions
{
internal static Expression ToContainsValuesExpression(this TypedConditionExpression tc, Expression getAttributeValueExpr, Expression containsAttributeExpr)
{
- var leftHandSideExpression = tc.AttributeType.GetAppropiateCastExpressionBasedOnType(getAttributeValueExpr, null);
+ var leftHandSideExpression = typeof(OptionSetValueCollection).GetAppropiateCastExpressionBasedOnType(getAttributeValueExpr, null);
var rightHandSideExpression = Expression.Constant(OptionSetValueCollectionExtensions.ConvertToHashSetOfInt(tc.CondExpression.Values, isOptionSetValueCollectionAccepted: false));
return Expression.AndAlso(
diff --git a/src/FakeXrmEasy.Core/Query/TypeCastExpressions.cs b/src/FakeXrmEasy.Core/Query/TypeCastExpressions.cs
index 60471560..bac01917 100644
--- a/src/FakeXrmEasy.Core/Query/TypeCastExpressions.cs
+++ b/src/FakeXrmEasy.Core/Query/TypeCastExpressions.cs
@@ -58,7 +58,7 @@ internal static Expression GetAppropiateCastExpressionBasedOnAttributeTypeOrValu
return GetAppropiateCastExpressionDefault(input, value); //any other type
}
- return GetAppropiateCastExpressionBasedOnValueInherentType(input, value); //Dynamic entities
+ return GetAppropiateCastExpressionBasedOnValueInherentType(input, value); //Dynamic / late bound entities
}
internal static Expression GetAppropiateCastExpressionBasedGuid(Expression input)
diff --git a/src/FakeXrmEasy.Core/Query/TypedConditionExpression.cs b/src/FakeXrmEasy.Core/Query/TypedConditionExpression.cs
index 6835222f..86039788 100644
--- a/src/FakeXrmEasy.Core/Query/TypedConditionExpression.cs
+++ b/src/FakeXrmEasy.Core/Query/TypedConditionExpression.cs
@@ -9,17 +9,17 @@
namespace FakeXrmEasy.Query
{
///
- /// A condition expression with a decorated type
+ /// A condition expression with a decorated type of the attribute in the condition expression
///
public class TypedConditionExpression
{
///
- ///
+ /// The original condition expression
///
public ConditionExpression CondExpression { get; set; }
///
- ///
+ /// The attribute type of the condition expression, if known (i.e. was generated via a strongly-typed generation tool)
///
public Type AttributeType { get; set; }
@@ -29,7 +29,7 @@ public class TypedConditionExpression
public bool IsOuter { get; set; }
///
- ///
+ /// Creates a TypedConditionExpression from an existing ConditionExpression with no attribute type information
///
///
public TypedConditionExpression(ConditionExpression c)
diff --git a/tests/FakeXrmEasy.Core.Tests/FakeContextTests/TranslateQueryExpressionTests/OperatorTests/MultiSelectOptionSet/MultiSelectOptionSetTests.cs b/tests/FakeXrmEasy.Core.Tests/FakeContextTests/TranslateQueryExpressionTests/OperatorTests/MultiSelectOptionSet/MultiSelectOptionSetTests.cs
index 88db0e87..952c6eb2 100644
--- a/tests/FakeXrmEasy.Core.Tests/FakeContextTests/TranslateQueryExpressionTests/OperatorTests/MultiSelectOptionSet/MultiSelectOptionSetTests.cs
+++ b/tests/FakeXrmEasy.Core.Tests/FakeContextTests/TranslateQueryExpressionTests/OperatorTests/MultiSelectOptionSet/MultiSelectOptionSetTests.cs
@@ -500,9 +500,6 @@ public void When_executing_a_query_expression_containvalues_operator_throws_exce
[Fact]
public void When_executing_a_query_expression_containvalues_operator_throws_exception_for_optionsetvaluecollection_right_hand_side()
{
-
-
-
_service.Create(new Contact { FirstName = "1,2,3", new_MultiSelectAttribute = new OptionSetValueCollection() { new OptionSetValue(1), new OptionSetValue(2) } });
var qe = new QueryExpression("contact");
@@ -514,9 +511,6 @@ public void When_executing_a_query_expression_containvalues_operator_throws_exce
[Fact]
public void When_executing_a_query_expression_containvalues_operator_returns_partial_matches_for_single_int_array_right_hand_side()
{
-
-
-
_service.Create(new Contact { FirstName = "1,2", new_MultiSelectAttribute = new OptionSetValueCollection() { new OptionSetValue(1), new OptionSetValue(2) } });
_service.Create(new Contact { FirstName = "2", new_MultiSelectAttribute = new OptionSetValueCollection() { new OptionSetValue(2) } });
_service.Create(new Contact { FirstName = "2,3", new_MultiSelectAttribute = new OptionSetValueCollection() { new OptionSetValue(2), new OptionSetValue(3) } });
@@ -558,9 +552,6 @@ public void When_executing_a_query_expression_containvalues_operator_returns_par
[Fact]
public void When_executing_a_query_expression_containvalues_operator_returns_partial_matches_for_int_params_right_hand_side()
{
-
-
-
_service.Create(new Contact { FirstName = "1,2", new_MultiSelectAttribute = new OptionSetValueCollection() { new OptionSetValue(1), new OptionSetValue(2) } });
_service.Create(new Contact { FirstName = "2", new_MultiSelectAttribute = new OptionSetValueCollection() { new OptionSetValue(2) } });
_service.Create(new Contact { FirstName = "2,3", new_MultiSelectAttribute = new OptionSetValueCollection() { new OptionSetValue(2), new OptionSetValue(3) } });
diff --git a/tests/FakeXrmEasy.Core.Tests/FakeXrmEasy.Core.Tests.csproj b/tests/FakeXrmEasy.Core.Tests/FakeXrmEasy.Core.Tests.csproj
index 76bae833..30bf304d 100644
--- a/tests/FakeXrmEasy.Core.Tests/FakeXrmEasy.Core.Tests.csproj
+++ b/tests/FakeXrmEasy.Core.Tests/FakeXrmEasy.Core.Tests.csproj
@@ -12,7 +12,7 @@
true
FakeXrmEasy.CoreTests
- 2.3.2
+ 2.3.3
Jordi Montaña
Dynamics Value S.L.
Internal Unit test suite for FakeXrmEasy.Core package
@@ -141,22 +141,22 @@
-
+
-
+
-
+
-
+
-
+
-
+
diff --git a/tests/FakeXrmEasy.Core.Tests/Issues/Issue467.cs b/tests/FakeXrmEasy.Core.Tests/Issues/Issue467.cs
new file mode 100644
index 00000000..90bc00c0
--- /dev/null
+++ b/tests/FakeXrmEasy.Core.Tests/Issues/Issue467.cs
@@ -0,0 +1,89 @@
+#if FAKE_XRM_EASY_9
+using Microsoft.Xrm.Sdk;
+using Microsoft.Xrm.Sdk.Query;
+using System;
+using System.Collections.Generic;
+using Xunit;
+
+namespace FakeXrmEasy.Tests.Issues
+{
+ // https://github.com/jordimontana82/fake-xrm-easy/issues/467
+
+ public class Issue467 : FakeXrmEasyTestsBase
+ {
+ Entity _product;
+ // setup
+
+ public Issue467()
+ {
+ _product = new Entity("product", Guid.NewGuid());
+ var productCategories = new OptionSetValueCollection
+ {
+ new OptionSetValue(121720003),
+ new OptionSetValue(121720002)
+ };
+ _product["name"] = "Test product";
+ _product["abc_productcategories"] = productCategories;
+
+ _context.Initialize(new List { _product });
+ }
+
+ // This test currently fails - Object reference not set to an instance of an object
+ [Fact]
+ public void When_A_Query_Expression_Has_A_ContainsValues_Clause_On_A_MultiSelectOptionSet_It_Should_Not_Fail()
+ {
+ var query = new QueryExpression("product");
+ query.ColumnSet = new ColumnSet(new[] { "name" });
+ query.Criteria.AddCondition("abc_productcategories", ConditionOperator.ContainValues, 121720003, 121720002);
+
+ EntityCollection products = _service.RetrieveMultiple(query);
+ Assert.Equal(_product.Id, products.Entities[0].Id);
+ }
+
+ // This test currently fails - Object reference not set to an instance of an object
+ [Fact]
+ public void When_A_Query_Expression_Has_A_ContainsValues_Array_Clause_On_A_MultiSelectOptionSet_It_Should_Not_Fail()
+ {
+ var query = new QueryExpression("product");
+ query.Criteria.AddCondition("abc_productcategories", ConditionOperator.ContainValues, new[] { 121720003, 121720002 });
+
+ EntityCollection products = _service.RetrieveMultiple(query);
+ Assert.Equal(_product.Id, products.Entities[0].Id);
+ }
+
+ // This test currently fails - Object reference not set to an instance of an object
+ [Fact]
+ public void When_A_Query_Expression_Has_A_DoesNotContainValues_Clause_On_A_MultiSelectOptionSet_It_Should_Not_Fail()
+ {
+ var query = new QueryExpression("product");
+ query.Criteria.AddCondition("abc_productcategories", ConditionOperator.DoesNotContainValues, 121720003, 121720002);
+
+ EntityCollection products = _service.RetrieveMultiple(query);
+ Assert.Empty(products.Entities);
+ }
+
+ // This test should also pass, but fails with this error
+ // When using arithmetic values in Fetch a ProxyTypesAssembly must be used in order to know which types to cast values to.
+ // This is a requirement of FXE, but I don't think any of the Early bound classes have multi-select option set attributes so difficult to test in the core project...
+ //
+ [Fact]
+ public void When_A_FetchXml_Query_Has_A_Contains_Clause_On_A_MultiSelectOptionSet_It_Should_Not_Fail()
+ {
+ string fetchXml = @"
+
+
+
+ 121720003
+ 121720002
+
+
+
+ ";
+
+ EntityCollection products = _service.RetrieveMultiple(new FetchExpression(fetchXml));
+ Assert.Equal(_product.Id, products.Entities[0].Id);
+ }
+
+ }
+}
+#endif
\ No newline at end of file