diff --git a/CHANGELOG.md b/CHANGELOG.md index aec5cde1..b677ca76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## [2.5.2] + +### Added + +- **Alpha**: Initial support for AboveOrEqual in ConditionExpressions + ## [2.5.1] ### Changed diff --git a/src/FakeXrmEasy.Core/FakeXrmEasy.Core.csproj b/src/FakeXrmEasy.Core/FakeXrmEasy.Core.csproj index dfc0c1ca..f0bce7b7 100644 --- a/src/FakeXrmEasy.Core/FakeXrmEasy.Core.csproj +++ b/src/FakeXrmEasy.Core/FakeXrmEasy.Core.csproj @@ -8,7 +8,7 @@ net452 net452 FakeXrmEasy.Core - 2.5.1 + 2.5.2 Jordi Montaña Dynamics Value FakeXrmEasy Core @@ -102,22 +102,22 @@ - + - + - + - + - + - + diff --git a/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.AboveOrEqual.cs b/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.AboveOrEqual.cs new file mode 100644 index 00000000..13b71b8c --- /dev/null +++ b/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.AboveOrEqual.cs @@ -0,0 +1,55 @@ +#if FAKE_XRM_EASY_365 || FAKE_XRM_EASY_9 +using FakeXrmEasy.Abstractions; +using FakeXrmEasy.Query; +using Microsoft.Xrm.Sdk; +using System; +using System.Linq; +using System.Linq.Expressions; + +namespace FakeXrmEasy.Query +{ + internal static partial class ConditionExpressionExtensions + { + internal static Expression ToAboveOrEqualExpression(this TypedConditionExpression tc, Expression getAttributeValueExpr, Expression containsAttributeExpr, IXrmFakedContext context) + { + var c = tc.CondExpression; + var entityLogicalName = !string.IsNullOrEmpty(c.EntityName) ? c.EntityName : tc.QueryExpression.EntityName; + + if(!context.Relationships.Any(r => r.IsHierarchical && r.Entity1LogicalName.Equals(entityLogicalName) && r.Entity1Attribute.Equals(c.AttributeName))) + { + return tc.ToEqualExpression(context, getAttributeValueExpr, containsAttributeExpr); + } + + //Retrieve the hierarchical relationship to determine the parent attribute + var hierarchicalRelationship = context.Relationships.FirstOrDefault(r => r.IsHierarchical && r.Entity1LogicalName.Equals(entityLogicalName) && r.Entity1Attribute.Equals(c.AttributeName)); + + //Retrieve initial record from the hierarchical tree + var currentRecord = context.CreateQuery(entityLogicalName).FirstOrDefault(e => ((Guid)e.Attributes[c.AttributeName]) == ((Guid)c.Values[0])); + if(currentRecord == null) + { + return tc.ToEqualExpression(context, getAttributeValueExpr, containsAttributeExpr); + } + + //Iterrate through parents via the relationship attributes to walk through the entire hierarchy and build a list of identifiers of the nodes in the hierarchy. + RetrieveParentEntity(context, hierarchicalRelationship, currentRecord, c.Values); + + return tc.ToInExpression(getAttributeValueExpr, containsAttributeExpr); + } + + private static void RetrieveParentEntity(IXrmFakedContext context, XrmFakedRelationship hierarchicalRelationship, Entity currentRecord, DataCollection values) + { + if (!currentRecord.Attributes.ContainsKey(hierarchicalRelationship.Entity2Attribute) || currentRecord.Attributes[hierarchicalRelationship.Entity2Attribute] == null) + { + return; + } + + var parentRecord = context.CreateQuery(hierarchicalRelationship.Entity1LogicalName).FirstOrDefault(e => ((Guid)e.Attributes[hierarchicalRelationship.Entity1Attribute]) == ((EntityReference)currentRecord.Attributes[hierarchicalRelationship.Entity2Attribute]).Id); + if (parentRecord != null) + { + values.Add(parentRecord.Id); + RetrieveParentEntity(context, hierarchicalRelationship, parentRecord, values); + } + } + } +} +#endif \ No newline at end of file diff --git a/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.cs b/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.cs index 8a5fa6ba..d5cf8eda 100644 --- a/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.cs +++ b/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.cs @@ -288,6 +288,12 @@ internal static Expression ToExpression(this TypedConditionExpression c, QueryEx break; #endif +#if FAKE_XRM_EASY_365 || FAKE_XRM_EASY_9 + case ConditionOperator.AboveOrEqual: + operatorExpression = c.ToAboveOrEqualExpression(getNonBasicValueExpr, containsAttributeExpression, context); + break; +#endif + default: throw UnsupportedExceptionFactory.New(context.LicenseContext.Value, string.Format("Operator {0} not yet implemented for condition expression", c.CondExpression.Operator.ToString())); diff --git a/tests/FakeXrmEasy.Core.Tests/FakeXrmEasy.Core.Tests.csproj b/tests/FakeXrmEasy.Core.Tests/FakeXrmEasy.Core.Tests.csproj index 2ab2cc4f..7974fb3c 100644 --- a/tests/FakeXrmEasy.Core.Tests/FakeXrmEasy.Core.Tests.csproj +++ b/tests/FakeXrmEasy.Core.Tests/FakeXrmEasy.Core.Tests.csproj @@ -11,7 +11,7 @@ true FakeXrmEasy.CoreTests - 2.5.1 + 2.5.2 Jordi Montaña Dynamics Value S.L. Internal Unit test suite for FakeXrmEasy.Core package @@ -114,22 +114,22 @@ - + - + - + - + - + - + @@ -137,22 +137,22 @@ - + - + - + - + - + - + diff --git a/tests/FakeXrmEasy.Core.Tests/Query/TranslateQueryExpressionTests/FakeContextTestTranslateQueryExpression.cs b/tests/FakeXrmEasy.Core.Tests/Query/TranslateQueryExpressionTests/FakeContextTestTranslateQueryExpression.cs index 12c3f711..37beabd2 100644 --- a/tests/FakeXrmEasy.Core.Tests/Query/TranslateQueryExpressionTests/FakeContextTestTranslateQueryExpression.cs +++ b/tests/FakeXrmEasy.Core.Tests/Query/TranslateQueryExpressionTests/FakeContextTestTranslateQueryExpression.cs @@ -875,5 +875,54 @@ public void Should_Not_Fail_On_Conditions_In_Link_Entities_Multiple() Assert.Single(invoiceDetails.Entities); Assert.Equal(invoicedetail02.Id, invoiceDetails.Entities[0].Id); } + +#if FAKE_XRM_EASY_365 || FAKE_XRM_EASY_9 + [Fact] + public void When_executing_a_queryexpression_with_aboveorequal_all_records_in_hierarchy_are_returned() + { + Entity parentParentAccount = new Entity("account"); + parentParentAccount.Id = Guid.NewGuid(); + parentParentAccount.Attributes.Add("name", "Parent Parent Account"); + + Entity parentAccount = new Entity("account"); + parentAccount.Id = Guid.NewGuid(); + parentAccount.Attributes.Add("name", "Parent Account"); + parentAccount.Attributes.Add("parentaccountid", new EntityReference("account", parentParentAccount.Id)); + + Entity childAccount = new Entity("account"); + childAccount.Id = Guid.NewGuid(); + childAccount.Attributes.Add("name", "Child Account"); + childAccount.Attributes.Add("parentaccountid", new EntityReference("account", parentAccount.Id)); + + //Important step to specify the hierarchical relation ship. + _context.AddRelationship("account_parent_account", new XrmFakedRelationship + { + RelationshipType = XrmFakedRelationship.FakeRelationshipType.OneToMany, + IsHierarchical = true, + Entity1LogicalName = "account", + Entity1Attribute = "accountid", + Entity2LogicalName = "account", + Entity2Attribute = "parentaccountid" + }); + + _service.Create(parentParentAccount); + _service.Create(parentAccount); + _service.Create(childAccount); + + QueryExpression query = new QueryExpression { EntityName = "account", ColumnSet = new ColumnSet(true) }; + query.Criteria.AddCondition("accountid", ConditionOperator.AboveOrEqual, childAccount.Id); + + var result = _service.RetrieveMultiple(query); + + Assert.NotNull(result); + Assert.NotNull(result.Entities); + Assert.True(result.Entities.Count == 3); + + foreach (var item in result.Entities) + { + Assert.True(item.Id == childAccount.Id || item.Id == parentAccount.Id || item.Id == parentParentAccount.Id); + } + } +#endif } } \ No newline at end of file