diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9350c529..288b019e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+## [2.7.0]
+
+### Added
+
+- Added support for link entities that use the `Exists` join operator - https://github.com/DynamicsValue/fake-xrm-easy/issues/141
+
## [2.6.0]
### Added
diff --git a/src/FakeXrmEasy.Core/FakeXrmEasy.Core.csproj b/src/FakeXrmEasy.Core/FakeXrmEasy.Core.csproj
index 28ae8631..055e08e9 100644
--- a/src/FakeXrmEasy.Core/FakeXrmEasy.Core.csproj
+++ b/src/FakeXrmEasy.Core/FakeXrmEasy.Core.csproj
@@ -25,6 +25,7 @@
True
true
False
+ 2.7.0
diff --git a/src/FakeXrmEasy.Core/Query/LinkEntityQueryExtensions.cs b/src/FakeXrmEasy.Core/Query/LinkEntityQueryExtensions.cs
index 68af9083..a4838602 100644
--- a/src/FakeXrmEasy.Core/Query/LinkEntityQueryExtensions.cs
+++ b/src/FakeXrmEasy.Core/Query/LinkEntityQueryExtensions.cs
@@ -87,8 +87,16 @@ internal static IQueryable ToQueryable(this LinkEntity le, IXrmFakedCont
, (x, y) => x.outerEl
.JoinAttributes(y, new ColumnSet(true), leAlias, context));
-
break;
+#if FAKE_XRM_EASY_9
+ case JoinOperator.Exists:
+ // This is most likely not the most performant implementation, but it is probably the closest match
+ // to the generated sql query, which will be a correlated subquery.
+ query = query.Where(outerEl =>
+ inner.Any(innerEl =>
+ outerEl.KeySelector(linkFromAlias, context).Equals(innerEl.KeySelector(le.LinkToAttributeName, context))));
+ break;
+#endif
default: //This shouldn't be reached as there are only 3 types of Join...
throw UnsupportedExceptionFactory.New(context.LicenseContext.Value, string.Format("The join operator {0} is currently not supported. ", le.JoinOperator));
diff --git a/tests/FakeXrmEasy.Core.Tests/Middleware/Crud/FakeMessageExecutors/RetrieveMultipleRequestTests/QueryLinkEntityTests.cs b/tests/FakeXrmEasy.Core.Tests/Middleware/Crud/FakeMessageExecutors/RetrieveMultipleRequestTests/QueryLinkEntityTests.cs
index a3b2450c..ba1fc7f8 100644
--- a/tests/FakeXrmEasy.Core.Tests/Middleware/Crud/FakeMessageExecutors/RetrieveMultipleRequestTests/QueryLinkEntityTests.cs
+++ b/tests/FakeXrmEasy.Core.Tests/Middleware/Crud/FakeMessageExecutors/RetrieveMultipleRequestTests/QueryLinkEntityTests.cs
@@ -1127,5 +1127,101 @@ public void TestRetriveMultipleWithLinkEntityWithAlternateNullField()
// this fails (entity2Value is "value")
Assert.Null(entity2Value);
}
+
+ [Fact]
+ public void Should_Find_Record_With_Associations_When_Exists_Join_Is_Used()
+ {
+ // Arrange
+ Entity account = new Entity("account", Guid.NewGuid());
+
+ Entity contact = new Entity("contact", Guid.NewGuid())
+ {
+ ["parentcustomerid"] = account.ToEntityReference(),
+ };
+ List entities = new List()
+ {
+ account, contact
+ };
+
+ _context.Initialize(entities);
+
+ QueryExpression queryExpression = new QueryExpression("account");
+ queryExpression.ColumnSet = new ColumnSet(true);
+
+ LinkEntity contactQuery = queryExpression.AddLink("contact", "accountid", "parentcustomerid");
+ contactQuery.JoinOperator = JoinOperator.Exists;
+ contactQuery.EntityAlias = "c";
+
+ // Act
+ EntityCollection results = _service.RetrieveMultiple(queryExpression);
+
+ // Assert
+ Assert.Single(results.Entities);
+ }
+
+
+ [Fact]
+ public void Should_Not_Find_Record_Without_Associations_When_Exists_Join_Is_Used()
+ {
+ // Arrange
+ Entity account = new Entity("account", Guid.NewGuid());
+
+ Entity contact = new Entity("contact", Guid.NewGuid());
+ List entities = new List()
+ {
+ account, contact
+ };
+
+ _context.Initialize(entities);
+
+ QueryExpression queryExpression = new QueryExpression("account");
+ queryExpression.ColumnSet = new ColumnSet(true);
+
+ LinkEntity contactQuery = queryExpression.AddLink("contact", "accountid", "parentcustomerid");
+ contactQuery.JoinOperator = JoinOperator.Exists;
+ contactQuery.EntityAlias = "c";
+
+ // Act
+ EntityCollection result = _service.RetrieveMultiple(queryExpression);
+
+ // Assert
+ Assert.Empty(result.Entities);
+ }
+
+ [Fact]
+ public void Should_Return_Only_Outer_Record_When_Exists_Join_Is_Used()
+ {
+ // Arrange
+ Entity account = new Entity("account", Guid.NewGuid());
+
+ Entity contact = new Entity("contact", Guid.NewGuid())
+ {
+ ["parentcustomerid"] = account.ToEntityReference(),
+ ["firstname"] = "firstname"
+ };
+ List entities = new List()
+ {
+ account, contact
+ };
+
+ _context.Initialize(entities);
+
+ QueryExpression queryExpression = new QueryExpression("account");
+ queryExpression.ColumnSet = new ColumnSet(true);
+
+ LinkEntity contactQuery = queryExpression.AddLink("contact", "accountid", "parentcustomerid");
+ contactQuery.JoinOperator = JoinOperator.Exists;
+ contactQuery.EntityAlias = "c";
+ contactQuery.Columns = new ColumnSet(true);
+
+ // Act
+ EntityCollection result = _service.RetrieveMultiple(queryExpression);
+
+ // Assert
+ Assert.Single(result.Entities);
+
+ Entity outerRecord = result.Entities.First();
+ Assert.Null(outerRecord.GetAttributeValue("c.firstname"));
+ }
}
}
\ No newline at end of file