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 ba4f9572bb IGNITE-21009 .NET: Improve nullable column mapping (#3006)
ba4f9572bb is described below

commit ba4f9572bb2e2509cdf6dd1758dda5bd05b2b88c
Author: Pavel Tupitsyn <ptupit...@apache.org>
AuthorDate: Thu Jan 4 10:14:56 2024 +0200

    IGNITE-21009 .NET: Improve nullable column mapping (#3006)
    
    * Allow nullable simple types in `KeyValueView`, for example, 
`KeyValueView<int, long?>`
      * Existing `Apache.Ignite.Option<T>` represents all 3 states:
        * Row present, column not null (`HasValue = true`, `Value != null`)
        * Row present, column null (`HasValue = true`, `Value = null`)
        * Row not present (`HasValue = false`)
    * Allow nullable enums
    * Throw an exception when trying to map a nullable column to a non-nullable 
type
---
 .../cpp/tests/odbc-test/meta_queries_test.cpp      |   6 +
 .../dotnet/Apache.Ignite.Tests/IgniteTestsBase.cs  |   7 +-
 .../Apache.Ignite.Tests/Linq/LinqTests.Cast.cs     |  10 +-
 .../Apache.Ignite.Tests/Linq/LinqTests.GroupBy.cs  |   2 +-
 .../Apache.Ignite.Tests/Linq/LinqTests.Join.cs     |   4 +-
 .../dotnet/Apache.Ignite.Tests/Linq/LinqTests.cs   |  28 ++--
 .../Apache.Ignite.Tests/ProjectFilesTests.cs       |  50 ++++---
 .../Table/KeyValueViewPocoPrimitiveTests.cs        |   5 +-
 .../Table/KeyValueViewPrimitiveTests.cs            | 147 +++++++++++++++++----
 .../dotnet/Apache.Ignite.Tests/Table/Poco2.cs      |  18 +--
 .../Apache.Ignite.Tests/Table/PocoAllColumns.cs    |  11 +-
 .../dotnet/Apache.Ignite.Tests/Table/PocoEnums.cs  |   8 ++
 .../Table/RecordViewPocoTests.cs                   |  72 +++++++---
 .../Internal/Common/IgniteArgumentCheck.cs         |   6 +
 .../Apache.Ignite/Internal/Table/KeyValueView.cs   |   3 -
 .../Table/Serialization/ILGeneratorExtensions.cs   |   6 +
 .../Table/Serialization/ObjectSerializerHandler.cs |  51 ++++++-
 .../Table/Serialization/ReflectionUtils.cs         |  12 +-
 .../Serialization/TuplePairSerializerHandler.cs    |  12 +-
 .../dotnet/Apache.Ignite/Internal/Table/Table.cs   |   3 +-
 .../dotnet/Apache.Ignite/Table/IKeyValueView.cs    |   1 -
 .../platforms/dotnet/Apache.Ignite/Table/ITable.cs |   3 +-
 .../runner/app/PlatformTestNodeRunner.java         |  33 +++++
 23 files changed, 375 insertions(+), 123 deletions(-)

diff --git a/modules/platforms/cpp/tests/odbc-test/meta_queries_test.cpp 
b/modules/platforms/cpp/tests/odbc-test/meta_queries_test.cpp
index e15c00208a..2af8886f11 100644
--- a/modules/platforms/cpp/tests/odbc-test/meta_queries_test.cpp
+++ b/modules/platforms/cpp/tests/odbc-test/meta_queries_test.cpp
@@ -737,6 +737,12 @@ TEST_F(meta_queries_test, tables_meta) {
 
     check_meta<COLUMNS_NUM, ODBC_BUFFER_SIZE>(columns, columns_len, 
"TBL_ALL_COLUMNS");
 
+    ret = SQLFetch(m_statement);
+    if (!SQL_SUCCEEDED(ret))
+        FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement));
+
+    check_meta<COLUMNS_NUM, ODBC_BUFFER_SIZE>(columns, columns_len, 
"TBL_ALL_COLUMNS_NOT_NULL");
+
     ret = SQLFetch(m_statement);
     if (!SQL_SUCCEEDED(ret))
         FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement));
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/IgniteTestsBase.cs 
b/modules/platforms/dotnet/Apache.Ignite.Tests/IgniteTestsBase.cs
index da222099d9..fe43913ff2 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/IgniteTestsBase.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/IgniteTestsBase.cs
@@ -37,6 +37,7 @@ namespace Apache.Ignite.Tests
         protected const string TableName = "TBL1";
 
         protected const string TableAllColumnsName = "TBL_ALL_COLUMNS";
+        protected const string TableAllColumnsNotNullName = 
"TBL_ALL_COLUMNS_NOT_NULL";
         protected const string TableAllColumnsSqlName = "TBL_ALL_COLUMNS_SQL";
 
         protected const string TableInt8Name = "TBL_INT8";
@@ -105,8 +106,10 @@ namespace Apache.Ignite.Tests
             PocoView = Table.GetRecordView<Poco>();
 
             var tableAllColumns = await 
Client.Tables.GetTableAsync(TableAllColumnsName);
-            PocoAllColumnsView = 
tableAllColumns!.GetRecordView<PocoAllColumns>();
-            PocoAllColumnsNullableView = 
tableAllColumns.GetRecordView<PocoAllColumnsNullable>();
+            PocoAllColumnsNullableView = 
tableAllColumns!.GetRecordView<PocoAllColumnsNullable>();
+
+            var tableAllColumnsNotNull = await 
Client.Tables.GetTableAsync(TableAllColumnsNotNullName);
+            PocoAllColumnsView = 
tableAllColumnsNotNull!.GetRecordView<PocoAllColumns>();
 
             var tableAllColumnsSql = await 
Client.Tables.GetTableAsync(TableAllColumnsSqlName);
             PocoAllColumnsSqlView = 
tableAllColumnsSql!.GetRecordView<PocoAllColumnsSql>();
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.Cast.cs 
b/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.Cast.cs
index ac5cb6601f..e45fbde816 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.Cast.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.Cast.cs
@@ -32,11 +32,11 @@ public partial class LinqTests
         var query = PocoIntView.AsQueryable()
             .Select(x => new
             {
-                Byte = (sbyte)(x.Val / 10),
-                Short = (short)x.Val,
-                Long = (long)x.Val,
-                Float = (float)x.Val / 1000,
-                Double = (double)x.Val / 2000,
+                Byte = (sbyte?)(x.Val / 10),
+                Short = (short?)x.Val,
+                Long = (long?)x.Val,
+                Float = (float?)x.Val / 1000,
+                Double = (double?)x.Val / 2000,
             })
             .OrderByDescending(x => x.Long);
 
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.GroupBy.cs 
b/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.GroupBy.cs
index c0837cbb8b..2ac2f0ba2f 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.GroupBy.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.GroupBy.cs
@@ -35,7 +35,7 @@ public partial class LinqTests
             .Select(x => x.Key)
             .OrderBy(x => x);
 
-        List<sbyte> res = query.ToList();
+        List<sbyte?> res = query.ToList();
 
         Assert.AreEqual(new[] { 0, 1, 2, 3 }, res);
 
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.Join.cs 
b/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.Join.cs
index c075718008..ccbb66122e 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.Join.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.Join.cs
@@ -232,7 +232,7 @@ public partial class LinqTests
                 resultSelector: (a, b) => new
                 {
                     Id = a.Key,
-                    Price = b.Val
+                    Price = b.Val!.Value
                 })
             .OrderBy(x => x.Id);
 
@@ -250,7 +250,7 @@ public partial class LinqTests
         Assert.AreEqual(0, res[3].Price);
 
         StringAssert.Contains(
-            "select _T0.KEY, _T1.VAL " +
+            "select _T0.KEY, cast(_T1.VAL as smallint) as PRICE " +
             "from PUBLIC.TBL_INT32 as _T0 " +
             "left outer join (select * from PUBLIC.TBL_INT16 as _T2 ) as _T1 " 
+
             "on (cast(_T1.KEY as int) = _T0.KEY)",
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.cs 
b/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.cs
index 2d91b21acc..4b66655358 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.cs
@@ -419,7 +419,7 @@ public partial class LinqTests : IgniteTestsBase
             .Select(x => x.Val)
             .Distinct();
 
-        List<sbyte> res = query.ToList();
+        List<sbyte?> res = query.ToList();
 
         CollectionAssert.AreEquivalent(new[] { 0, 1, 2, 3 }, res);
 
@@ -557,7 +557,7 @@ public partial class LinqTests : IgniteTestsBase
     {
         var query = PocoDecimalView.AsQueryable()
             .OrderByDescending(x => x.Val)
-            .Select(x => new PocoDecimal(x.Val, x.Key));
+            .Select(x => new PocoDecimal(x.Key, x.Val));
 
         var res = query.ToList();
         Assert.AreEqual(9.0m, res[0].Val);
@@ -723,27 +723,27 @@ public partial class LinqTests : IgniteTestsBase
         B = 300
     }
 
-    private record PocoByte(sbyte Key, sbyte Val);
+    private record PocoByte(sbyte Key, sbyte? Val);
 
-    private record PocoShort(short Key, short Val);
+    private record PocoShort(short Key, short? Val);
 
-    private record PocoInt(int Key, int Val);
+    private record PocoInt(int Key, int? Val);
 
-    private record PocoLong(long Key, long Val);
+    private record PocoLong(long Key, long? Val);
 
-    private record PocoFloat(float Key, float Val);
+    private record PocoFloat(float Key, float? Val);
 
-    private record PocoDouble(double Key, double Val);
+    private record PocoDouble(double Key, double? Val);
 
-    private record PocoDecimal(decimal Key, decimal Val);
+    private record PocoDecimal(decimal Key, decimal? Val);
 
-    private record PocoString(string Key, string Val);
+    private record PocoString(string Key, string? Val);
 
-    private record PocoDate(LocalDate Key, LocalDate Val);
+    private record PocoDate(LocalDate Key, LocalDate? Val);
 
-    private record PocoTime(LocalTime Key, LocalTime Val);
+    private record PocoTime(LocalTime Key, LocalTime? Val);
 
-    private record PocoDateTime(LocalDateTime Key, LocalDateTime Val);
+    private record PocoDateTime(LocalDateTime Key, LocalDateTime? Val);
 
-    private record PocoIntEnum(int Key, TestEnum Val);
+    private record PocoIntEnum(int Key, TestEnum? Val);
 }
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/ProjectFilesTests.cs 
b/modules/platforms/dotnet/Apache.Ignite.Tests/ProjectFilesTests.cs
index b4e0f521d2..e889cc007a 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/ProjectFilesTests.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/ProjectFilesTests.cs
@@ -19,6 +19,7 @@ namespace Apache.Ignite.Tests
 {
     using System;
     using System.Collections.Generic;
+    using System.Diagnostics.CodeAnalysis;
     using System.IO;
     using NUnit.Framework;
 
@@ -83,32 +84,51 @@ namespace Apache.Ignite.Tests
         [Test]
         public void TestTodosHaveTickets()
         {
-            Assert.Multiple(() =>
+            var exceptions = new List<Exception>();
+
+            foreach (var file in GetCsFiles())
             {
-                foreach (var file in GetCsFiles())
+                if (file.EndsWith("ProjectFilesTests.cs", 
StringComparison.Ordinal))
                 {
-                    if (file.EndsWith("ProjectFilesTests.cs", 
StringComparison.Ordinal))
-                    {
-                        continue;
-                    }
+                    continue;
+                }
 
-                    int lineNum = 0;
-                    foreach (var line in File.ReadAllLines(file))
-                    {
-                        lineNum++;
+                int lineNum = 0;
+                foreach (var line in File.ReadAllLines(file))
+                {
+                    lineNum++;
 
-                        if (line.Contains("TODO", StringComparison.Ordinal))
-                        {
-                            StringAssert.Contains("IGNITE-", line, $"TODOs 
should be linked to tickets in {file}:line {lineNum}");
-                        }
+                    if (line.Contains("TODO", StringComparison.Ordinal) && 
!line.Contains("IGNITE-", StringComparison.Ordinal))
+                    {
+                        exceptions.Add(new TodoWithoutTicketException(
+                            "TODO without ticket: " + line.Trim(),
+                            $"at 
Apache.Ignite.Tests.ProjectFilesTests.TestTodosHaveTickets() in {file}:line 
{lineNum}"));
                     }
                 }
-            });
+            }
+
+            if (exceptions.Count > 0)
+            {
+                throw new AggregateException(exceptions);
+            }
         }
 
         private static IEnumerable<string> GetCsFiles()
         {
             return Directory.GetFiles(TestUtils.SolutionDir, "*.cs", 
SearchOption.AllDirectories);
         }
+
+        [SuppressMessage("Design", "CA1064:Exceptions should be public", 
Justification = "Tests.")]
+        [SuppressMessage("Design", "CA1032:Implement standard exception 
constructors", Justification = "Tests.")]
+        private sealed class TodoWithoutTicketException : AssertionException
+        {
+            public TodoWithoutTicketException(string message, string 
stackTrace)
+                : base(message)
+            {
+                StackTrace = stackTrace;
+            }
+
+            public override string StackTrace { get; }
+        }
     }
 }
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/KeyValueViewPocoPrimitiveTests.cs
 
b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/KeyValueViewPocoPrimitiveTests.cs
index b33e0248b1..6f06f7e3d4 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/KeyValueViewPocoPrimitiveTests.cs
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/KeyValueViewPocoPrimitiveTests.cs
@@ -83,13 +83,10 @@ public class KeyValueViewPocoPrimitiveTests : 
IgniteTestsBase
     }
 
     [Test]
-    public void TestPutNullThrowsArgumentException()
+    public void TestPutNullKeyThrowsArgumentException()
     {
         var keyEx = Assert.ThrowsAsync<ArgumentNullException>(async () => 
await KvView.PutAsync(null, null!, null!));
         Assert.AreEqual("Value cannot be null. (Parameter 'key')", 
keyEx!.Message);
-
-        var valEx = Assert.ThrowsAsync<ArgumentNullException>(async () => 
await KvView.PutAsync(null, GetKeyPoco(1L), null!));
-        Assert.AreEqual("Value cannot be null. (Parameter 'val')", 
valEx!.Message);
     }
 
     [Test]
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/KeyValueViewPrimitiveTests.cs
 
b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/KeyValueViewPrimitiveTests.cs
index 23a77dd18d..5a5ada166c 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/KeyValueViewPrimitiveTests.cs
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/KeyValueViewPrimitiveTests.cs
@@ -50,6 +50,70 @@ public class KeyValueViewPrimitiveTests : IgniteTestsBase
         Assert.AreEqual("val", res);
     }
 
+    [Test]
+    public async Task TestGetNullableValueType()
+    {
+        var table = await Client.Tables.GetTableAsync(TableInt64Name);
+        var recView = table!.RecordBinaryView;
+        var view = table.GetKeyValueView<long, long?>();
+
+        // Put as tuple, read as primitive.
+        await recView.UpsertAsync(null, new IgniteTuple { ["KEY"] = 1L, 
["VAL"] = 1L });
+        await recView.UpsertAsync(null, new IgniteTuple { ["KEY"] = 2L, 
["VAL"] = null });
+        await recView.DeleteAsync(null, new IgniteTuple { ["KEY"] = 3L });
+
+        // Row present, value present.
+        var res1 = await view.GetAsync(null, 1);
+        Assert.IsTrue(res1.HasValue);
+        Assert.AreEqual(1, res1.Value);
+
+        // Row present, column value is null.
+        var res2 = await view.GetAsync(null, 2);
+        Assert.IsTrue(res2.HasValue);
+        Assert.AreEqual(null, res2.Value);
+
+        // Row is not present.
+        var res3 = await view.GetAsync(null, 3);
+        Assert.IsFalse(res3.HasValue);
+    }
+
+    [Test]
+    public async Task TestPutNullableValueType()
+    {
+        var table = await Client.Tables.GetTableAsync(TableInt64Name);
+        var recView = table!.RecordBinaryView;
+        var view = table.GetKeyValueView<long, long?>();
+
+        // Put as primitive, read as tuple.
+        await view.PutAsync(null, 4L, 4L);
+        await view.PutAsync(null, 5L, null);
+        await view.RemoveAsync(null, 6L);
+
+        // Row present, value present.
+        var res1 = await recView.GetAsync(null, new IgniteTuple { ["KEY"] = 4L 
});
+        Assert.IsTrue(res1.HasValue);
+        Assert.AreEqual(4L, res1.Value["VAL"]);
+
+        // Row present, column value is null.
+        var res2 = await recView.GetAsync(null, new IgniteTuple { ["KEY"] = 5L 
});
+        Assert.IsTrue(res2.HasValue);
+        Assert.AreEqual(null, res2.Value["VAL"]);
+
+        // Row is not present.
+        var res3 = await recView.GetAsync(null, new IgniteTuple { ["KEY"] = 6L 
});
+        Assert.IsFalse(res3.HasValue);
+    }
+
+    [Test]
+    public async Task TestPutGetNullableTypeMismatch()
+    {
+        var table = await Client.Tables.GetTableAsync(TableInt64Name);
+        var view = table!.GetKeyValueView<long, long>();
+
+        var ex = Assert.ThrowsAsync<IgniteClientException>(async () => await 
view.GetAsync(null, 2));
+        Assert.AreEqual("Can't map 'System.Int64' to column 'VAL' - column is 
nullable, but field is not.", ex!.Message);
+    }
+
     [Test]
     public async Task TestGetNonExistentKeyReturnsEmptyOption()
     {
@@ -76,10 +140,11 @@ public class KeyValueViewPrimitiveTests : IgniteTestsBase
     }
 
     [Test]
-    public void TestPutNullThrowsArgumentException()
+    public void TestPutNullKeyThrowsArgumentException()
     {
-        var valEx = Assert.ThrowsAsync<ArgumentNullException>(async () => 
await KvView.PutAsync(null, 1L, null!));
-        Assert.AreEqual("Value cannot be null. (Parameter 'val')", 
valEx!.Message);
+        var view = Table.GetKeyValueView<object, string>();
+        var keyEx = Assert.ThrowsAsync<ArgumentNullException>(async () => 
await view.PutAsync(null, null!, null!));
+        Assert.AreEqual("Value cannot be null. (Parameter 'key')", 
keyEx!.Message);
     }
 
     [Test]
@@ -100,7 +165,7 @@ public class KeyValueViewPrimitiveTests : IgniteTestsBase
         await KvView.PutAllAsync(null, new Dictionary<long, string>());
         await KvView.PutAllAsync(
             null,
-            Enumerable.Range(-1, 7).Select(x => new KeyValuePair<long, 
string>(x, "v" + x)));
+            Enumerable.Range(-1, 7).Select(x => new KeyValuePair<long, 
string>(x, x == 3 ? null! : "v" + x)));
 
         IDictionary<long, string> res = await KvView.GetAllAsync(null, 
Enumerable.Range(-10, 20).Select(x => (long)x));
 
@@ -109,7 +174,15 @@ public class KeyValueViewPrimitiveTests : IgniteTestsBase
         for (int i = -1; i < 6; i++)
         {
             string val = res[i];
-            Assert.AreEqual("v" + i, val);
+
+            if (i == 3)
+            {
+                Assert.IsNull(val);
+            }
+            else
+            {
+                Assert.AreEqual("v" + i, val);
+            }
         }
     }
 
@@ -190,15 +263,20 @@ public class KeyValueViewPrimitiveTests : IgniteTestsBase
     public async Task TestRemoveAllExact()
     {
         await KvView.PutAsync(null, 1, "1");
+        await KvView.PutAsync(null, 2, "1");
+        await KvView.PutAsync(null, 3, null!);
+        await KvView.PutAsync(null, 4, null!);
 
         IList<long> res1 = await KvView.RemoveAllAsync(
             null,
-            Enumerable.Range(-1, 8).Select(x => new KeyValuePair<long, 
string>(x, x.ToString())));
+            Enumerable.Range(-1, 8).Select(x => new KeyValuePair<long, 
string>(x, x == 3 ? null! : x.ToString())));
 
         bool res2 = await KvView.ContainsAsync(null, 1);
+        bool res3 = await KvView.ContainsAsync(null, 3);
 
-        Assert.AreEqual(new[] { -1, 0, 2, 3, 4, 5, 6 }, res1.OrderBy(x => x));
+        Assert.AreEqual(new[] { -1, 0, 2, 4, 5, 6 }, res1.OrderBy(x => x));
         Assert.IsFalse(res2);
+        Assert.IsFalse(res3);
     }
 
     [Test]
@@ -284,20 +362,31 @@ public class KeyValueViewPrimitiveTests : IgniteTestsBase
     [Test]
     public async Task TestAllTypes()
     {
-        await TestKey((sbyte)1, TableInt8Name);
-        await TestKey((short)1, TableInt16Name);
-        await TestKey(1, TableInt32Name);
-        await TestKey(1L, TableInt64Name);
-        await TestKey(1.1f, TableFloatName);
-        await TestKey(1.1d, TableDoubleName);
-        await TestKey(1.234m, TableDecimalName);
-        await TestKey("foo", TableStringName);
-        await TestKey(new LocalDateTime(2022, 10, 13, 8, 4, 42), 
TableDateTimeName);
-        await TestKey(new LocalTime(3, 4, 5), TableTimeName);
-        await TestKey(Instant.FromUnixTimeMilliseconds(123456789101112), 
TableTimestampName);
-        await TestKey(new BigInteger(123456789101112), TableNumberName);
-        await TestKey(new byte[] { 1, 2, 3 }, TableBytesName);
-        await TestKey(new BitArray(new[] { byte.MaxValue }), TableBitmaskName);
+        await TestKey((sbyte)1, (sbyte?)1, TableInt8Name);
+        await TestKey((short)1, (short?)1, TableInt16Name);
+        await TestKey(1, (int?)1, TableInt32Name);
+        await TestKey(1L, (long?)1L, TableInt64Name);
+        await TestKey(1.1f, (float?)1.1f, TableFloatName);
+        await TestKey(1.1d, (double?)1.1d, TableDoubleName);
+        await TestKey(1.234m, (decimal?)1.234m, TableDecimalName);
+        await TestKey("foo", "foo", TableStringName);
+
+        var localDateTime = new LocalDateTime(2022, 10, 13, 8, 4, 42);
+        await TestKey(localDateTime, (LocalDateTime?)localDateTime, 
TableDateTimeName);
+
+        var localTime = new LocalTime(3, 4, 5);
+        await TestKey(localTime, (LocalTime?)localTime, TableTimeName);
+
+        var instant = Instant.FromUnixTimeMilliseconds(123456789101112);
+        await TestKey(instant, (Instant?)instant, TableTimestampName);
+
+        var bigInteger = new BigInteger(123456789101112);
+        await TestKey(bigInteger, (BigInteger?)bigInteger, TableNumberName);
+
+        await TestKey(new byte[] { 1, 2, 3 }, new byte[] { 1, 2, 3, 4 }, 
TableBytesName);
+
+        var bitArray = new BitArray(new[] { byte.MaxValue });
+        await TestKey(bitArray, bitArray, TableBitmaskName);
     }
 
     [Test]
@@ -306,29 +395,29 @@ public class KeyValueViewPrimitiveTests : IgniteTestsBase
         StringAssert.StartsWith("KeyValueView`2[Int64, String] { Table = Table 
{ Name = TBL1, Id =", KvView.ToString());
     }
 
-    private static async Task TestKey<T>(T val, IKeyValueView<T, T> kvView)
-        where T : notnull
+    private static async Task TestKey<TK, TV>(TK key, TV val, 
IKeyValueView<TK, TV> kvView)
+        where TK : notnull
     {
         // Tests EmitKvWriter.
-        await kvView.PutAsync(null, val, val);
+        await kvView.PutAsync(null, key, val);
 
         // Tests EmitKvValuePartReader.
-        var (getRes, _) = await kvView.GetAsync(null, val);
+        var (getRes, _) = await kvView.GetAsync(null, key);
 
         // Tests EmitKvReader.
-        var getAllRes = await kvView.GetAllAsync(null, new[] { val });
+        var getAllRes = await kvView.GetAllAsync(null, new[] { key });
 
         Assert.AreEqual(val, getRes);
         Assert.AreEqual(val, getAllRes.Single().Value);
     }
 
-    private async Task TestKey<T>(T val, string tableName)
-        where T : notnull
+    private async Task TestKey<TK, TV>(TK key, TV val, string tableName)
+        where TK : notnull
     {
         var table = await Client.Tables.GetTableAsync(tableName);
 
         Assert.IsNotNull(table, tableName);
 
-        await TestKey(val, table!.GetKeyValueView<T, T>());
+        await TestKey(key, val, table!.GetKeyValueView<TK, TV>());
     }
 }
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/Poco2.cs 
b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/Poco2.cs
index 716859fbaa..8003f17f69 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/Poco2.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/Poco2.cs
@@ -21,24 +21,24 @@ namespace Apache.Ignite.Tests.Table
     {
         public int Id { get; set; }
 
-        public sbyte Prop1 { get; set; }
+        public sbyte? Prop1 { get; set; }
 
-        public short Prop2 { get; set; }
+        public short? Prop2 { get; set; }
 
-        public int Prop3 { get; set; }
+        public int? Prop3 { get; set; }
 
-        public long Prop4 { get; set; }
+        public long? Prop4 { get; set; }
 
-        public float Prop5 { get; set; }
+        public float? Prop5 { get; set; }
 
-        public double Prop6 { get; set; }
+        public double? Prop6 { get; set; }
 
-        public long Prop7 { get; set; }
+        public long? Prop7 { get; set; }
 
         public string? Prop8 { get; set; }
 
-        public int Prop9 { get; set; }
+        public int? Prop9 { get; set; }
 
-        public int Prop10 { get; set; }
+        public int? Prop10 { get; set; }
     }
 }
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/PocoAllColumns.cs 
b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/PocoAllColumns.cs
index 92e7f1b1e0..a6b21fbdd6 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/PocoAllColumns.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/PocoAllColumns.cs
@@ -18,9 +18,7 @@
 namespace Apache.Ignite.Tests.Table
 {
     using System;
-    using System.Collections;
     using System.Diagnostics.CodeAnalysis;
-    using NodaTime;
 
     /// <summary>
     /// Test user object.
@@ -38,12 +36,5 @@ namespace Apache.Ignite.Tests.Table
         float Float,
         double Double,
         Guid Uuid,
-        LocalDate Date,
-        BitArray BitMask,
-        LocalTime Time,
-        LocalDateTime DateTime,
-        Instant Timestamp,
-        byte[] Blob,
-        decimal Decimal,
-        bool Boolean);
+        decimal Decimal);
 }
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/PocoEnums.cs 
b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/PocoEnums.cs
index c8b612d9d5..f178034f53 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/PocoEnums.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/PocoEnums.cs
@@ -28,12 +28,20 @@ public static class PocoEnums
 {
     public record PocoIntEnum(long Key, IntEnum Int32);
 
+    public record PocoIntEnumNullable(long Key, IntEnum? Int32);
+
     public record PocoShortEnum(long Key, ShortEnum Int16);
 
+    public record PocoShortEnumNullable(long Key, ShortEnum? Int16);
+
     public record PocoLongEnum(long Key, LongEnum Int64);
 
+    public record PocoLongEnumNullable(long Key, LongEnum? Int64);
+
     public record PocoByteEnum(long Key, ByteEnum Int8);
 
+    public record PocoByteEnumNullable(long Key, ByteEnum? Int8);
+
     public record PocoUnsignedByteEnum(long Key, UnsignedByteEnum Int8);
 
     public enum IntEnum
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewPocoTests.cs 
b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewPocoTests.cs
index 04d9c5439e..3d30edaf86 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewPocoTests.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewPocoTests.cs
@@ -612,7 +612,6 @@ namespace Apache.Ignite.Tests.Table
         {
             var pocoView = PocoAllColumnsView;
 
-            var dt = LocalDateTime.FromDateTime(DateTime.UtcNow);
             var poco = new PocoAllColumns(
                 Key: 123,
                 Str: "str",
@@ -623,21 +622,12 @@ namespace Apache.Ignite.Tests.Table
                 Float: 32.32f,
                 Double: 64.64,
                 Uuid: Guid.NewGuid(),
-                Date: dt.Date,
-                BitMask: new BitArray(new byte[] { 1 }),
-                Time: dt.TimeOfDay,
-                DateTime: dt,
-                Timestamp: Instant.FromDateTimeUtc(DateTime.UtcNow),
-                Blob: new byte[] { 1, 2, 3 },
-                Decimal: 123.456m,
-                Boolean: true);
+                Decimal: 123.456m);
 
             await pocoView.UpsertAsync(null, poco);
 
             var res = (await pocoView.GetAsync(null, poco)).Value;
 
-            Assert.AreEqual(poco.Blob, res.Blob);
-            Assert.AreEqual(poco.Date, res.Date);
             Assert.AreEqual(poco.Decimal, res.Decimal);
             Assert.AreEqual(poco.Double, res.Double);
             Assert.AreEqual(poco.Float, res.Float);
@@ -647,11 +637,6 @@ namespace Apache.Ignite.Tests.Table
             Assert.AreEqual(poco.Int64, res.Int64);
             Assert.AreEqual(poco.Str, res.Str);
             Assert.AreEqual(poco.Uuid, res.Uuid);
-            Assert.AreEqual(poco.BitMask, res.BitMask);
-            Assert.AreEqual(poco.Timestamp, res.Timestamp);
-            Assert.AreEqual(poco.Time, res.Time);
-            Assert.AreEqual(poco.DateTime, res.DateTime);
-            Assert.AreEqual(poco.Boolean, res.Boolean);
         }
 
         [Test]
@@ -732,6 +717,8 @@ namespace Apache.Ignite.Tests.Table
         [Test]
         public async Task TestEnumColumns()
         {
+            var table = await 
Client.Tables.GetTableAsync(TableAllColumnsNotNullName);
+
             // Normal values.
             await Test(new PocoEnums.PocoIntEnum(1, PocoEnums.IntEnum.Foo));
             await Test(new PocoEnums.PocoByteEnum(1, PocoEnums.ByteEnum.Foo));
@@ -753,7 +740,41 @@ namespace Apache.Ignite.Tests.Table
             async Task Test<T>(T val)
                 where T : notnull
             {
-                var table = await 
Client.Tables.GetTableAsync(TableAllColumnsName);
+                var view = table!.GetRecordView<T>();
+
+                await view.UpsertAsync(null, val);
+
+                var res = await view.GetAsync(null, val);
+                Assert.AreEqual(val, res.Value);
+            }
+        }
+
+        [Test]
+        public async Task TestEnumColumnsNullable()
+        {
+            var table = await Client.Tables.GetTableAsync(TableAllColumnsName);
+
+            // Normal values.
+            await Test(new PocoEnums.PocoIntEnumNullable(1, 
PocoEnums.IntEnum.Foo));
+            await Test(new PocoEnums.PocoByteEnumNullable(1, 
PocoEnums.ByteEnum.Foo));
+            await Test(new PocoEnums.PocoShortEnumNullable(1, 
PocoEnums.ShortEnum.Foo));
+            await Test(new PocoEnums.PocoLongEnumNullable(1, 
PocoEnums.LongEnum.Foo));
+
+            // Values that are not represented in the enum (it is just a 
number underneath).
+            await Test(new PocoEnums.PocoIntEnumNullable(1, 
(PocoEnums.IntEnum)100));
+            await Test(new PocoEnums.PocoByteEnumNullable(1, 
(PocoEnums.ByteEnum)101));
+            await Test(new PocoEnums.PocoShortEnumNullable(1, 
(PocoEnums.ShortEnum)102));
+            await Test(new PocoEnums.PocoLongEnumNullable(1, 
(PocoEnums.LongEnum)103));
+
+            // Default values.
+            await Test(new PocoEnums.PocoIntEnumNullable(1, default));
+            await Test(new PocoEnums.PocoByteEnumNullable(1, default));
+            await Test(new PocoEnums.PocoShortEnumNullable(1, default));
+            await Test(new PocoEnums.PocoLongEnumNullable(1, default));
+
+            async Task Test<T>(T val)
+                where T : notnull
+            {
                 var view = table!.GetRecordView<T>();
 
                 await view.UpsertAsync(null, val);
@@ -776,6 +797,19 @@ namespace Apache.Ignite.Tests.Table
                 ex!.Message);
         }
 
+        [Test]
+        public async Task TestColumnNullabilityMismatchThrowsException()
+        {
+            var table = await Client.Tables.GetTableAsync(TableAllColumnsName);
+            var pocoView = table!.GetRecordView<NonNullableLongType>();
+
+            var ex = Assert.ThrowsAsync<IgniteClientException>(async () => 
await pocoView.UpsertAsync(null, new NonNullableLongType(1, 1)));
+            Assert.AreEqual(
+                "Can't map field 'NonNullableLongType.<Int64>k__BackingField' 
of type 'System.Int64' " +
+                "to column 'INT64' - column is nullable, but field is not.",
+                ex!.Message);
+        }
+
         [Test]
         public async Task TestUnsupportedEnumColumnTypeThrowsException()
         {
@@ -809,7 +843,9 @@ namespace Apache.Ignite.Tests.Table
             StringAssert.StartsWith("RecordView`1[Poco] { Table = Table { Name 
= TBL1, Id =", PocoView.ToString());
         }
 
-        // ReSharper disable once NotAccessedPositionalProperty.Local
+        // ReSharper disable NotAccessedPositionalProperty.Local
         private record UnsupportedByteType(byte Int8);
+
+        private record NonNullableLongType(long Key, long Int64);
     }
 }
diff --git 
a/modules/platforms/dotnet/Apache.Ignite/Internal/Common/IgniteArgumentCheck.cs 
b/modules/platforms/dotnet/Apache.Ignite/Internal/Common/IgniteArgumentCheck.cs
index d25c892d3c..374dd7c5f2 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite/Internal/Common/IgniteArgumentCheck.cs
+++ 
b/modules/platforms/dotnet/Apache.Ignite/Internal/Common/IgniteArgumentCheck.cs
@@ -20,6 +20,7 @@ namespace Apache.Ignite.Internal.Common
 {
     using System;
     using System.Diagnostics.CodeAnalysis;
+    using System.Reflection;
     using System.Runtime.CompilerServices;
     using JetBrains.Annotations;
 
@@ -28,6 +29,11 @@ namespace Apache.Ignite.Internal.Common
     /// </summary>
     internal static class IgniteArgumentCheck
     {
+        /// <summary>
+        /// Gets the <see cref="NotNull{T}"/> method info.
+        /// </summary>
+        internal static MethodInfo NotNullMethod { get; } = 
typeof(IgniteArgumentCheck).GetMethod(nameof(NotNull))!;
+
         /// <summary>
         /// Throws an ArgumentNullException if specified arg is null.
         /// <para />
diff --git 
a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/KeyValueView.cs 
b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/KeyValueView.cs
index fcbc4fac77..64973f7604 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/KeyValueView.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/KeyValueView.cs
@@ -36,7 +36,6 @@ using Serialization;
 /// <typeparam name="TV">Value type.</typeparam>
 internal sealed class KeyValueView<TK, TV> : IKeyValueView<TK, TV>
     where TK : notnull
-    where TV : notnull
 {
     /** Record view. */
     private readonly RecordView<KvPair<TK, TV>> _recordView;
@@ -173,7 +172,6 @@ internal sealed class KeyValueView<TK, TV> : 
IKeyValueView<TK, TV>
     private static KvPair<TK, TV> ToKv(KeyValuePair<TK, TV> x)
     {
         IgniteArgumentCheck.NotNull(x.Key);
-        IgniteArgumentCheck.NotNull(x.Value);
 
         return new(x.Key, x.Value);
     }
@@ -188,7 +186,6 @@ internal sealed class KeyValueView<TK, TV> : 
IKeyValueView<TK, TV>
     private static KvPair<TK, TV> ToKv(TK key, TV val)
     {
         IgniteArgumentCheck.NotNull(key);
-        IgniteArgumentCheck.NotNull(val);
 
         return new(key, val);
     }
diff --git 
a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/ILGeneratorExtensions.cs
 
b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/ILGeneratorExtensions.cs
index 0f0b60d5bc..94f0cc0d22 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/ILGeneratorExtensions.cs
+++ 
b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/ILGeneratorExtensions.cs
@@ -80,6 +80,12 @@ internal static class ILGeneratorExtensions
         var fromUnderlying = Nullable.GetUnderlyingType(from);
         var toUnderlying = Nullable.GetUnderlyingType(to);
 
+        if (toUnderlying is { IsEnum: true })
+        {
+            toUnderlying = toUnderlying.GetEnumUnderlyingType();
+            to = typeof(Nullable<>).MakeGenericType(toUnderlying);
+        }
+
         if (fromUnderlying != null && toUnderlying != null)
         {
             var emitNull = il.DefineLabel();
diff --git 
a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/ObjectSerializerHandler.cs
 
b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/ObjectSerializerHandler.cs
index a6af89b283..d42651954b 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/ObjectSerializerHandler.cs
+++ 
b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/ObjectSerializerHandler.cs
@@ -218,10 +218,22 @@ namespace Apache.Ignite.Internal.Table.Serialization
                 else
                 {
                     ValidateFieldType(fieldInfo, col);
+
+                    var field = index < schema.KeyColumnCount ? keyField : 
valField;
+
+                    if (field != fieldInfo)
+                    {
+                        // POCO mapping: emit null check (nulls are allowed 
for single-column simple type mapping like KvPair<int, long?>).
+                        il.Emit(OpCodes.Ldarg_2); // record
+                        il.Emit(OpCodes.Ldfld, field);
+                        il.Emit(OpCodes.Ldstr, field == keyField ? "key" : 
"val");
+
+                        il.Emit(OpCodes.Call, 
IgniteArgumentCheck.NotNullMethod.MakeGenericMethod(field.FieldType));
+                    }
+
                     il.Emit(OpCodes.Ldarg_0); // writer
                     il.Emit(OpCodes.Ldarg_2); // record
 
-                    var field = index < schema.KeyColumnCount ? keyField : 
valField;
                     il.Emit(OpCodes.Ldfld, field);
 
                     if (field != fieldInfo)
@@ -339,11 +351,13 @@ namespace Apache.Ignite.Internal.Table.Serialization
                 {
                     fieldInfo = keyField;
                     local = kvLocal;
+                    ValidateSingleFieldMappingType(keyType, col);
                 }
                 else if (i == schema.KeyColumnCount && valMethod != null)
                 {
                     fieldInfo = valField;
                     local = kvLocal;
+                    ValidateSingleFieldMappingType(valType, col);
                 }
                 else
                 {
@@ -407,29 +421,58 @@ namespace Apache.Ignite.Internal.Table.Serialization
         private static void ValidateFieldType(FieldInfo fieldInfo, Column 
column)
         {
             var columnType = column.Type.ToClrType();
+            var type = fieldInfo.FieldType;
 
-            var fieldType = Nullable.GetUnderlyingType(fieldInfo.FieldType) ?? 
fieldInfo.FieldType;
-            fieldType = fieldType.UnwrapEnum();
+            bool typeIsNullable = !type.IsValueType;
+            if (!typeIsNullable && Nullable.GetUnderlyingType(type) is {} 
nullableType)
+            {
+                typeIsNullable = true;
+                type = nullableType;
+            }
 
-            if (fieldType != columnType)
+            type = type.UnwrapEnum();
+
+            if (type != columnType)
             {
                 var message = $"Can't map field 
'{fieldInfo.DeclaringType?.Name}.{fieldInfo.Name}' of type 
'{fieldInfo.FieldType}' " +
                               $"to column '{column.Name}' of type 
'{columnType}' - types do not match.";
 
                 throw new 
IgniteClientException(ErrorGroups.Client.Configuration, message);
             }
+
+            if (column.IsNullable && !typeIsNullable)
+            {
+                var message = $"Can't map field 
'{fieldInfo.DeclaringType?.Name}.{fieldInfo.Name}' of type 
'{fieldInfo.FieldType}' " +
+                              $"to column '{column.Name}' - column is 
nullable, but field is not.";
+
+                throw new 
IgniteClientException(ErrorGroups.Client.Configuration, message);
+            }
         }
 
         private static void ValidateSingleFieldMappingType(Type type, Column 
column)
         {
             var columnType = column.Type.ToClrType();
 
+            bool typeIsNullable = !type.IsValueType;
+            if (!typeIsNullable && Nullable.GetUnderlyingType(type) is {} 
nullableType)
+            {
+                typeIsNullable = true;
+                type = nullableType;
+            }
+
             if (type != columnType)
             {
                 var message = $"Can't map '{type}' to column '{column.Name}' 
of type '{columnType}' - types do not match.";
 
                 throw new 
IgniteClientException(ErrorGroups.Client.Configuration, message);
             }
+
+            if (column.IsNullable && !typeIsNullable)
+            {
+                var message = $"Can't map '{type}' to column '{column.Name}' - 
column is nullable, but field is not.";
+
+                throw new 
IgniteClientException(ErrorGroups.Client.Configuration, message);
+            }
         }
 
         private static void ValidateMappedCount(int mappedCount, Type type, 
Schema schema, int columnCount)
diff --git 
a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/ReflectionUtils.cs
 
b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/ReflectionUtils.cs
index f31927bee4..c80609e302 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/ReflectionUtils.cs
+++ 
b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/ReflectionUtils.cs
@@ -81,7 +81,17 @@ namespace Apache.Ignite.Internal.Table.Serialization
         /// </summary>
         /// <param name="type">Type to unwrap.</param>
         /// <returns>Underlying type when enum; type itself 
otherwise.</returns>
-        public static Type UnwrapEnum(this Type type) => type.IsEnum ? 
Enum.GetUnderlyingType(type) : type;
+        public static Type UnwrapEnum(this Type type)
+        {
+            if (Nullable.GetUnderlyingType(type) is { IsEnum: true } 
underlyingType)
+            {
+                return 
typeof(Nullable<>).MakeGenericType(Enum.GetUnderlyingType(underlyingType));
+            }
+
+            return type.IsEnum
+                ? Enum.GetUnderlyingType(type)
+                : type;
+        }
 
         /// <summary>
         /// Gets a map of fields by column name. Ignores case, handles <see 
cref="ColumnAttribute"/> and <see cref="NotMappedAttribute"/>.
diff --git 
a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/TuplePairSerializerHandler.cs
 
b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/TuplePairSerializerHandler.cs
index 7a4b745757..7aa0aa24f5 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/TuplePairSerializerHandler.cs
+++ 
b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/TuplePairSerializerHandler.cs
@@ -72,12 +72,22 @@ internal sealed class TuplePairSerializerHandler : 
IRecordSerializerHandler<KvPa
         int columnCount,
         Span<byte> noValueSet)
     {
+        var key = record.Key;
+        var val = record.Val;
+
+        IgniteArgumentCheck.NotNull(key);
+
+        if (columnCount > schema.KeyColumnCount)
+        {
+            IgniteArgumentCheck.NotNull(val);
+        }
+
         int written = 0;
 
         for (var index = 0; index < columnCount; index++)
         {
             var col = schema.Columns[index];
-            var rec = index < schema.KeyColumnCount ? record.Key : record.Val;
+            var rec = index < schema.KeyColumnCount ? key : val;
             var colIdx = rec.GetOrdinal(col.Name);
 
             if (colIdx >= 0)
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Table.cs 
b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Table.cs
index 9e1fc51842..754d57e900 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Table.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Table.cs
@@ -136,8 +136,7 @@ namespace Apache.Ignite.Internal.Table
 
         /// <inheritdoc/>
         public IKeyValueView<TK, TV> GetKeyValueView<TK, TV>()
-            where TK : notnull
-            where TV : notnull =>
+            where TK : notnull =>
             new KeyValueView<TK, TV>(GetRecordViewInternal<KvPair<TK, TV>>());
 
         /// <inheritdoc/>
diff --git a/modules/platforms/dotnet/Apache.Ignite/Table/IKeyValueView.cs 
b/modules/platforms/dotnet/Apache.Ignite/Table/IKeyValueView.cs
index 4a8b94decf..6ca68a6f69 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Table/IKeyValueView.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Table/IKeyValueView.cs
@@ -30,7 +30,6 @@ using Transactions;
 /// <typeparam name="TV">Value type.</typeparam>
 public interface IKeyValueView<TK, TV> : IDataStreamerTarget<KeyValuePair<TK, 
TV>>
     where TK : notnull
-    where TV : notnull
 {
     /// <summary>
     /// Gets a value associated with the given key.
diff --git a/modules/platforms/dotnet/Apache.Ignite/Table/ITable.cs 
b/modules/platforms/dotnet/Apache.Ignite/Table/ITable.cs
index f27127a042..8750d55a92 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Table/ITable.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Table/ITable.cs
@@ -58,7 +58,6 @@ namespace Apache.Ignite.Table
         /// <typeparam name="TV">Value type.</typeparam>
         /// <returns>Key-value view.</returns>
         public IKeyValueView<TK, TV> GetKeyValueView<TK, TV>()
-            where TK : notnull
-            where TV : notnull;
+            where TK : notnull;
     }
 }
diff --git 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/PlatformTestNodeRunner.java
 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/PlatformTestNodeRunner.java
index d8fe7f761e..14627286b1 100644
--- 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/PlatformTestNodeRunner.java
+++ 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/PlatformTestNodeRunner.java
@@ -47,6 +47,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 
 import io.netty.util.ResourceLeakDetector;
 import java.io.IOException;
+import java.math.BigDecimal;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.Arrays;
@@ -63,6 +64,7 @@ import org.apache.ignite.compute.JobExecutionContext;
 import org.apache.ignite.internal.app.IgniteImpl;
 import org.apache.ignite.internal.binarytuple.BinaryTupleReader;
 import org.apache.ignite.internal.catalog.commands.ColumnParams;
+import org.apache.ignite.internal.catalog.commands.DefaultValue;
 import org.apache.ignite.internal.client.proto.ColumnTypeConverter;
 import org.apache.ignite.internal.schema.Column;
 import org.apache.ignite.internal.schema.SchemaDescriptor;
@@ -106,6 +108,8 @@ public class PlatformTestNodeRunner {
 
     private static final String TABLE_NAME_ALL_COLUMNS_SQL = 
"TBL_ALL_COLUMNS_SQL"; // All column types supported by SQL.
 
+    private static final String TABLE_NAME_ALL_COLUMNS_NOT_NULL = 
"TBL_ALL_COLUMNS_NOT_NULL";
+
     private static final String ZONE_NAME = "zone1";
 
     /** Time to keep the node alive. */
@@ -328,6 +332,35 @@ public class PlatformTestNodeRunner {
                 List.of(keyCol)
         );
 
+        createTable(
+                ignite.catalogManager(),
+                DEFAULT_SCHEMA_NAME,
+                ZONE_NAME,
+                TABLE_NAME_ALL_COLUMNS_NOT_NULL,
+                List.of(
+                        
ColumnParams.builder().name(keyCol).type(INT64).build(),
+                        
ColumnParams.builder().name("STR").type(STRING).nullable(false)
+                                
.defaultValue(DefaultValue.constant("")).length(1000).build(),
+                        
ColumnParams.builder().name("INT8").type(INT8).nullable(false)
+                                .defaultValue(DefaultValue.constant((byte) 
0)).build(),
+                        
ColumnParams.builder().name("INT16").type(INT16).nullable(false)
+                                .defaultValue(DefaultValue.constant((short) 
0)).build(),
+                        
ColumnParams.builder().name("INT32").type(INT32).nullable(false)
+                                
.defaultValue(DefaultValue.constant(0)).build(),
+                        
ColumnParams.builder().name("INT64").type(INT64).nullable(false)
+                                .defaultValue(DefaultValue.constant((long) 
0)).build(),
+                        
ColumnParams.builder().name("FLOAT").type(FLOAT).nullable(false)
+                                .defaultValue(DefaultValue.constant((float) 
0)).build(),
+                        
ColumnParams.builder().name("DOUBLE").type(DOUBLE).nullable(false)
+                                .defaultValue(DefaultValue.constant((double) 
0)).build(),
+                        
ColumnParams.builder().name("UUID").type(UUID).nullable(false)
+                                .defaultValue(DefaultValue.constant(new 
java.util.UUID(0, 0))).build(),
+                        
ColumnParams.builder().name("DECIMAL").type(DECIMAL).precision(19).scale(3).nullable(false)
+                                
.defaultValue(DefaultValue.constant(BigDecimal.ZERO)).build()
+                ),
+                List.of(keyCol)
+        );
+
         // TODO IGNITE-18431 remove extra table, use TABLE_NAME_ALL_COLUMNS 
for SQL tests.
         createTable(
                 ignite.catalogManager(),


Reply via email to