This is an automated email from the ASF dual-hosted git repository.
ptupitsyn pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new fcc308e1b59 IGNITE-25940 .NET: Add ContainsAllKeys to table views
(#7596)
fcc308e1b59 is described below
commit fcc308e1b5999f737a3255a1d8b055cd5d6e4ab4
Author: Alexandr Mokretsov <[email protected]>
AuthorDate: Wed Feb 18 22:56:15 2026 +0300
IGNITE-25940 .NET: Add ContainsAllKeys to table views (#7596)
---
.../CurrentClientWithOldServerCompatibilityTest.cs | 5 +--
.../dotnet/Apache.Ignite.Tests/FakeServer.cs | 1 +
.../Apache.Ignite.Tests/PartitionAwarenessTests.cs | 4 ++
.../Table/KeyValueViewBinaryTests.cs | 48 ++++++++++++++++++++
.../Table/KeyValueViewPocoPrimitiveTests.cs | 48 ++++++++++++++++++++
.../Table/KeyValueViewPocoTests.cs | 48 ++++++++++++++++++++
.../Table/KeyValueViewPrimitivePocoTests.cs | 39 +++++++++++++++++
.../Table/KeyValueViewPrimitiveTests.cs | 39 +++++++++++++++++
.../Table/RecordViewBinaryTests.cs | 51 ++++++++++++++++++++++
.../Table/RecordViewPocoTests.cs | 51 ++++++++++++++++++++++
.../Apache.Ignite/ApiCompatibilitySuppressions.xml | 28 ++++++++++++
.../dotnet/Apache.Ignite/ClientOperationType.cs | 5 +++
.../Apache.Ignite/Internal/Proto/ClientOp.cs | 3 ++
.../Internal/Proto/ClientOpExtensions.cs | 1 +
.../Apache.Ignite/Internal/Table/KeyValueView.cs | 4 ++
.../Apache.Ignite/Internal/Table/RecordView.cs | 15 +++++++
.../dotnet/Apache.Ignite/RetryReadPolicy.cs | 1 +
.../dotnet/Apache.Ignite/Table/IKeyValueView.cs | 11 +++++
.../dotnet/Apache.Ignite/Table/IRecordView.cs | 11 +++++
19 files changed, 410 insertions(+), 3 deletions(-)
diff --git
a/modules/platforms/dotnet/Apache.Ignite.Tests/Compatibility/CurrentClientWithOldServerCompatibilityTest.cs
b/modules/platforms/dotnet/Apache.Ignite.Tests/Compatibility/CurrentClientWithOldServerCompatibilityTest.cs
index 818b38b882b..66cd7c2d140 100644
---
a/modules/platforms/dotnet/Apache.Ignite.Tests/Compatibility/CurrentClientWithOldServerCompatibilityTest.cs
+++
b/modules/platforms/dotnet/Apache.Ignite.Tests/Compatibility/CurrentClientWithOldServerCompatibilityTest.cs
@@ -306,9 +306,8 @@ public class CurrentClientWithOldServerCompatibilityTest
Assert.IsFalse(await view.ContainsKeyAsync(null, new IgniteTuple {
["ID"] = -id }));
// Contains all.
- // TODO IGNITE-25940 .NET: Add ContainsAll to table views
- // Assert.IsTrue(await view.ContainsAllAsync(null, new[] { key, key2
}));
- // Assert.IsFalse(await view.ContainsAllAsync(null, new[] { key, new
IgniteTuple { ["ID"] = -id } }));
+ Assert.IsTrue(await view.ContainsAllKeysAsync(null, new[] { key, key2
}));
+ Assert.IsFalse(await view.ContainsAllKeysAsync(null, new[] { key, new
IgniteTuple { ["ID"] = -id } }));
// Get.
Assert.IsTrue((await view.GetAsync(null, key)).HasValue);
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/FakeServer.cs
b/modules/platforms/dotnet/Apache.Ignite.Tests/FakeServer.cs
index 6fb4011e891..5b0037afe95 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/FakeServer.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/FakeServer.cs
@@ -312,6 +312,7 @@ namespace Apache.Ignite.Tests
case ClientOp.TupleDelete:
case ClientOp.TupleDeleteExact:
case ClientOp.TupleContainsKey:
+ case ClientOp.TupleContainsAllKeys:
Send(handler, requestId, new byte[] { 1,
MessagePackCode.True }.AsMemory());
continue;
diff --git
a/modules/platforms/dotnet/Apache.Ignite.Tests/PartitionAwarenessTests.cs
b/modules/platforms/dotnet/Apache.Ignite.Tests/PartitionAwarenessTests.cs
index e58ce786c40..29328599ea1 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/PartitionAwarenessTests.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/PartitionAwarenessTests.cs
@@ -213,6 +213,7 @@ public class PartitionAwarenessTests
await AssertOpOnNode(tx => recordView.UpsertAllAsync(tx, keys),
ClientOp.TupleUpsertAll, expectedNode);
await AssertOpOnNode(tx => recordView.DeleteAllAsync(tx, keys),
ClientOp.TupleDeleteAll, expectedNode);
await AssertOpOnNode(tx => recordView.DeleteAllExactAsync(tx, keys),
ClientOp.TupleDeleteAllExact, expectedNode);
+ await AssertOpOnNode(tx => recordView.ContainsAllKeysAsync(tx, keys),
ClientOp.TupleContainsAllKeys, expectedNode);
}
[Test]
@@ -247,6 +248,7 @@ public class PartitionAwarenessTests
await AssertOpOnNode(tx => recordView.UpsertAllAsync(tx, keys),
ClientOp.TupleUpsertAll, expectedNode);
await AssertOpOnNode(tx => recordView.DeleteAllAsync(tx, keys),
ClientOp.TupleDeleteAll, expectedNode);
await AssertOpOnNode(tx => recordView.DeleteAllExactAsync(tx, keys),
ClientOp.TupleDeleteAllExact, expectedNode);
+ await AssertOpOnNode(tx => recordView.ContainsAllKeysAsync(tx, keys),
ClientOp.TupleContainsAllKeys, expectedNode);
}
[Test]
@@ -284,6 +286,7 @@ public class PartitionAwarenessTests
await AssertOpOnNode(tx => kvView.PutAllAsync(tx, pairs),
ClientOp.TupleUpsertAll, expectedNode);
await AssertOpOnNode(tx => kvView.RemoveAllAsync(tx, keys),
ClientOp.TupleDeleteAll, expectedNode);
await AssertOpOnNode(tx => kvView.RemoveAllAsync(tx, pairs),
ClientOp.TupleDeleteAllExact, expectedNode);
+ await AssertOpOnNode(tx => kvView.ContainsAllKeysAsync(tx, keys),
ClientOp.TupleContainsAllKeys, expectedNode);
await AssertOpOnNode(_ =>
kvView.StreamDataAsync(pairs.ToAsyncEnumerable()), ClientOp.StreamerBatchSend,
expectedNode);
}
@@ -324,6 +327,7 @@ public class PartitionAwarenessTests
await AssertOpOnNode(tx => kvView.PutAllAsync(tx, pairs),
ClientOp.TupleUpsertAll, expectedNode);
await AssertOpOnNode(tx => kvView.RemoveAllAsync(tx, keys),
ClientOp.TupleDeleteAll, expectedNode);
await AssertOpOnNode(tx => kvView.RemoveAllAsync(tx, pairs),
ClientOp.TupleDeleteAllExact, expectedNode);
+ await AssertOpOnNode(tx => kvView.ContainsAllKeysAsync(tx, keys),
ClientOp.TupleContainsAllKeys, expectedNode);
}
[Test]
diff --git
a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/KeyValueViewBinaryTests.cs
b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/KeyValueViewBinaryTests.cs
index ddb3521f39d..111fd04d5ca 100644
---
a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/KeyValueViewBinaryTests.cs
+++
b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/KeyValueViewBinaryTests.cs
@@ -93,6 +93,54 @@ public class KeyValueViewBinaryTests : IgniteTestsBase
Assert.AreEqual("Value cannot be null. (Parameter 'val')",
valEx!.Message);
}
+ [Test]
+ public async Task TestContainsAllKeysWhenKeysAreEmptyReturnsTrue()
+ {
+ var result = await KvView.ContainsAllKeysAsync(null, []);
+
+ Assert.IsTrue(result);
+ }
+
+ [Test]
+ public async Task TestContainsAllKeysWhenAllKeysExistReturnsTrue()
+ {
+ await KvView.PutAsync(null, GetTuple(1L), GetTuple("val1"));
+ await KvView.PutAsync(null, GetTuple(2L), GetTuple("val2"));
+ await KvView.PutAsync(null, GetTuple(3L), GetTuple("val3"));
+
+ var result = await KvView.ContainsAllKeysAsync(null, [GetTuple(1L),
GetTuple(2L), GetTuple(3L)]);
+
+ Assert.IsTrue(result);
+ }
+
+ [Test]
+ public async Task TestContainsAllKeysWithAllNonExistingKeysReturnsFalse()
+ {
+ var result = await KvView.ContainsAllKeysAsync(null, [GetTuple(1),
GetTuple(2)]);
+
+ Assert.IsFalse(result);
+ }
+
+ [Test]
+ public async Task TestContainsAllKeysWithNonExistingKeysReturnsFalse()
+ {
+ await KvView.PutAsync(null, GetTuple(1L), GetTuple("val1"));
+ await KvView.PutAsync(null, GetTuple(2L), GetTuple("val2"));
+
+ var result = await KvView.ContainsAllKeysAsync(null, [GetTuple(1L),
GetTuple(2L), GetTuple(3L)]);
+
+ Assert.IsFalse(result);
+ }
+
+ [Test]
+ public void
TestContainsAllKeysThrowsArgumentExceptionOnNullCollectionElement()
+ {
+ var ex = Assert.ThrowsAsync<ArgumentNullException>(
+ async () => await KvView.ContainsAllKeysAsync(null, [GetTuple(1),
null!]));
+
+ Assert.AreEqual("Value cannot be null. (Parameter 'key')",
ex!.Message);
+ }
+
[Test]
public async Task TestContains()
{
diff --git
a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/KeyValueViewPocoPrimitiveTests.cs
b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/KeyValueViewPocoPrimitiveTests.cs
index bedc1f8f7da..f1631be184d 100644
---
a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/KeyValueViewPocoPrimitiveTests.cs
+++
b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/KeyValueViewPocoPrimitiveTests.cs
@@ -90,6 +90,54 @@ public class KeyValueViewPocoPrimitiveTests : IgniteTestsBase
Assert.AreEqual("Value cannot be null. (Parameter 'key')",
keyEx!.Message);
}
+ [Test]
+ public async Task TestContainsAllKeysWhenKeysAreEmptyReturnsTrue()
+ {
+ var result = await KvView.ContainsAllKeysAsync(null, []);
+
+ Assert.IsTrue(result);
+ }
+
+ [Test]
+ public async Task TestContainsAllKeysWhenAllKeysExistReturnsTrue()
+ {
+ await KvView.PutAsync(null, GetKeyPoco(1L), "val1");
+ await KvView.PutAsync(null, GetKeyPoco(2L), "val2");
+ await KvView.PutAsync(null, GetKeyPoco(3L), "val3");
+
+ var result = await KvView.ContainsAllKeysAsync(null, [GetKeyPoco(1L),
GetKeyPoco(2L), GetKeyPoco(3L)]);
+
+ Assert.IsTrue(result);
+ }
+
+ [Test]
+ public async Task TestContainsAllKeysWithAllNonExistingKeysReturnsFalse()
+ {
+ var result = await KvView.ContainsAllKeysAsync(null, [GetKeyPoco(1),
GetKeyPoco(2)]);
+
+ Assert.IsFalse(result);
+ }
+
+ [Test]
+ public async Task TestContainsAllKeysWithNonExistingKeysReturnsFalse()
+ {
+ await KvView.PutAsync(null, GetKeyPoco(1L), "val1");
+ await KvView.PutAsync(null, GetKeyPoco(2L), "val2");
+
+ var result = await KvView.ContainsAllKeysAsync(null, [GetKeyPoco(1L),
GetKeyPoco(2L), GetKeyPoco(3L)]);
+
+ Assert.IsFalse(result);
+ }
+
+ [Test]
+ public void
TestContainsAllKeysThrowsArgumentExceptionOnNullCollectionElement()
+ {
+ var ex = Assert.ThrowsAsync<ArgumentNullException>(
+ async () => await KvView.ContainsAllKeysAsync(null,
[GetKeyPoco(1), null!]));
+
+ Assert.AreEqual("Value cannot be null. (Parameter 'key')",
ex!.Message);
+ }
+
[Test]
public async Task TestContains()
{
diff --git
a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/KeyValueViewPocoTests.cs
b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/KeyValueViewPocoTests.cs
index d0e90a9c32d..e79dabc0252 100644
---
a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/KeyValueViewPocoTests.cs
+++
b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/KeyValueViewPocoTests.cs
@@ -97,6 +97,54 @@ public class KeyValueViewPocoTests(string mode) :
IgniteTestsBase(useMapper: mod
Assert.AreEqual("Value cannot be null. (Parameter 'val')",
valEx!.Message);
}
+ [Test]
+ public async Task TestContainsAllKeysWhenKeysAreEmptyReturnsTrue()
+ {
+ var result = await KvView.ContainsAllKeysAsync(null, []);
+
+ Assert.IsTrue(result);
+ }
+
+ [Test]
+ public async Task TestContainsAllKeysWhenAllKeysExistReturnsTrue()
+ {
+ await KvView.PutAsync(null, GetKeyPoco(1L), GetValPoco("val1"));
+ await KvView.PutAsync(null, GetKeyPoco(2L), GetValPoco("val2"));
+ await KvView.PutAsync(null, GetKeyPoco(3L), GetValPoco("val3"));
+
+ var result = await KvView.ContainsAllKeysAsync(null, [GetKeyPoco(1L),
GetKeyPoco(2L), GetKeyPoco(3L)]);
+
+ Assert.IsTrue(result);
+ }
+
+ [Test]
+ public async Task TestContainsAllKeysWithAllNonExistingKeysReturnsFalse()
+ {
+ var result = await KvView.ContainsAllKeysAsync(null, [GetKeyPoco(1),
GetKeyPoco(2)]);
+
+ Assert.IsFalse(result);
+ }
+
+ [Test]
+ public async Task TestContainsAllKeysWithNonExistingKeysReturnsFalse()
+ {
+ await KvView.PutAsync(null, GetKeyPoco(1L), GetValPoco("val1"));
+ await KvView.PutAsync(null, GetKeyPoco(2L), GetValPoco("val2"));
+
+ var result = await KvView.ContainsAllKeysAsync(null, [GetKeyPoco(1L),
GetKeyPoco(2L), GetKeyPoco(3L)]);
+
+ Assert.IsFalse(result);
+ }
+
+ [Test]
+ public void
TestContainsAllKeysThrowsArgumentExceptionOnNullCollectionElement()
+ {
+ var ex = Assert.ThrowsAsync<ArgumentNullException>(
+ async () => await KvView.ContainsAllKeysAsync(null,
[GetKeyPoco(1), null!]));
+
+ Assert.AreEqual("Value cannot be null. (Parameter 'key')",
ex!.Message);
+ }
+
[Test]
public async Task TestContains()
{
diff --git
a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/KeyValueViewPrimitivePocoTests.cs
b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/KeyValueViewPrimitivePocoTests.cs
index 73806faaa88..e5dbf629710 100644
---
a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/KeyValueViewPrimitivePocoTests.cs
+++
b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/KeyValueViewPrimitivePocoTests.cs
@@ -81,6 +81,45 @@ public class KeyValueViewPrimitivePocoTests : IgniteTestsBase
Assert.AreEqual("Value cannot be null. (Parameter 'val')",
valEx!.Message);
}
+ [Test]
+ public async Task TestContainsAllKeysWhenKeysAreEmptyReturnsTrue()
+ {
+ var result = await KvView.ContainsAllKeysAsync(null, []);
+
+ Assert.IsTrue(result);
+ }
+
+ [Test]
+ public async Task TestContainsAllKeysWhenAllKeysExistReturnsTrue()
+ {
+ await KvView.PutAsync(null, 1L, GetValPoco("val1"));
+ await KvView.PutAsync(null, 2L, GetValPoco("val2"));
+ await KvView.PutAsync(null, 3L, GetValPoco("val3"));
+
+ var result = await KvView.ContainsAllKeysAsync(null, [1L, 2L, 3L]);
+
+ Assert.IsTrue(result);
+ }
+
+ [Test]
+ public async Task TestContainsAllKeysWithAllNonExistingKeysReturnsFalse()
+ {
+ var result = await KvView.ContainsAllKeysAsync(null, [1L, 2L]);
+
+ Assert.IsFalse(result);
+ }
+
+ [Test]
+ public async Task TestContainsAllKeysWithNonExistingKeysReturnsFalse()
+ {
+ await KvView.PutAsync(null, 1L, GetValPoco("val1"));
+ await KvView.PutAsync(null, 2L, GetValPoco("val2"));
+
+ var result = await KvView.ContainsAllKeysAsync(null, [1L, 2L, 3L]);
+
+ Assert.IsFalse(result);
+ }
+
[Test]
public async Task TestContains()
{
diff --git
a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/KeyValueViewPrimitiveTests.cs
b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/KeyValueViewPrimitiveTests.cs
index 6fe27371502..88f8c0826f2 100644
---
a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/KeyValueViewPrimitiveTests.cs
+++
b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/KeyValueViewPrimitiveTests.cs
@@ -156,6 +156,45 @@ public class KeyValueViewPrimitiveTests : IgniteTestsBase
Assert.AreEqual("Value cannot be null. (Parameter 'key')",
keyEx!.Message);
}
+ [Test]
+ public async Task TestContainsAllKeysWhenKeysAreEmptyReturnsTrue()
+ {
+ var result = await KvView.ContainsAllKeysAsync(null, []);
+
+ Assert.IsTrue(result);
+ }
+
+ [Test]
+ public async Task TestContainsAllKeysWhenAllKeysExistReturnsTrue()
+ {
+ await KvView.PutAsync(null, 1L, "val1");
+ await KvView.PutAsync(null, 2L, "val2");
+ await KvView.PutAsync(null, 3L, "val3");
+
+ var result = await KvView.ContainsAllKeysAsync(null, [1L, 2L, 3L]);
+
+ Assert.IsTrue(result);
+ }
+
+ [Test]
+ public async Task TestContainsAllKeysWithAllNonExistingKeysReturnsFalse()
+ {
+ var result = await KvView.ContainsAllKeysAsync(null, [1L, 2L]);
+
+ Assert.IsFalse(result);
+ }
+
+ [Test]
+ public async Task TestContainsAllKeysWithNonExistingKeysReturnsFalse()
+ {
+ await KvView.PutAsync(null, 1L, "val1");
+ await KvView.PutAsync(null, 2L, "val2");
+
+ var result = await KvView.ContainsAllKeysAsync(null, [1L, 2L, 3L]);
+
+ Assert.IsFalse(result);
+ }
+
[Test]
public async Task TestContains()
{
diff --git
a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewBinaryTests.cs
b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewBinaryTests.cs
index 65f0d1b305d..8b56cb89dc1 100644
---
a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewBinaryTests.cs
+++
b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewBinaryTests.cs
@@ -615,6 +615,57 @@ namespace Apache.Ignite.Tests.Table
await TupleView.UpsertAllAsync(null, tuples);
}
+ [Test]
+ public async Task TestContainsAllKeysWhenKeysAreEmptyReturnsTrue()
+ {
+ var result = await TupleView.ContainsAllKeysAsync(null, []);
+
+ Assert.IsTrue(result);
+ }
+
+ [Test]
+ public async Task TestContainsAllKeysWhenAllKeysExistReturnsTrue()
+ {
+ var records = Enumerable
+ .Range(1, 10)
+ .Select(x => GetTuple(x,
x.ToString(CultureInfo.InvariantCulture)));
+ await TupleView.UpsertAllAsync(null, records);
+
+ var result = await TupleView.ContainsAllKeysAsync(null,
Enumerable.Range(1, 10).Select(x => GetTuple(x)));
+
+ Assert.IsTrue(result);
+ }
+
+ [Test]
+ public async Task
TestContainsAllKeysWithAllNonExistingKeysReturnsFalse()
+ {
+ var result = await TupleView.ContainsAllKeysAsync(null,
[GetTuple(1), GetTuple(2)]);
+
+ Assert.IsFalse(result);
+ }
+
+ [Test]
+ public async Task TestContainsAllKeysWithNonExistingKeysReturnsFalse()
+ {
+ var records = Enumerable
+ .Range(1, 10)
+ .Select(x => GetTuple(x,
x.ToString(CultureInfo.InvariantCulture)));
+ await TupleView.UpsertAllAsync(null, records);
+
+ var result = await TupleView.ContainsAllKeysAsync(null,
Enumerable.Range(5, 10).Select(x => GetTuple(x)));
+
+ Assert.IsFalse(result);
+ }
+
+ [Test]
+ public void
TestContainsAllKeysThrowsArgumentExceptionOnNullCollectionElement()
+ {
+ var ex = Assert.ThrowsAsync<ArgumentException>(
+ async () => await TupleView.ContainsAllKeysAsync(null,
[GetTuple(1), null!]));
+
+ Assert.AreEqual("Record collection can't contain null elements.",
ex!.Message);
+ }
+
[Test]
[Category(TestUtils.CategoryIntensive)]
public void TestUpsertAllBufferOverflow()
diff --git
a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewPocoTests.cs
b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewPocoTests.cs
index 8d181cf02e7..be864c51e3f 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewPocoTests.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewPocoTests.cs
@@ -870,6 +870,57 @@ namespace Apache.Ignite.Tests.Table
Assert.IsFalse(await PocoView.ContainsKeyAsync(null,
GetPoco(-128)));
}
+ [Test]
+ public async Task TestContainsAllKeysWhenKeysAreEmptyReturnsTrue()
+ {
+ var result = await PocoView.ContainsAllKeysAsync(null, []);
+
+ Assert.IsTrue(result);
+ }
+
+ [Test]
+ public async Task TestContainsAllKeysWhenAllKeysExistReturnsTrue()
+ {
+ var records = Enumerable
+ .Range(1, 10)
+ .Select(x => GetPoco(x,
x.ToString(CultureInfo.InvariantCulture)));
+ await PocoView.UpsertAllAsync(null, records);
+
+ var result = await PocoView.ContainsAllKeysAsync(null,
Enumerable.Range(1, 10).Select(x => GetPoco(x)));
+
+ Assert.IsTrue(result);
+ }
+
+ [Test]
+ public async Task
TestContainsAllKeysWithAllNonExistingKeysReturnsFalse()
+ {
+ var result = await PocoView.ContainsAllKeysAsync(null,
[GetPoco(1), GetPoco(2)]);
+
+ Assert.IsFalse(result);
+ }
+
+ [Test]
+ public async Task TestContainsAllKeysWithNonExistingKeysReturnsFalse()
+ {
+ var records = Enumerable
+ .Range(1, 10)
+ .Select(x => GetPoco(x,
x.ToString(CultureInfo.InvariantCulture)));
+ await PocoView.UpsertAllAsync(null, records);
+
+ var result = await PocoView.ContainsAllKeysAsync(null,
Enumerable.Range(5, 10).Select(x => GetPoco(x)));
+
+ Assert.IsFalse(result);
+ }
+
+ [Test]
+ public void
TestContainsAllKeysThrowsArgumentExceptionOnNullCollectionElement()
+ {
+ var ex = Assert.ThrowsAsync<ArgumentException>(
+ async () => await PocoView.ContainsAllKeysAsync(null,
[GetPoco(1), null!]));
+
+ Assert.AreEqual("Record collection can't contain null elements.",
ex!.Message);
+ }
+
[Test]
public void TestToString()
{
diff --git
a/modules/platforms/dotnet/Apache.Ignite/ApiCompatibilitySuppressions.xml
b/modules/platforms/dotnet/Apache.Ignite/ApiCompatibilitySuppressions.xml
index dc1796783ff..05619136abe 100644
--- a/modules/platforms/dotnet/Apache.Ignite/ApiCompatibilitySuppressions.xml
+++ b/modules/platforms/dotnet/Apache.Ignite/ApiCompatibilitySuppressions.xml
@@ -161,4 +161,32 @@
<Right>lib/net8.0/Apache.Ignite.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
+ <Suppression>
+ <DiagnosticId>CP0006</DiagnosticId>
+
<Target>M:Apache.Ignite.Table.IKeyValueView`2.ContainsAllKeysAsync(Apache.Ignite.Transactions.ITransaction,System.Collections.Generic.IEnumerable{``0})</Target>
+ <Left>lib/net8.0/Apache.Ignite.dll</Left>
+ <Right>lib/net8.0/Apache.Ignite.dll</Right>
+ <IsBaselineSuppression>true</IsBaselineSuppression>
+ </Suppression>
+ <Suppression>
+ <DiagnosticId>CP0006</DiagnosticId>
+
<Target>M:Apache.Ignite.Table.IRecordView`1.ContainsAllKeysAsync(Apache.Ignite.Transactions.ITransaction,System.Collections.Generic.IEnumerable{``0})</Target>
+ <Left>lib/net8.0/Apache.Ignite.dll</Left>
+ <Right>lib/net8.0/Apache.Ignite.dll</Right>
+ <IsBaselineSuppression>true</IsBaselineSuppression>
+ </Suppression>
+ <Suppression>
+ <DiagnosticId>CP0006</DiagnosticId>
+
<Target>M:Apache.Ignite.Table.IKeyValueView`2.ContainsAllKeysAsync(Apache.Ignite.Transactions.ITransaction,System.Collections.Generic.IEnumerable{`0})</Target>
+ <Left>lib/net8.0/Apache.Ignite.dll</Left>
+ <Right>lib/net8.0/Apache.Ignite.dll</Right>
+ <IsBaselineSuppression>true</IsBaselineSuppression>
+ </Suppression>
+ <Suppression>
+ <DiagnosticId>CP0006</DiagnosticId>
+
<Target>M:Apache.Ignite.Table.IRecordView`1.ContainsAllKeysAsync(Apache.Ignite.Transactions.ITransaction,System.Collections.Generic.IEnumerable{`0})</Target>
+ <Left>lib/net8.0/Apache.Ignite.dll</Left>
+ <Right>lib/net8.0/Apache.Ignite.dll</Right>
+ <IsBaselineSuppression>true</IsBaselineSuppression>
+ </Suppression>
</Suppressions>
\ No newline at end of file
diff --git a/modules/platforms/dotnet/Apache.Ignite/ClientOperationType.cs
b/modules/platforms/dotnet/Apache.Ignite/ClientOperationType.cs
index ad3917942db..264e2ef02ad 100644
--- a/modules/platforms/dotnet/Apache.Ignite/ClientOperationType.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/ClientOperationType.cs
@@ -166,6 +166,11 @@ namespace Apache.Ignite
/// </summary>
StreamerWithReceiverBatchSend,
+ /// <summary>
+ /// Contains all keys (<see
cref="IKeyValueView{TK,TV}.ContainsAllKeysAsync"/>).
+ /// </summary>
+ TupleContainsAllKeys,
+
/// <summary>
/// SQL batch (<see cref="ISql.ExecuteBatchAsync"/>).
/// </summary>
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/ClientOp.cs
b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/ClientOp.cs
index 0743ba8eb5c..b361488c782 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/ClientOp.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/ClientOp.cs
@@ -145,6 +145,9 @@ namespace Apache.Ignite.Internal.Proto
/** Send streamer batch with receiver. */
StreamerWithReceiverBatchSend = 66,
+ /** Check if all tuples with the given keys collection exist. */
+ TupleContainsAllKeys = 67,
+
/** Cancel an operation. */
OperationCancel = 70,
diff --git
a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/ClientOpExtensions.cs
b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/ClientOpExtensions.cs
index 568f05a2c15..c5bb8b14ad5 100644
---
a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/ClientOpExtensions.cs
+++
b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/ClientOpExtensions.cs
@@ -73,6 +73,7 @@ namespace Apache.Ignite.Internal.Proto
ClientOp.StreamerBatchSend =>
ClientOperationType.StreamerBatchSend,
ClientOp.PrimaryReplicasGet =>
ClientOperationType.PrimaryReplicasGet,
ClientOp.StreamerWithReceiverBatchSend =>
ClientOperationType.StreamerBatchSend,
+ ClientOp.TupleContainsAllKeys =>
ClientOperationType.TupleContainsAllKeys,
ClientOp.ServerOpResponse => null,
ClientOp.OperationCancel => null,
diff --git
a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/KeyValueView.cs
b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/KeyValueView.cs
index 15d860e685a..ba09701f384 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/KeyValueView.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/KeyValueView.cs
@@ -75,6 +75,10 @@ internal sealed class KeyValueView<TK, TV> :
IKeyValueView<TK, TV>
public async Task<bool> ContainsAsync(ITransaction? transaction, TK key) =>
await _recordView.ContainsKeyAsync(transaction,
ToKv(key)).ConfigureAwait(false);
+ /// <inheritdoc/>
+ public async Task<bool> ContainsAllKeysAsync(ITransaction? transaction,
IEnumerable<TK> keys) =>
+ await _recordView.ContainsAllKeysAsync(transaction, keys.Select(static
x => ToKv(x))).ConfigureAwait(false);
+
/// <inheritdoc/>
public async Task PutAsync(ITransaction? transaction, TK key, TV val) =>
await _recordView.UpsertAsync(transaction, ToKv(key,
val)).ConfigureAwait(false);
diff --git
a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/RecordView.cs
b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/RecordView.cs
index 9a27e81445c..79c753f109c 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/RecordView.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/RecordView.cs
@@ -122,6 +122,21 @@ namespace Apache.Ignite.Internal.Table
return ReadSchemaAndBoolean(resBuf);
}
+ /// <inheritdoc/>
+ public async Task<bool> ContainsAllKeysAsync(ITransaction?
transaction, IEnumerable<T> keys)
+ {
+ IgniteArgumentCheck.NotNull(keys);
+
+ using var resBuf = await
DoMultiRecordOutOpAsync(ClientOp.TupleContainsAllKeys, transaction, keys, true)
+ .ConfigureAwait(false);
+ if (resBuf == null)
+ {
+ return true;
+ }
+
+ return ReadSchemaAndBoolean(resBuf);
+ }
+
/// <inheritdoc/>
public async Task<IList<Option<T>>> GetAllAsync(ITransaction?
transaction, IEnumerable<T> keys) =>
await GetAllAsync(
diff --git a/modules/platforms/dotnet/Apache.Ignite/RetryReadPolicy.cs
b/modules/platforms/dotnet/Apache.Ignite/RetryReadPolicy.cs
index bedda74d7cd..897f67df2bf 100644
--- a/modules/platforms/dotnet/Apache.Ignite/RetryReadPolicy.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/RetryReadPolicy.cs
@@ -65,6 +65,7 @@ namespace Apache.Ignite
ClientOperationType.ComputeGetStatus => true,
ClientOperationType.StreamerBatchSend => false,
ClientOperationType.StreamerWithReceiverBatchSend => false,
+ ClientOperationType.TupleContainsAllKeys => true,
ClientOperationType.PrimaryReplicasGet => true,
var unsupported => throw new
NotSupportedException("Unsupported operation type: " + unsupported)
};
diff --git a/modules/platforms/dotnet/Apache.Ignite/Table/IKeyValueView.cs
b/modules/platforms/dotnet/Apache.Ignite/Table/IKeyValueView.cs
index b788b73529f..9f4046068fa 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Table/IKeyValueView.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Table/IKeyValueView.cs
@@ -68,6 +68,17 @@ public interface IKeyValueView<TK, TV> :
IDataStreamerTarget<KeyValuePair<TK, TV
/// </returns>
Task<bool> ContainsAsync(ITransaction? transaction, TK key);
+ /// <summary>
+ /// Determines if the table contains entries for all specified keys.
+ /// </summary>
+ /// <param name="transaction">The transaction or <c>null</c> to auto
commit.</param>
+ /// <param name="keys">Keys.</param>
+ /// <returns>
+ /// A <see cref="Task"/> representing the asynchronous operation.
+ /// The task result is <c>true</c> if values exist for all specified keys,
and <c>false</c> otherwise.
+ /// </returns>
+ Task<bool> ContainsAllKeysAsync(ITransaction? transaction, IEnumerable<TK>
keys);
+
/// <summary>
/// Puts a value with a given key.
/// </summary>
diff --git a/modules/platforms/dotnet/Apache.Ignite/Table/IRecordView.cs
b/modules/platforms/dotnet/Apache.Ignite/Table/IRecordView.cs
index d4fa41b325e..1bf85711907 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Table/IRecordView.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Table/IRecordView.cs
@@ -54,6 +54,17 @@ namespace Apache.Ignite.Table
/// </returns>
Task<bool> ContainsKeyAsync(ITransaction? transaction, T key);
+ /// <summary>
+ /// Determines if the table contains entries for all specified keys.
+ /// </summary>
+ /// <param name="transaction">The transaction or <c>null</c> to auto
commit.</param>
+ /// <param name="keys">Collection of records with key columns
set.</param>
+ /// <returns>
+ /// A <see cref="Task"/> representing the asynchronous operation.
+ /// The task result is <c>true</c> if values exist for all specified
keys, and <c>false</c> otherwise.
+ /// </returns>
+ Task<bool> ContainsAllKeysAsync(ITransaction? transaction,
IEnumerable<T> keys);
+
/// <summary>
/// Gets multiple records by keys.
/// </summary>