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>

Reply via email to