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