You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by pt...@apache.org on 2022/10/12 09:56:39 UTC
[ignite-3] branch main updated: IGNITE-16355 .NET: Support value types in the Table API (#1190)
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 c4c6821c4b IGNITE-16355 .NET: Support value types in the Table API (#1190)
c4c6821c4b is described below
commit c4c6821c4b1b66e0a7af59e8af4ba665e0656097
Author: Pavel Tupitsyn <pt...@apache.org>
AuthorDate: Wed Oct 12 12:56:34 2022 +0300
IGNITE-16355 .NET: Support value types in the Table API (#1190)
* Remove `where T : class` constraint from all APIs.
* Use `Option<T>` as a return type for APIs which can return "no value" results, such as `Get`, `GetAndUpsert`, `GetAndDelete`.
* It is not possible to handle nullable value and reference types in a generic way, so we have to use a common wrapper. `Option<T>` also clearly indicates which APIs always return something and which don't.
* Fix `ObjectSerializerHandler` to support value types.
---
.../Apache.Ignite.Tests/Compute/ComputeTests.cs | 4 +
.../dotnet/Apache.Ignite.Tests/OptionTests.cs | 105 ++++++++++++++
.../dotnet/Apache.Ignite.Tests/Sql/SqlTests.cs | 2 +-
.../dotnet/Apache.Ignite.Tests/Table/PocoStruct.cs | 23 +++
.../Table/RecordViewBinaryTests.cs | 132 ++++++++---------
.../Table/RecordViewDefaultMappingTest.cs | 6 +-
.../Table/RecordViewPocoTests.cs | 158 +++++++++++++--------
.../Serialization/ObjectSerializerHandlerTests.cs | 1 -
.../Transactions/TransactionsTests.cs | 32 ++---
.../dotnet/Apache.Ignite/Apache.Ignite.csproj | 1 +
.../dotnet/Apache.Ignite/Compute/ICompute.cs | 3 +-
.../Internal/Common/IgniteArgumentCheck.cs | 19 +--
.../Apache.Ignite/Internal/Compute/Compute.cs | 4 +-
.../dotnet/Apache.Ignite/Internal/Sql/Sql.cs | 1 -
.../Apache.Ignite/Internal/Table/RecordView.cs | 13 +-
.../Serialization/IRecordSerializerHandler.cs | 3 +-
.../Table/Serialization/ObjectSerializerHandler.cs | 32 +++--
.../Table/Serialization/RecordSerializer.cs | 17 +--
.../dotnet/Apache.Ignite/Internal/Table/Table.cs | 5 +-
modules/platforms/dotnet/Apache.Ignite/Option.cs | 111 +++++++++++++++
.../dotnet/Apache.Ignite/Sql/IResultSet.cs | 1 -
modules/platforms/dotnet/Apache.Ignite/Sql/ISql.cs | 3 +-
.../dotnet/Apache.Ignite/Table/IRecordView.cs | 11 +-
.../platforms/dotnet/Apache.Ignite/Table/ITable.cs | 3 +-
24 files changed, 471 insertions(+), 219 deletions(-)
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Compute/ComputeTests.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/Compute/ComputeTests.cs
index f81e83cc64..ee2a9584e0 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Compute/ComputeTests.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Compute/ComputeTests.cs
@@ -226,9 +226,13 @@ namespace Apache.Ignite.Tests.Compute
var keyPoco = new Poco { Key = key };
var resNodeName2 = await Client.Compute.ExecuteColocatedAsync<string, Poco>(TableName, keyPoco, NodeNameJob);
+ var keyPocoStruct = new PocoStruct(key, null);
+ var resNodeName3 = await Client.Compute.ExecuteColocatedAsync<string, PocoStruct>(TableName, keyPocoStruct, NodeNameJob);
+
var expectedNodeName = PlatformTestNodeRunner + nodeName;
Assert.AreEqual(expectedNodeName, resNodeName);
Assert.AreEqual(expectedNodeName, resNodeName2);
+ Assert.AreEqual(expectedNodeName, resNodeName3);
}
[Test]
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/OptionTests.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/OptionTests.cs
new file mode 100644
index 0000000000..955b8f8414
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/OptionTests.cs
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache.Ignite.Tests;
+
+using System;
+using NUnit.Framework;
+
+/// <summary>
+/// Tests for <see cref="Option{T}"/>.
+/// </summary>
+public sealed class OptionTests
+{
+ [Test]
+ public void TestDefaultValueEqualsNone()
+ {
+ Assert.AreEqual(default(Option<int>), Option.None<int>());
+ Assert.IsTrue(Option.None<int>() == default);
+
+ Assert.AreEqual(default(Option<string>), Option.None<string>());
+ Assert.IsTrue(Option.None<string>() == default);
+ }
+
+ [Test]
+ public void TestNoneValueThrows()
+ {
+ var ex = Assert.Throws<InvalidOperationException>(() =>
+ {
+ _ = Option.None<int>().Value;
+ });
+
+ Assert.AreEqual("Value is not present. Check HasValue property before accessing Value.", ex!.Message);
+ }
+
+ [Test]
+ public void TestEquality()
+ {
+ Assert.AreEqual(Option.Some(123), (Option<int>)123);
+ Assert.AreNotEqual(Option.Some(123), Option.Some(124));
+ }
+
+ [Test]
+ public void TestSomeReferenceTypeDeconstruct()
+ {
+ var (val, hasVal) = Option.Some("abc");
+
+ Assert.IsTrue(hasVal);
+ Assert.AreEqual("abc", val);
+ }
+
+ [Test]
+ public void TestNoneReferenceTypeDeconstruct()
+ {
+ var (val, hasVal) = Option.None<string>();
+
+ Assert.IsFalse(hasVal);
+ Assert.IsNull(val);
+ }
+
+ [Test]
+ public void TestSomeValueTypeDeconstruct()
+ {
+ var (val, hasVal) = Option.Some(123L);
+
+ Assert.IsTrue(hasVal);
+ Assert.AreEqual(123L, val);
+ }
+
+ [Test]
+ public void TestNoneValueTypeDeconstruct()
+ {
+ var (val, hasVal) = Option.None<long>();
+
+ Assert.IsFalse(hasVal);
+ Assert.AreEqual(0L, val);
+ }
+
+ [Test]
+ public void TestNoneToString()
+ {
+ Assert.AreEqual("Option { HasValue = False }", Option.None<int>().ToString());
+ Assert.AreEqual("Option { HasValue = False }", Option.None<string>().ToString());
+ }
+
+ [Test]
+ public void TestSomeToString()
+ {
+ Assert.AreEqual("Option { HasValue = True, Value = 123 }", Option.Some(123).ToString());
+ Assert.AreEqual("Option { HasValue = True, Value = Foo }", Option.Some("Foo").ToString());
+ }
+}
\ No newline at end of file
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Sql/SqlTests.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/Sql/SqlTests.cs
index e06a4b6563..6a4626d8bf 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Sql/SqlTests.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Sql/SqlTests.cs
@@ -168,7 +168,7 @@ namespace Apache.Ignite.Tests.Sql
var table = await Client.Tables.GetTableAsync("TEST");
var res = await table!.RecordBinaryView.GetAsync(null, new IgniteTuple { ["ID"] = 1 });
- Assert.AreEqual("s-1", res!["VAL"]);
+ Assert.AreEqual("s-1", res.Value["VAL"]);
}
[Test]
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/PocoStruct.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/PocoStruct.cs
new file mode 100644
index 0000000000..21790df3f4
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/PocoStruct.cs
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache.Ignite.Tests.Table;
+
+/// <summary>
+/// Test user struct.
+/// </summary>
+public record struct PocoStruct(long Key, string? Val, string? UnmappedStr = null);
\ No newline at end of file
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewBinaryTests.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewBinaryTests.cs
index b9a000ac9f..5264aa42ff 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewBinaryTests.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewBinaryTests.cs
@@ -44,12 +44,12 @@ namespace Apache.Ignite.Tests.Table
await TupleView.UpsertAsync(null, GetTuple(1, "foo"));
var keyTuple = GetTuple(1);
- var resTuple = (await TupleView.GetAsync(null, keyTuple))!;
+ var (value, hasValue) = await TupleView.GetAsync(null, keyTuple);
- Assert.IsNotNull(resTuple);
- Assert.AreEqual(2, resTuple.FieldCount);
- Assert.AreEqual(1L, resTuple["key"]);
- Assert.AreEqual("foo", resTuple["val"]);
+ Assert.IsTrue(hasValue);
+ Assert.AreEqual(2, value.FieldCount);
+ Assert.AreEqual(1L, value["key"]);
+ Assert.AreEqual("foo", value["val"]);
}
[Test]
@@ -58,10 +58,10 @@ namespace Apache.Ignite.Tests.Table
var key = GetTuple(1);
await TupleView.UpsertAsync(null, GetTuple(1, "foo"));
- Assert.AreEqual("foo", (await TupleView.GetAsync(null, key))![1]);
+ Assert.AreEqual("foo", (await TupleView.GetAsync(null, key)).Value[1]);
await TupleView.UpsertAsync(null, GetTuple(1, "bar"));
- Assert.AreEqual("bar", (await TupleView.GetAsync(null, key))![1]);
+ Assert.AreEqual("bar", (await TupleView.GetAsync(null, key)).Value[1]);
}
[Test]
@@ -71,8 +71,8 @@ namespace Apache.Ignite.Tests.Table
var res = await TupleView.GetAsync(null, GetTuple(CustomTestIgniteTuple.Key));
- Assert.IsNotNull(res);
- Assert.AreEqual(CustomTestIgniteTuple.Value, res![1]);
+ Assert.IsTrue(res.HasValue);
+ Assert.AreEqual(CustomTestIgniteTuple.Value, res.Value[1]);
}
[Test]
@@ -88,43 +88,43 @@ namespace Apache.Ignite.Tests.Table
[Test]
public async Task TestGetAndUpsertNonExistentRecordReturnsNull()
{
- IIgniteTuple? res = await TupleView.GetAndUpsertAsync(null, GetTuple(2, "2"));
+ Option<IIgniteTuple> res = await TupleView.GetAndUpsertAsync(null, GetTuple(2, "2"));
- Assert.IsNull(res);
- Assert.AreEqual("2", (await TupleView.GetAsync(null, GetTuple(2)))![1]);
+ Assert.IsFalse(res.HasValue);
+ Assert.AreEqual("2", (await TupleView.GetAsync(null, GetTuple(2))).Value[1]);
}
[Test]
public async Task TestGetAndUpsertExistingRecordOverwritesAndReturns()
{
await TupleView.UpsertAsync(null, GetTuple(2, "2"));
- IIgniteTuple? res = await TupleView.GetAndUpsertAsync(null, GetTuple(2, "22"));
+ Option<IIgniteTuple> res = await TupleView.GetAndUpsertAsync(null, GetTuple(2, "22"));
- Assert.IsNotNull(res);
- Assert.AreEqual(2, res![0]);
- Assert.AreEqual("2", res[1]);
- Assert.AreEqual("22", (await TupleView.GetAsync(null, GetTuple(2)))![1]);
+ Assert.IsTrue(res.HasValue);
+ Assert.AreEqual(2, res.Value[0]);
+ Assert.AreEqual("2", res.Value[1]);
+ Assert.AreEqual("22", (await TupleView.GetAsync(null, GetTuple(2))).Value[1]);
}
[Test]
public async Task TestGetAndDeleteNonExistentRecordReturnsNull()
{
- IIgniteTuple? res = await TupleView.GetAndDeleteAsync(null, GetTuple(2, "2"));
+ Option<IIgniteTuple> res = await TupleView.GetAndDeleteAsync(null, GetTuple(2, "2"));
- Assert.IsNull(res);
- Assert.IsNull(await TupleView.GetAsync(null, GetTuple(2)));
+ Assert.IsFalse(res.HasValue);
+ Assert.IsFalse((await TupleView.GetAsync(null, GetTuple(2))).HasValue);
}
[Test]
public async Task TestGetAndDeleteExistingRecordRemovesAndReturns()
{
await TupleView.UpsertAsync(null, GetTuple(2, "2"));
- IIgniteTuple? res = await TupleView.GetAndDeleteAsync(null, GetTuple(2));
+ var (res, hasRes) = await TupleView.GetAndDeleteAsync(null, GetTuple(2));
- Assert.IsNotNull(res);
- Assert.AreEqual(2, res![0]);
+ Assert.IsTrue(hasRes);
+ Assert.AreEqual(2, res[0]);
Assert.AreEqual("2", res[1]);
- Assert.IsNull(await TupleView.GetAsync(null, GetTuple(2)));
+ Assert.IsFalse((await TupleView.GetAsync(null, GetTuple(2))).HasValue);
}
[Test]
@@ -133,7 +133,7 @@ namespace Apache.Ignite.Tests.Table
var res = await TupleView.InsertAsync(null, GetTuple(1, "1"));
Assert.IsTrue(res);
- Assert.IsTrue(await TupleView.GetAsync(null, GetTuple(1)) != null);
+ Assert.IsTrue((await TupleView.GetAsync(null, GetTuple(1))).HasValue);
}
[Test]
@@ -143,7 +143,7 @@ namespace Apache.Ignite.Tests.Table
var res = await TupleView.InsertAsync(null, GetTuple(1, "2"));
Assert.IsFalse(res);
- Assert.AreEqual("1", (await TupleView.GetAsync(null, GetTuple(1)))![1]);
+ Assert.AreEqual("1", (await TupleView.GetAsync(null, GetTuple(1))).Value[1]);
}
[Test]
@@ -158,7 +158,7 @@ namespace Apache.Ignite.Tests.Table
await TupleView.UpsertAsync(null, GetTuple(1, "1"));
Assert.IsTrue(await TupleView.DeleteAsync(null, GetTuple(1)));
- Assert.IsNull(await TupleView.GetAsync(null, GetTuple(1)));
+ Assert.IsTrue(await TupleView.GetAsync(null, GetTuple(1)) is { HasValue: false });
}
[Test]
@@ -174,7 +174,7 @@ namespace Apache.Ignite.Tests.Table
Assert.IsFalse(await TupleView.DeleteExactAsync(null, GetTuple(1)));
Assert.IsFalse(await TupleView.DeleteExactAsync(null, GetTuple(1, "2")));
- Assert.IsNotNull(await TupleView.GetAsync(null, GetTuple(1)));
+ Assert.IsTrue((await TupleView.GetAsync(null, GetTuple(1))).HasValue);
}
[Test]
@@ -183,7 +183,7 @@ namespace Apache.Ignite.Tests.Table
await TupleView.UpsertAsync(null, GetTuple(1, "1"));
Assert.IsTrue(await TupleView.DeleteExactAsync(null, GetTuple(1, "1")));
- Assert.IsNull(await TupleView.GetAsync(null, GetTuple(1)));
+ Assert.IsFalse((await TupleView.GetAsync(null, GetTuple(1))).HasValue);
}
[Test]
@@ -192,7 +192,7 @@ namespace Apache.Ignite.Tests.Table
bool res = await TupleView.ReplaceAsync(null, GetTuple(1, "1"));
Assert.IsFalse(res);
- Assert.IsNull(await TupleView.GetAsync(null, GetTuple(1)));
+ Assert.IsFalse((await TupleView.GetAsync(null, GetTuple(1))).HasValue);
}
[Test]
@@ -202,27 +202,27 @@ namespace Apache.Ignite.Tests.Table
bool res = await TupleView.ReplaceAsync(null, GetTuple(1, "2"));
Assert.IsTrue(res);
- Assert.AreEqual("2", (await TupleView.GetAsync(null, GetTuple(1)))![1]);
+ Assert.AreEqual("2", (await TupleView.GetAsync(null, GetTuple(1))).Value[1]);
}
[Test]
public async Task TestGetAndReplaceNonExistentRecordReturnsNullDoesNotCreateRecord()
{
- IIgniteTuple? res = await TupleView.GetAndReplaceAsync(null, GetTuple(1, "1"));
+ Option<IIgniteTuple> res = await TupleView.GetAndReplaceAsync(null, GetTuple(1, "1"));
- Assert.IsNull(res);
- Assert.IsNull(await TupleView.GetAsync(null, GetTuple(1)));
+ Assert.IsFalse(res.HasValue);
+ Assert.IsFalse((await TupleView.GetAsync(null, GetTuple(1))).HasValue);
}
[Test]
public async Task TestGetAndReplaceExistingRecordReturnsOldOverwrites()
{
await TupleView.UpsertAsync(null, GetTuple(1, "1"));
- IIgniteTuple? res = await TupleView.GetAndReplaceAsync(null, GetTuple(1, "2"));
+ Option<IIgniteTuple> res = await TupleView.GetAndReplaceAsync(null, GetTuple(1, "2"));
- Assert.IsNotNull(res);
- Assert.AreEqual("1", res![1]);
- Assert.AreEqual("2", (await TupleView.GetAsync(null, GetTuple(1)))![1]);
+ Assert.IsTrue(res.HasValue);
+ Assert.AreEqual("1", res.Value[1]);
+ Assert.AreEqual("2", (await TupleView.GetAsync(null, GetTuple(1))).Value[1]);
}
[Test]
@@ -231,7 +231,7 @@ namespace Apache.Ignite.Tests.Table
bool res = await TupleView.ReplaceAsync(null, GetTuple(1, "1"), GetTuple(1, "2"));
Assert.IsFalse(res);
- Assert.IsNull(await TupleView.GetAsync(null, GetTuple(1)));
+ Assert.IsFalse((await TupleView.GetAsync(null, GetTuple(1))).HasValue);
}
[Test]
@@ -241,7 +241,7 @@ namespace Apache.Ignite.Tests.Table
bool res = await TupleView.ReplaceAsync(null, GetTuple(1, "11"), GetTuple(1, "22"));
Assert.IsFalse(res);
- Assert.AreEqual("1", (await TupleView.GetAsync(null, GetTuple(1)))![1]);
+ Assert.AreEqual("1", (await TupleView.GetAsync(null, GetTuple(1))).Value[1]);
}
[Test]
@@ -251,7 +251,7 @@ namespace Apache.Ignite.Tests.Table
bool res = await TupleView.ReplaceAsync(null, GetTuple(1, "1"), GetTuple(1, "22"));
Assert.IsTrue(res);
- Assert.AreEqual("22", (await TupleView.GetAsync(null, GetTuple(1)))![1]);
+ Assert.AreEqual("22", (await TupleView.GetAsync(null, GetTuple(1))).Value[1]);
}
[Test]
@@ -265,7 +265,7 @@ namespace Apache.Ignite.Tests.Table
foreach (var id in ids)
{
var res = await TupleView.GetAsync(null, GetTuple(id));
- Assert.AreEqual(id.ToString(CultureInfo.InvariantCulture), res![1]);
+ Assert.AreEqual(id.ToString(CultureInfo.InvariantCulture), res.Value[1]);
}
}
@@ -283,7 +283,7 @@ namespace Apache.Ignite.Tests.Table
foreach (var id in ids)
{
var res = await TupleView.GetAsync(null, GetTuple(id));
- Assert.AreEqual(id.ToString(CultureInfo.InvariantCulture), res![1]);
+ Assert.AreEqual(id.ToString(CultureInfo.InvariantCulture), res.Value[1]);
}
}
@@ -300,7 +300,7 @@ namespace Apache.Ignite.Tests.Table
foreach (var id in ids)
{
var res = await TupleView.GetAsync(null, GetTuple(id));
- Assert.AreEqual(id.ToString(CultureInfo.InvariantCulture), res![1]);
+ Assert.AreEqual(id.ToString(CultureInfo.InvariantCulture), res.Value[1]);
}
}
@@ -327,13 +327,13 @@ namespace Apache.Ignite.Tests.Table
{
var res = await TupleView.GetAsync(null, GetTuple(id));
- if (existing.TryGetValue(res![0]!, out var old))
+ if (existing.TryGetValue(res.Value[0]!, out var old))
{
- Assert.AreEqual(old[1], res[1]);
+ Assert.AreEqual(old[1], res.Value[1]);
}
else
{
- Assert.AreEqual(id.ToString(CultureInfo.InvariantCulture), res[1]);
+ Assert.AreEqual(id.ToString(CultureInfo.InvariantCulture), res.Value[1]);
}
}
}
@@ -362,15 +362,15 @@ namespace Apache.Ignite.Tests.Table
// TODO: Key order should be preserved by the server (IGNITE-16004).
var res = await TupleView.GetAllAsync(null, Enumerable.Range(9, 4).Select(x => GetTuple(x)));
- var resArr = res.OrderBy(x => x?[0]).ToArray();
+ var resArr = res.OrderBy(x => x.Value[0]).ToArray();
Assert.AreEqual(2, res.Count);
- Assert.AreEqual(9, resArr[0]![0]);
- Assert.AreEqual("9", resArr[0]![1]);
+ Assert.AreEqual(9, resArr[0].Value[0]);
+ Assert.AreEqual("9", resArr[0].Value[1]);
- Assert.AreEqual(10, resArr[1]![0]);
- Assert.AreEqual("10", resArr[1]![1]);
+ Assert.AreEqual(10, resArr[1].Value[0]);
+ Assert.AreEqual("10", resArr[1].Value[1]);
}
[Test]
@@ -412,8 +412,8 @@ namespace Apache.Ignite.Tests.Table
var skipped = await TupleView.DeleteAllAsync(null, new[] { GetTuple(1), GetTuple(2) });
Assert.AreEqual(0, skipped.Count);
- Assert.IsNull(await TupleView.GetAsync(null, GetTuple(1)));
- Assert.IsNull(await TupleView.GetAsync(null, GetTuple(2)));
+ Assert.IsFalse((await TupleView.GetAsync(null, GetTuple(1))).HasValue);
+ Assert.IsFalse((await TupleView.GetAsync(null, GetTuple(2))).HasValue);
}
[Test]
@@ -424,9 +424,9 @@ namespace Apache.Ignite.Tests.Table
Assert.AreEqual(1, skipped.Count);
Assert.AreEqual(4, skipped[0][0]);
- Assert.IsNull(await TupleView.GetAsync(null, GetTuple(1)));
- Assert.IsNull(await TupleView.GetAsync(null, GetTuple(2)));
- Assert.IsNotNull(await TupleView.GetAsync(null, GetTuple(3)));
+ Assert.IsFalse((await TupleView.GetAsync(null, GetTuple(1))).HasValue);
+ Assert.IsFalse((await TupleView.GetAsync(null, GetTuple(2))).HasValue);
+ Assert.IsTrue((await TupleView.GetAsync(null, GetTuple(3))).HasValue);
}
[Test]
@@ -454,8 +454,8 @@ namespace Apache.Ignite.Tests.Table
var skipped = await TupleView.DeleteAllExactAsync(null, new[] { GetTuple(1, "1"), GetTuple(2, "2") });
Assert.AreEqual(0, skipped.Count);
- Assert.IsNull(await TupleView.GetAsync(null, GetTuple(1)));
- Assert.IsNull(await TupleView.GetAsync(null, GetTuple(2)));
+ Assert.IsFalse((await TupleView.GetAsync(null, GetTuple(1))).HasValue);
+ Assert.IsFalse((await TupleView.GetAsync(null, GetTuple(2))).HasValue);
}
[Test]
@@ -465,9 +465,9 @@ namespace Apache.Ignite.Tests.Table
var skipped = await TupleView.DeleteAllExactAsync(null, new[] { GetTuple(1, "1"), GetTuple(2, "22") });
Assert.AreEqual(1, skipped.Count);
- Assert.IsNull(await TupleView.GetAsync(null, GetTuple(1)));
- Assert.IsNotNull(await TupleView.GetAsync(null, GetTuple(2)));
- Assert.IsNotNull(await TupleView.GetAsync(null, GetTuple(3)));
+ Assert.IsFalse((await TupleView.GetAsync(null, GetTuple(1))).HasValue);
+ Assert.IsTrue((await TupleView.GetAsync(null, GetTuple(2))).HasValue);
+ Assert.IsTrue((await TupleView.GetAsync(null, GetTuple(3))).HasValue);
}
[Test]
@@ -529,9 +529,9 @@ namespace Apache.Ignite.Tests.Table
await TupleView.UpsertAsync(null, tuple);
var keyTuple = new IgniteTuple { [KeyCol] = key };
- var resTuple = (await TupleView.GetAsync(null, keyTuple))!;
+ var (resTuple, resTupleHasValue) = await TupleView.GetAsync(null, keyTuple);
- Assert.IsNotNull(resTuple);
+ Assert.IsTrue(resTupleHasValue);
Assert.AreEqual(2, resTuple.FieldCount);
Assert.AreEqual(key, resTuple["key"]);
Assert.AreEqual(val, resTuple["val"]);
@@ -566,9 +566,9 @@ namespace Apache.Ignite.Tests.Table
await tupleView.UpsertAsync(null, tuple);
- var res = await tupleView.GetAsync(null, tuple);
+ var res = (await tupleView.GetAsync(null, tuple)).Value;
- Assert.AreEqual(tuple["Blob"], res!["Blob"]);
+ Assert.AreEqual(tuple["Blob"], res["Blob"]);
Assert.AreEqual(tuple["Date"], res["Date"]);
Assert.AreEqual(tuple["Decimal"], res["Decimal"]);
Assert.AreEqual(tuple["Double"], res["Double"]);
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewDefaultMappingTest.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewDefaultMappingTest.cs
index 5c4922451d..639584283d 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewDefaultMappingTest.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewDefaultMappingTest.cs
@@ -74,11 +74,7 @@ namespace Apache.Ignite.Tests.Table
Assert.AreEqual("2", res.Val);
}
- private T Get<T>(T key)
- where T : class
- {
- return Table.GetRecordView<T>().GetAsync(null, key).GetAwaiter().GetResult()!;
- }
+ private T Get<T>(T key) => Table.GetRecordView<T>().GetAsync(null, key).GetAwaiter().GetResult().Value;
private class FieldsTest
{
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewPocoTests.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewPocoTests.cs
index 6da8f826dc..3db41a988d 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewPocoTests.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewPocoTests.cs
@@ -43,10 +43,9 @@ namespace Apache.Ignite.Tests.Table
await PocoView.UpsertAsync(null, GetPoco(1, "foo"));
var keyTuple = GetPoco(1);
- var resTuple = (await PocoView.GetAsync(null, keyTuple))!;
-
- Assert.IsNotNull(resTuple);
+ var (resTuple, hasValue) = await PocoView.GetAsync(null, keyTuple);
+ Assert.IsTrue(hasValue);
Assert.AreEqual(1L, resTuple.Key);
Assert.AreEqual("foo", resTuple.Val);
@@ -54,16 +53,46 @@ namespace Apache.Ignite.Tests.Table
Assert.AreEqual(default(Guid), resTuple.UnmappedId);
}
+ [Test]
+ public async Task TestUpsertGetValueType()
+ {
+ var pocoView = Table.GetRecordView<PocoStruct>();
+
+ await pocoView.UpsertAsync(null, new PocoStruct(1, "foo"));
+
+ var keyTuple = new PocoStruct(1, null);
+ var (resTuple, hasValue) = await pocoView.GetAsync(null, keyTuple);
+
+ Assert.IsTrue(hasValue);
+ Assert.AreEqual(1L, resTuple.Key);
+ Assert.AreEqual("foo", resTuple.Val);
+ Assert.IsNull(resTuple.UnmappedStr);
+ }
+
+ [Test]
+ public async Task TestGetMissingRowValueType()
+ {
+ var pocoView = Table.GetRecordView<PocoStruct>();
+
+ var keyTuple = new PocoStruct(1, null);
+ var (resTuple, hasValue) = await pocoView.GetAsync(null, keyTuple);
+
+ Assert.IsFalse(hasValue);
+ Assert.AreEqual(0L, resTuple.Key);
+ Assert.IsNull(resTuple.Val);
+ Assert.IsNull(resTuple.UnmappedStr);
+ }
+
[Test]
public async Task TestUpsertOverridesPreviousValue()
{
var key = GetPoco(1);
await PocoView.UpsertAsync(null, GetPoco(1, "foo"));
- Assert.AreEqual("foo", (await PocoView.GetAsync(null, key))!.Val);
+ Assert.AreEqual("foo", (await PocoView.GetAsync(null, key)).Value.Val);
await PocoView.UpsertAsync(null, GetPoco(1, "bar"));
- Assert.AreEqual("bar", (await PocoView.GetAsync(null, key))!.Val);
+ Assert.AreEqual("bar", (await PocoView.GetAsync(null, key)).Value.Val);
}
[Test]
@@ -79,43 +108,43 @@ namespace Apache.Ignite.Tests.Table
[Test]
public async Task TestGetAndUpsertNonExistentRecordReturnsNull()
{
- Poco? res = await PocoView.GetAndUpsertAsync(null, GetPoco(2, "2"));
+ Option<Poco> res = await PocoView.GetAndUpsertAsync(null, GetPoco(2, "2"));
- Assert.IsNull(res);
- Assert.AreEqual("2", (await PocoView.GetAsync(null, GetPoco(2)))!.Val);
+ Assert.IsFalse(res.HasValue);
+ Assert.AreEqual("2", (await PocoView.GetAsync(null, GetPoco(2))).Value.Val);
}
[Test]
public async Task TestGetAndUpsertExistingRecordOverwritesAndReturns()
{
await PocoView.UpsertAsync(null, GetPoco(2, "2"));
- Poco? res = await PocoView.GetAndUpsertAsync(null, GetPoco(2, "22"));
+ var (res, hasRes) = await PocoView.GetAndUpsertAsync(null, GetPoco(2, "22"));
- Assert.IsNotNull(res);
- Assert.AreEqual(2, res!.Key);
+ Assert.IsTrue(hasRes);
+ Assert.AreEqual(2, res.Key);
Assert.AreEqual("2", res.Val);
- Assert.AreEqual("22", (await PocoView.GetAsync(null, GetPoco(2)))!.Val);
+ Assert.AreEqual("22", (await PocoView.GetAsync(null, GetPoco(2))).Value.Val);
}
[Test]
public async Task TestGetAndDeleteNonExistentRecordReturnsNull()
{
- Poco? res = await PocoView.GetAndDeleteAsync(null, GetPoco(2, "2"));
+ Option<Poco> res = await PocoView.GetAndDeleteAsync(null, GetPoco(2, "2"));
- Assert.IsNull(res);
- Assert.IsNull(await PocoView.GetAsync(null, GetPoco(2)));
+ Assert.IsFalse(res.HasValue);
+ Assert.IsFalse((await PocoView.GetAsync(null, GetPoco(2))).HasValue);
}
[Test]
public async Task TestGetAndDeleteExistingRecordRemovesAndReturns()
{
await PocoView.UpsertAsync(null, GetPoco(2, "2"));
- Poco? res = await PocoView.GetAndDeleteAsync(null, GetPoco(2));
+ var (res, hasRes) = await PocoView.GetAndDeleteAsync(null, GetPoco(2));
- Assert.IsNotNull(res);
- Assert.AreEqual(2, res!.Key);
+ Assert.IsTrue(hasRes);
+ Assert.AreEqual(2, res.Key);
Assert.AreEqual("2", res.Val);
- Assert.IsNull(await PocoView.GetAsync(null, GetPoco(2)));
+ Assert.IsFalse((await PocoView.GetAsync(null, GetPoco(2))).HasValue);
}
[Test]
@@ -124,7 +153,7 @@ namespace Apache.Ignite.Tests.Table
var res = await PocoView.InsertAsync(null, GetPoco(1, "1"));
Assert.IsTrue(res);
- Assert.IsTrue(await PocoView.GetAsync(null, GetPoco(1)) != null);
+ Assert.IsTrue((await PocoView.GetAsync(null, GetPoco(1))).HasValue);
}
[Test]
@@ -134,7 +163,7 @@ namespace Apache.Ignite.Tests.Table
var res = await PocoView.InsertAsync(null, GetPoco(1, "2"));
Assert.IsFalse(res);
- Assert.AreEqual("1", (await PocoView.GetAsync(null, GetPoco(1)))!.Val);
+ Assert.AreEqual("1", (await PocoView.GetAsync(null, GetPoco(1))).Value.Val);
}
[Test]
@@ -149,7 +178,7 @@ namespace Apache.Ignite.Tests.Table
await PocoView.UpsertAsync(null, GetPoco(1, "1"));
Assert.IsTrue(await PocoView.DeleteAsync(null, GetPoco(1)));
- Assert.IsNull(await PocoView.GetAsync(null, GetPoco(1)));
+ Assert.IsFalse((await PocoView.GetAsync(null, GetPoco(1))).HasValue);
}
[Test]
@@ -165,7 +194,7 @@ namespace Apache.Ignite.Tests.Table
Assert.IsFalse(await PocoView.DeleteExactAsync(null, GetPoco(1)));
Assert.IsFalse(await PocoView.DeleteExactAsync(null, GetPoco(1, "2")));
- Assert.IsNotNull(await PocoView.GetAsync(null, GetPoco(1)));
+ Assert.IsTrue(await PocoView.GetAsync(null, GetPoco(1)) is { HasValue: true });
}
[Test]
@@ -174,16 +203,17 @@ namespace Apache.Ignite.Tests.Table
await PocoView.UpsertAsync(null, GetPoco(1, "1"));
Assert.IsTrue(await PocoView.DeleteExactAsync(null, GetPoco(1, "1")));
- Assert.IsNull(await PocoView.GetAsync(null, GetPoco(1)));
+ Assert.IsFalse((await PocoView.GetAsync(null, GetPoco(1))).HasValue);
}
[Test]
public async Task TestReplaceNonExistentRecordReturnsFalseDoesNotCreateRecord()
{
bool res = await PocoView.ReplaceAsync(null, GetPoco(1, "1"));
+ Option<Poco> res2 = await PocoView.GetAsync(null, GetPoco(1));
Assert.IsFalse(res);
- Assert.IsNull(await PocoView.GetAsync(null, GetPoco(1)));
+ Assert.IsFalse(res2.HasValue);
}
[Test]
@@ -193,36 +223,38 @@ namespace Apache.Ignite.Tests.Table
bool res = await PocoView.ReplaceAsync(null, GetPoco(1, "2"));
Assert.IsTrue(res);
- Assert.AreEqual("2", (await PocoView.GetAsync(null, GetPoco(1)))!.Val);
+ Assert.AreEqual("2", (await PocoView.GetAsync(null, GetPoco(1))).Value.Val);
}
[Test]
public async Task TestGetAndReplaceNonExistentRecordReturnsNullDoesNotCreateRecord()
{
- Poco? res = await PocoView.GetAndReplaceAsync(null, GetPoco(1, "1"));
+ Option<Poco> res = await PocoView.GetAndReplaceAsync(null, GetPoco(1, "1"));
+ Option<Poco> res2 = await PocoView.GetAsync(null, GetPoco(1));
- Assert.IsNull(res);
- Assert.IsNull(await PocoView.GetAsync(null, GetPoco(1)));
+ Assert.IsFalse(res.HasValue);
+ Assert.IsFalse(res2.HasValue);
}
[Test]
public async Task TestGetAndReplaceExistingRecordReturnsOldOverwrites()
{
await PocoView.UpsertAsync(null, GetPoco(1, "1"));
- Poco? res = await PocoView.GetAndReplaceAsync(null, GetPoco(1, "2"));
+ var (res, hasRes) = await PocoView.GetAndReplaceAsync(null, GetPoco(1, "2"));
- Assert.IsNotNull(res);
- Assert.AreEqual("1", res!.Val);
- Assert.AreEqual("2", (await PocoView.GetAsync(null, GetPoco(1)))!.Val);
+ Assert.IsTrue(hasRes);
+ Assert.AreEqual("1", res.Val);
+ Assert.AreEqual("2", (await PocoView.GetAsync(null, GetPoco(1))).Value.Val);
}
[Test]
public async Task TestReplaceExactNonExistentRecordReturnsFalseDoesNotCreateRecord()
{
bool res = await PocoView.ReplaceAsync(null, GetPoco(1, "1"), GetPoco(1, "2"));
+ Option<Poco> res2 = await PocoView.GetAsync(null, GetPoco(1));
Assert.IsFalse(res);
- Assert.IsNull(await PocoView.GetAsync(null, GetPoco(1)));
+ Assert.IsFalse(res2.HasValue);
}
[Test]
@@ -232,7 +264,7 @@ namespace Apache.Ignite.Tests.Table
bool res = await PocoView.ReplaceAsync(null, GetPoco(1, "11"), GetPoco(1, "22"));
Assert.IsFalse(res);
- Assert.AreEqual("1", (await PocoView.GetAsync(null, GetPoco(1)))!.Val);
+ Assert.AreEqual("1", (await PocoView.GetAsync(null, GetPoco(1))).Value.Val);
}
[Test]
@@ -242,7 +274,7 @@ namespace Apache.Ignite.Tests.Table
bool res = await PocoView.ReplaceAsync(null, GetPoco(1, "1"), GetPoco(1, "22"));
Assert.IsTrue(res);
- Assert.AreEqual("22", (await PocoView.GetAsync(null, GetPoco(1)))!.Val);
+ Assert.AreEqual("22", (await PocoView.GetAsync(null, GetPoco(1))).Value.Val);
}
[Test]
@@ -256,7 +288,7 @@ namespace Apache.Ignite.Tests.Table
foreach (var id in ids)
{
var res = await PocoView.GetAsync(null, GetPoco(id));
- Assert.AreEqual(id.ToString(CultureInfo.InvariantCulture), res!.Val);
+ Assert.AreEqual(id.ToString(CultureInfo.InvariantCulture), res.Value.Val);
}
}
@@ -274,7 +306,7 @@ namespace Apache.Ignite.Tests.Table
foreach (var id in ids)
{
var res = await PocoView.GetAsync(null, GetPoco(id));
- Assert.AreEqual(id.ToString(CultureInfo.InvariantCulture), res!.Val);
+ Assert.AreEqual(id.ToString(CultureInfo.InvariantCulture), res.Value.Val);
}
}
@@ -291,7 +323,7 @@ namespace Apache.Ignite.Tests.Table
foreach (var id in ids)
{
var res = await PocoView.GetAsync(null, GetPoco(id));
- Assert.AreEqual(id.ToString(CultureInfo.InvariantCulture), res!.Val);
+ Assert.AreEqual(id.ToString(CultureInfo.InvariantCulture), res.Value.Val);
}
}
@@ -318,13 +350,13 @@ namespace Apache.Ignite.Tests.Table
{
var res = await PocoView.GetAsync(null, GetPoco(id));
- if (existing.TryGetValue(res!.Key, out var old))
+ if (existing.TryGetValue(res.Value.Key, out var old))
{
- Assert.AreEqual(old.Val, res.Val);
+ Assert.AreEqual(old.Val, res.Value.Val);
}
else
{
- Assert.AreEqual(id.ToString(CultureInfo.InvariantCulture), res.Val);
+ Assert.AreEqual(id.ToString(CultureInfo.InvariantCulture), res.Value.Val);
}
}
}
@@ -353,15 +385,15 @@ namespace Apache.Ignite.Tests.Table
// TODO: Key order should be preserved by the server (IGNITE-16004).
var res = await PocoView.GetAllAsync(null, Enumerable.Range(9, 4).Select(x => GetPoco(x)));
- var resArr = res.OrderBy(x => x?.Key).ToArray();
+ var resArr = res.OrderBy(x => x.Value.Key).ToArray();
Assert.AreEqual(2, res.Count);
- Assert.AreEqual(9, resArr[0]!.Key);
- Assert.AreEqual("9", resArr[0]!.Val);
+ Assert.AreEqual(9, resArr[0].Value.Key);
+ Assert.AreEqual("9", resArr[0].Value.Val);
- Assert.AreEqual(10, resArr[1]!.Key);
- Assert.AreEqual("10", resArr[1]!.Val);
+ Assert.AreEqual(10, resArr[1].Value.Key);
+ Assert.AreEqual("10", resArr[1].Value.Val);
}
[Test]
@@ -403,8 +435,8 @@ namespace Apache.Ignite.Tests.Table
var skipped = await PocoView.DeleteAllAsync(null, new[] { GetPoco(1), GetPoco(2) });
Assert.AreEqual(0, skipped.Count);
- Assert.IsNull(await PocoView.GetAsync(null, GetPoco(1)));
- Assert.IsNull(await PocoView.GetAsync(null, GetPoco(2)));
+ Assert.IsFalse((await PocoView.GetAsync(null, GetPoco(1))).HasValue);
+ Assert.IsFalse((await PocoView.GetAsync(null, GetPoco(2))).HasValue);
}
[Test]
@@ -415,9 +447,9 @@ namespace Apache.Ignite.Tests.Table
Assert.AreEqual(1, skipped.Count);
Assert.AreEqual(4, skipped[0].Key);
- Assert.IsNull(await PocoView.GetAsync(null, GetPoco(1)));
- Assert.IsNull(await PocoView.GetAsync(null, GetPoco(2)));
- Assert.IsNotNull(await PocoView.GetAsync(null, GetPoco(3)));
+ Assert.IsFalse((await PocoView.GetAsync(null, GetPoco(1))).HasValue);
+ Assert.IsFalse((await PocoView.GetAsync(null, GetPoco(2))).HasValue);
+ Assert.IsTrue((await PocoView.GetAsync(null, GetPoco(3))).HasValue);
}
[Test]
@@ -445,8 +477,8 @@ namespace Apache.Ignite.Tests.Table
var skipped = await PocoView.DeleteAllExactAsync(null, new[] { GetPoco(1, "1"), GetPoco(2, "2") });
Assert.AreEqual(0, skipped.Count);
- Assert.IsNull(await PocoView.GetAsync(null, GetPoco(1)));
- Assert.IsNull(await PocoView.GetAsync(null, GetPoco(2)));
+ Assert.IsFalse((await PocoView.GetAsync(null, GetPoco(1))).HasValue);
+ Assert.IsFalse((await PocoView.GetAsync(null, GetPoco(2))).HasValue);
}
[Test]
@@ -456,9 +488,9 @@ namespace Apache.Ignite.Tests.Table
var skipped = await PocoView.DeleteAllExactAsync(null, new[] { GetPoco(1, "1"), GetPoco(2, "22") });
Assert.AreEqual(1, skipped.Count);
- Assert.IsNull(await PocoView.GetAsync(null, GetPoco(1)));
- Assert.IsNotNull(await PocoView.GetAsync(null, GetPoco(2)));
- Assert.IsNotNull(await PocoView.GetAsync(null, GetPoco(3)));
+ Assert.IsFalse((await PocoView.GetAsync(null, GetPoco(1))).HasValue);
+ Assert.IsTrue((await PocoView.GetAsync(null, GetPoco(2))).HasValue);
+ Assert.IsTrue((await PocoView.GetAsync(null, GetPoco(3))).HasValue);
}
[Test]
@@ -520,9 +552,9 @@ namespace Apache.Ignite.Tests.Table
await PocoView.UpsertAsync(null, poco);
var keyTuple = new Poco { Key = key };
- var resTuple = (await PocoView.GetAsync(null, keyTuple))!;
+ var (resTuple, resTupleHasValue) = await PocoView.GetAsync(null, keyTuple);
- Assert.IsNotNull(resTuple);
+ Assert.IsTrue(resTupleHasValue);
Assert.AreEqual(key, resTuple.Key);
Assert.AreEqual(val, resTuple.Val);
}
@@ -558,9 +590,9 @@ namespace Apache.Ignite.Tests.Table
await pocoView.UpsertAsync(null, poco);
- var res = await pocoView.GetAsync(null, new Poco2 { Id = -1 });
+ var res = (await pocoView.GetAsync(null, new Poco2 { Id = -1 })).Value;
- Assert.AreEqual(poco.Prop1, res!.Prop1);
+ Assert.AreEqual(poco.Prop1, res.Prop1);
Assert.AreEqual(poco.Prop2, res.Prop2);
Assert.AreEqual(poco.Prop3, res.Prop3);
Assert.AreEqual(poco.Prop4, res.Prop4);
@@ -599,9 +631,9 @@ namespace Apache.Ignite.Tests.Table
await pocoView.UpsertAsync(null, poco);
- var res = await pocoView.GetAsync(null, poco);
+ var res = (await pocoView.GetAsync(null, poco)).Value;
- Assert.AreEqual(poco.Blob, res!.Blob);
+ Assert.AreEqual(poco.Blob, res.Blob);
Assert.AreEqual(poco.Date, res.Date);
Assert.AreEqual(poco.Decimal, res.Decimal);
Assert.AreEqual(poco.Double, res.Double);
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/Serialization/ObjectSerializerHandlerTests.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/Serialization/ObjectSerializerHandlerTests.cs
index 1d276584e1..a096224b81 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/Serialization/ObjectSerializerHandlerTests.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/Serialization/ObjectSerializerHandlerTests.cs
@@ -117,7 +117,6 @@ namespace Apache.Ignite.Tests.Table.Serialization
}
private static byte[] Write<T>(T obj, bool keyOnly = false)
- where T : class
{
var handler = new ObjectSerializerHandler<T>();
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Transactions/TransactionsTests.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/Transactions/TransactionsTests.cs
index 7d373ced17..b6f339d7b9 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Transactions/TransactionsTests.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Transactions/TransactionsTests.cs
@@ -46,38 +46,38 @@ namespace Apache.Ignite.Tests.Transactions
Assert.IsFalse(await TupleView.DeleteExactAsync(tx, GetTuple(1, "1")));
Assert.IsFalse(await TupleView.InsertAsync(tx, GetTuple(1, "111")));
- Assert.AreEqual(GetTuple(1, "22"), await TupleView.GetAsync(tx, key));
- Assert.AreEqual(GetTuple(1, "22"), await TupleView.GetAndUpsertAsync(tx, GetTuple(1, "33")));
- Assert.AreEqual(GetTuple(1, "33"), await TupleView.GetAndReplaceAsync(tx, GetTuple(1, "44")));
+ Assert.AreEqual(GetTuple(1, "22"), (await TupleView.GetAsync(tx, key)).Value);
+ Assert.AreEqual(GetTuple(1, "22"), (await TupleView.GetAndUpsertAsync(tx, GetTuple(1, "33"))).Value);
+ Assert.AreEqual(GetTuple(1, "33"), (await TupleView.GetAndReplaceAsync(tx, GetTuple(1, "44"))).Value);
Assert.IsTrue(await TupleView.ReplaceAsync(tx, GetTuple(1, "55")));
- Assert.AreEqual(GetTuple(1, "55"), await TupleView.GetAndDeleteAsync(tx, key));
+ Assert.AreEqual(GetTuple(1, "55"), (await TupleView.GetAndDeleteAsync(tx, key)).Value);
Assert.IsFalse(await TupleView.DeleteAsync(tx, key));
await TupleView.UpsertAllAsync(tx, new[] { GetTuple(1, "6"), GetTuple(2, "7") });
Assert.AreEqual(2, (await TupleView.GetAllAsync(tx, new[] { key, GetTuple(2), GetTuple(3) })).Count);
var insertAllRes = await TupleView.InsertAllAsync(tx, new[] { GetTuple(1, "8"), GetTuple(3, "9") });
- Assert.AreEqual(GetTuple(1, "6"), await TupleView.GetAsync(tx, key));
+ Assert.AreEqual(GetTuple(1, "6"), (await TupleView.GetAsync(tx, key)).Value);
Assert.AreEqual(GetTuple(1, "8"), insertAllRes.Single());
Assert.IsFalse(await TupleView.ReplaceAsync(tx, GetTuple(-1)));
Assert.IsTrue(await TupleView.ReplaceAsync(tx, GetTuple(1, "10")));
- Assert.AreEqual(GetTuple(1, "10"), await TupleView.GetAsync(tx, key));
+ Assert.AreEqual(GetTuple(1, "10"), (await TupleView.GetAsync(tx, key)).Value);
Assert.IsFalse(await TupleView.ReplaceAsync(tx, GetTuple(1, "1"), GetTuple(1, "11")));
Assert.IsTrue(await TupleView.ReplaceAsync(tx, GetTuple(1, "10"), GetTuple(1, "12")));
- Assert.AreEqual(GetTuple(1, "12"), await TupleView.GetAsync(tx, key));
+ Assert.AreEqual(GetTuple(1, "12"), (await TupleView.GetAsync(tx, key)).Value);
var deleteAllRes = await TupleView.DeleteAllAsync(tx, new[] { GetTuple(3), GetTuple(4) });
Assert.AreEqual(4, deleteAllRes.Single()[0]);
- Assert.IsNull(await TupleView.GetAsync(tx, GetTuple(3)));
+ Assert.IsFalse((await TupleView.GetAsync(tx, GetTuple(3))).HasValue);
var deleteAllExactRes = await TupleView.DeleteAllAsync(tx, new[] { GetTuple(1, "12"), GetTuple(5) });
Assert.AreEqual(5, deleteAllExactRes.Single()[0]);
- Assert.IsNull(await TupleView.GetAsync(tx, key));
+ Assert.IsFalse((await TupleView.GetAsync(tx, key)).HasValue);
await tx.RollbackAsync();
- Assert.AreEqual(GetTuple(1, "1"), await TupleView.GetAsync(null, key));
+ Assert.AreEqual(GetTuple(1, "1"), (await TupleView.GetAsync(null, key)).Value);
}
[Test]
@@ -88,7 +88,7 @@ namespace Apache.Ignite.Tests.Transactions
await tx.CommitAsync();
var res = await TupleView.GetAsync(null, GetTuple(1));
- Assert.AreEqual("2", res![ValCol]);
+ Assert.AreEqual("2", res.Value[ValCol]);
}
[Test]
@@ -98,8 +98,8 @@ namespace Apache.Ignite.Tests.Transactions
await TupleView.UpsertAsync(tx, GetTuple(1, "2"));
await tx.RollbackAsync();
- var res = await TupleView.GetAsync(null, GetTuple(1));
- Assert.IsNull(res);
+ var (_, hasValue) = await TupleView.GetAsync(null, GetTuple(1));
+ Assert.IsFalse(hasValue);
}
[Test]
@@ -111,8 +111,8 @@ namespace Apache.Ignite.Tests.Transactions
await tx.RollbackAsync();
}
- var res = await TupleView.GetAsync(null, GetTuple(1));
- Assert.IsNull(res);
+ var (_, hasValue) = await TupleView.GetAsync(null, GetTuple(1));
+ Assert.IsFalse(hasValue);
}
[Test]
@@ -169,7 +169,7 @@ namespace Apache.Ignite.Tests.Transactions
await table!.RecordBinaryView.UpsertAsync(tx, GetTuple(1, "2"));
}
- Assert.AreEqual("1", (await TupleView.GetAsync(null, GetTuple(1)))![ValCol]);
+ Assert.AreEqual("1", (await TupleView.GetAsync(null, GetTuple(1))).Value[ValCol]);
}
[Test]
diff --git a/modules/platforms/dotnet/Apache.Ignite/Apache.Ignite.csproj b/modules/platforms/dotnet/Apache.Ignite/Apache.Ignite.csproj
index 35905d96f2..6922504ce9 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Apache.Ignite.csproj
+++ b/modules/platforms/dotnet/Apache.Ignite/Apache.Ignite.csproj
@@ -29,6 +29,7 @@
</PropertyGroup>
<ItemGroup>
+ <PackageReference Include="JetBrains.Annotations" Version="2022.1.0" PrivateAssets="all" />
<PackageReference Include="MessagePack" Version="[2.1.80,)" />
<PackageReference Include="NodaTime" Version="[3.*,)" />
</ItemGroup>
diff --git a/modules/platforms/dotnet/Apache.Ignite/Compute/ICompute.cs b/modules/platforms/dotnet/Apache.Ignite/Compute/ICompute.cs
index 8c893bd6d3..7ab817b05f 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Compute/ICompute.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Compute/ICompute.cs
@@ -58,8 +58,7 @@ namespace Apache.Ignite.Compute
/// <typeparam name="T">Job result type.</typeparam>
/// <typeparam name="TKey">Key type.</typeparam>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
- Task<T> ExecuteColocatedAsync<T, TKey>(string tableName, TKey key, string jobClassName, params object[] args)
- where TKey : class; // TODO: Remove class constraint (IGNITE-16355)
+ Task<T> ExecuteColocatedAsync<T, TKey>(string tableName, TKey key, string jobClassName, params object[] args);
/// <summary>
/// Executes a compute job represented by the given class on all of the specified nodes.
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Common/IgniteArgumentCheck.cs b/modules/platforms/dotnet/Apache.Ignite/Internal/Common/IgniteArgumentCheck.cs
index c3bdae4ef8..6242144b33 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Common/IgniteArgumentCheck.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Common/IgniteArgumentCheck.cs
@@ -19,7 +19,7 @@
namespace Apache.Ignite.Internal.Common
{
using System;
- using System.Collections.Generic;
+ using JetBrains.Annotations;
/// <summary>
/// Arguments check helpers.
@@ -31,7 +31,8 @@ namespace Apache.Ignite.Internal.Common
/// </summary>
/// <param name="arg">The argument.</param>
/// <param name="argName">Name of the argument.</param>
- public static void NotNull(object arg, string argName)
+ /// <typeparam name="T">Arg type.</typeparam>
+ public static void NotNull<T>([NoEnumeration] T arg, string argName)
{
if (arg == null)
{
@@ -55,20 +56,6 @@ namespace Apache.Ignite.Internal.Common
return arg;
}
- /// <summary>
- /// Throws an ArgumentException if specified arg is null or empty string.
- /// </summary>
- /// <param name="collection">The collection.</param>
- /// <param name="argName">Name of the argument.</param>
- /// <typeparam name="T">Type.</typeparam>
- public static void NotNullOrEmpty<T>(ICollection<T> collection, string? argName)
- {
- if (collection == null || collection.Count == 0)
- {
- throw new ArgumentException($"'{argName}' argument should not be null or empty.", argName);
- }
- }
-
/// <summary>
/// Throws an ArgumentException if specified condition is false.
/// </summary>
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Compute/Compute.cs b/modules/platforms/dotnet/Apache.Ignite/Internal/Compute/Compute.cs
index f8985ef3fc..24b0ada1d7 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Compute/Compute.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Compute/Compute.cs
@@ -76,8 +76,7 @@ namespace Apache.Ignite.Internal.Compute
.ConfigureAwait(false);
/// <inheritdoc/>
- public async Task<T> ExecuteColocatedAsync<T, TKey>(string tableName, TKey key, string jobClassName, params object[] args)
- where TKey : class =>
+ public async Task<T> ExecuteColocatedAsync<T, TKey>(string tableName, TKey key, string jobClassName, params object[] args) =>
await ExecuteColocatedAsync<T, TKey>(
tableName,
key,
@@ -199,7 +198,6 @@ namespace Apache.Ignite.Internal.Compute
Func<Table, IRecordSerializerHandler<TKey>> serializerHandlerFunc,
string jobClassName,
params object[] args)
- where TKey : class
{
// TODO: IGNITE-16990 - implement partition awareness.
IgniteArgumentCheck.NotNull(tableName, nameof(tableName));
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Sql/Sql.cs b/modules/platforms/dotnet/Apache.Ignite/Internal/Sql/Sql.cs
index 8b0e624f5b..b07e3a22e8 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Sql/Sql.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Sql/Sql.cs
@@ -91,7 +91,6 @@ namespace Apache.Ignite.Internal.Sql
/// <inheritdoc/>
public Task<IResultSet<T>> ExecuteAsync<T>(ITransaction? transaction, SqlStatement statement, params object[] args)
- where T : class
{
// TODO: IGNITE-17333 SQL ResultSet object mapping
throw new NotSupportedException();
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/RecordView.cs b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/RecordView.cs
index 0e6b9c2dff..f0bd09aab9 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/RecordView.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/RecordView.cs
@@ -33,7 +33,6 @@ namespace Apache.Ignite.Internal.Table
/// </summary>
/// <typeparam name="T">Record type.</typeparam>
internal sealed class RecordView<T> : IRecordView<T>
- where T : class
{
/** Table. */
private readonly Table _table;
@@ -58,7 +57,7 @@ namespace Apache.Ignite.Internal.Table
public RecordSerializer<T> RecordSerializer => _ser;
/// <inheritdoc/>
- public async Task<T?> GetAsync(ITransaction? transaction, T key)
+ public async Task<Option<T>> GetAsync(ITransaction? transaction, T key)
{
IgniteArgumentCheck.NotNull(key, nameof(key));
@@ -69,7 +68,7 @@ namespace Apache.Ignite.Internal.Table
}
/// <inheritdoc/>
- public async Task<IList<T?>> GetAllAsync(ITransaction? transaction, IEnumerable<T> keys)
+ public async Task<IList<Option<T>>> GetAllAsync(ITransaction? transaction, IEnumerable<T> keys)
{
IgniteArgumentCheck.NotNull(keys, nameof(keys));
@@ -77,7 +76,7 @@ namespace Apache.Ignite.Internal.Table
if (!iterator.MoveNext())
{
- return Array.Empty<T>();
+ return Array.Empty<Option<T>>();
}
var schema = await _table.GetLatestSchemaAsync().ConfigureAwait(false);
@@ -123,7 +122,7 @@ namespace Apache.Ignite.Internal.Table
}
/// <inheritdoc/>
- public async Task<T?> GetAndUpsertAsync(ITransaction? transaction, T record)
+ public async Task<Option<T>> GetAndUpsertAsync(ITransaction? transaction, T record)
{
IgniteArgumentCheck.NotNull(record, nameof(record));
@@ -192,7 +191,7 @@ namespace Apache.Ignite.Internal.Table
}
/// <inheritdoc/>
- public async Task<T?> GetAndReplaceAsync(ITransaction? transaction, T record)
+ public async Task<Option<T>> GetAndReplaceAsync(ITransaction? transaction, T record)
{
IgniteArgumentCheck.NotNull(record, nameof(record));
@@ -221,7 +220,7 @@ namespace Apache.Ignite.Internal.Table
}
/// <inheritdoc/>
- public async Task<T?> GetAndDeleteAsync(ITransaction? transaction, T key)
+ public async Task<Option<T>> GetAndDeleteAsync(ITransaction? transaction, T key)
{
IgniteArgumentCheck.NotNull(key, nameof(key));
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/IRecordSerializerHandler.cs b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/IRecordSerializerHandler.cs
index 2b33037bad..da30a46ffc 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/IRecordSerializerHandler.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/IRecordSerializerHandler.cs
@@ -24,7 +24,6 @@ namespace Apache.Ignite.Internal.Table.Serialization
/// </summary>
/// <typeparam name="T">Record type.</typeparam>
internal interface IRecordSerializerHandler<T>
- where T : class
{
/// <summary>
/// Reads a record.
@@ -42,7 +41,7 @@ namespace Apache.Ignite.Internal.Table.Serialization
/// <param name="schema">Schema.</param>
/// <param name="key">Key part.</param>
/// <returns>Resulting record with key and value parts.</returns>
- T? ReadValuePart(ref MessagePackReader reader, Schema schema, T key);
+ T ReadValuePart(ref MessagePackReader reader, Schema schema, T key);
/// <summary>
/// Writes a record.
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 914bf87bfd..f518175d00 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/ObjectSerializerHandler.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/ObjectSerializerHandler.cs
@@ -30,7 +30,6 @@ namespace Apache.Ignite.Internal.Table.Serialization
/// </summary>
/// <typeparam name="T">Object type.</typeparam>
internal class ObjectSerializerHandler<T> : IRecordSerializerHandler<T>
- where T : class
{
private readonly ConcurrentDictionary<(int, bool), WriteDelegate<T>> _writers = new();
@@ -159,7 +158,7 @@ namespace Apache.Ignite.Internal.Table.Serialization
skipVisibility: true);
var il = method.GetILGenerator();
- il.DeclareLocal(type);
+ var local = il.DeclareLocal(type);
il.Emit(OpCodes.Ldtoken, type);
il.Emit(OpCodes.Call, ReflectionUtils.GetTypeFromHandleMethod);
@@ -175,7 +174,7 @@ namespace Apache.Ignite.Internal.Table.Serialization
var col = columns[i];
var fieldInfo = type.GetFieldIgnoreCase(col.Name);
- EmitFieldRead(fieldInfo, il, col, i);
+ EmitFieldRead(fieldInfo, il, col, i, local);
}
il.Emit(OpCodes.Ldloc_0); // res
@@ -196,13 +195,20 @@ namespace Apache.Ignite.Internal.Table.Serialization
skipVisibility: true);
var il = method.GetILGenerator();
- il.DeclareLocal(type);
+ var local = il.DeclareLocal(type);
- il.Emit(OpCodes.Ldtoken, type);
- il.Emit(OpCodes.Call, ReflectionUtils.GetTypeFromHandleMethod);
- il.Emit(OpCodes.Call, ReflectionUtils.GetUninitializedObjectMethod);
-
- il.Emit(OpCodes.Stloc_0); // T res
+ if (type.IsValueType)
+ {
+ il.Emit(OpCodes.Ldloca_S, local);
+ il.Emit(OpCodes.Initobj, type);
+ }
+ else
+ {
+ il.Emit(OpCodes.Ldtoken, type);
+ il.Emit(OpCodes.Call, ReflectionUtils.GetTypeFromHandleMethod);
+ il.Emit(OpCodes.Call, ReflectionUtils.GetUninitializedObjectMethod);
+ il.Emit(OpCodes.Stloc_0); // T res
+ }
var columns = schema.Columns;
@@ -215,7 +221,7 @@ namespace Apache.Ignite.Internal.Table.Serialization
{
if (fieldInfo != null)
{
- il.Emit(OpCodes.Ldloc_0); // res
+ il.Emit(type.IsValueType ? OpCodes.Ldloca_S : OpCodes.Ldloc, local); // res
il.Emit(OpCodes.Ldarg_1); // key
il.Emit(OpCodes.Ldfld, fieldInfo);
il.Emit(OpCodes.Stfld, fieldInfo);
@@ -224,7 +230,7 @@ namespace Apache.Ignite.Internal.Table.Serialization
continue;
}
- EmitFieldRead(fieldInfo, il, col, i - schema.KeyColumnCount);
+ EmitFieldRead(fieldInfo, il, col, i - schema.KeyColumnCount, local);
}
il.Emit(OpCodes.Ldloc_0); // res
@@ -233,7 +239,7 @@ namespace Apache.Ignite.Internal.Table.Serialization
return (ReadValuePartDelegate<T>)method.CreateDelegate(typeof(ReadValuePartDelegate<T>));
}
- private static void EmitFieldRead(FieldInfo? fieldInfo, ILGenerator il, Column col, int elemIdx)
+ private static void EmitFieldRead(FieldInfo? fieldInfo, ILGenerator il, Column col, int elemIdx, LocalBuilder local)
{
if (fieldInfo == null)
{
@@ -244,7 +250,7 @@ namespace Apache.Ignite.Internal.Table.Serialization
var readMethod = BinaryTupleMethods.GetReadMethod(fieldInfo.FieldType);
- il.Emit(OpCodes.Ldloc_0); // res
+ il.Emit(fieldInfo.DeclaringType!.IsValueType ? OpCodes.Ldloca_S : OpCodes.Ldloc, local); // res
il.Emit(OpCodes.Ldarg_0); // reader
EmitLdcI4(il, elemIdx); // index
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/RecordSerializer.cs b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/RecordSerializer.cs
index 7913304cbe..29412babf5 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/RecordSerializer.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/RecordSerializer.cs
@@ -30,7 +30,6 @@ namespace Apache.Ignite.Internal.Table.Serialization
/// </summary>
/// <typeparam name="T">Record type.</typeparam>
internal class RecordSerializer<T>
- where T : class
{
/** Table. */
private readonly Table _table;
@@ -61,12 +60,12 @@ namespace Apache.Ignite.Internal.Table.Serialization
/// <param name="schema">Schema or null when there is no value.</param>
/// <param name="key">Key part.</param>
/// <returns>Resulting record with key and value parts.</returns>
- public T? ReadValue(PooledBuffer buf, Schema? schema, T key)
+ public Option<T> ReadValue(PooledBuffer buf, Schema? schema, T key)
{
if (schema == null)
{
// Null schema means null record.
- return null;
+ return default;
}
// Skip schema version.
@@ -113,12 +112,12 @@ namespace Apache.Ignite.Internal.Table.Serialization
/// <param name="schema">Schema or null when there is no value.</param>
/// <param name="keyOnly">Key only mode.</param>
/// <returns>List of records.</returns>
- public IList<T?> ReadMultipleNullable(PooledBuffer buf, Schema? schema, bool keyOnly = false)
+ public IList<Option<T>> ReadMultipleNullable(PooledBuffer buf, Schema? schema, bool keyOnly = false)
{
if (schema == null)
{
// Null schema means empty collection.
- return Array.Empty<T?>();
+ return Array.Empty<Option<T>>();
}
// Skip schema version.
@@ -126,13 +125,15 @@ namespace Apache.Ignite.Internal.Table.Serialization
r.Skip();
var count = r.ReadInt32();
- var res = new List<T?>(count);
+ var res = new List<Option<T>>(count);
for (var i = 0; i < count; i++)
{
- var hasValue = r.ReadBoolean();
+ Option<T> option = r.ReadBoolean()
+ ? _handler.Read(ref r, schema, keyOnly)
+ : default(Option<T>);
- res.Add(hasValue ? _handler.Read(ref r, schema, keyOnly) : null);
+ res.Add(option);
}
return res;
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Table.cs b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Table.cs
index 904023b2eb..1f64ae8f45 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Table.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Table.cs
@@ -81,9 +81,7 @@ namespace Apache.Ignite.Internal.Table
internal Guid Id { get; }
/// <inheritdoc/>
- public IRecordView<T> GetRecordView<T>()
- where T : class =>
- GetRecordViewInternal<T>();
+ public IRecordView<T> GetRecordView<T>() => GetRecordViewInternal<T>();
/// <summary>
/// Gets the record view for the specified type.
@@ -91,7 +89,6 @@ namespace Apache.Ignite.Internal.Table
/// <typeparam name="T">Record type.</typeparam>
/// <returns>Record view.</returns>
internal RecordView<T> GetRecordViewInternal<T>()
- where T : class
{
// ReSharper disable once HeapView.CanAvoidClosure (generics prevent this)
return (RecordView<T>)_recordViews.GetOrAdd(
diff --git a/modules/platforms/dotnet/Apache.Ignite/Option.cs b/modules/platforms/dotnet/Apache.Ignite/Option.cs
new file mode 100644
index 0000000000..1ed1fb03fd
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite/Option.cs
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache.Ignite;
+
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Text;
+
+/// <summary>
+/// A wrapper that may or may not contain a value of type <typeparamref name="T"/>.
+/// </summary>
+/// <typeparam name="T">Value type.</typeparam>
+public readonly record struct Option<T>
+{
+ private readonly T _value;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Option{T}"/> struct.
+ /// </summary>
+ /// <param name="value">Value.</param>
+ /// <param name="hasValue">Whether the value is present.</param>
+ [SuppressMessage(
+ "StyleCop.CSharp.DocumentationRules",
+ "SA1642:ConstructorSummaryDocumentationMustBeginWithStandardText",
+ Justification = "False positive.")]
+ private Option(T value, bool hasValue)
+ {
+ _value = value;
+ HasValue = hasValue;
+ }
+
+ /// <summary>
+ /// Gets the value.
+ /// </summary>
+ public T Value => HasValue
+ ? _value
+ : throw new InvalidOperationException("Value is not present. Check HasValue property before accessing Value.");
+
+ /// <summary>
+ /// Gets a value indicating whether the value is present.
+ /// </summary>
+ public bool HasValue { get; }
+
+ /// <summary>
+ /// Wraps a value into an option.
+ /// </summary>
+ /// <param name="value">Value.</param>
+ /// <returns>Wrapped value.</returns>
+ public static implicit operator Option<T>(T value) => new(value, true);
+
+ /// <summary>
+ /// Deconstructs this instance.
+ /// </summary>
+ /// <param name="value">Value.</param>
+ /// <param name="hasValue">Whether the value is present.</param>
+ public void Deconstruct(out T value, out bool hasValue)
+ {
+ value = _value;
+ hasValue = HasValue;
+ }
+
+ private bool PrintMembers(StringBuilder builder)
+ {
+ builder.Append("HasValue = ");
+ builder.Append(HasValue);
+
+ if (HasValue)
+ {
+ builder.Append(", Value = ");
+ builder.Append(_value);
+ }
+
+ return true;
+ }
+}
+
+/// <summary>
+/// Static helpers for <see cref="Option{T}"/>.
+/// </summary>
+public static class Option
+{
+ /// <summary>
+ /// Returns an option of the specified value.
+ /// </summary>
+ /// <param name="val">Value.</param>
+ /// <typeparam name="T">value type.</typeparam>
+ /// <returns>Option of T.</returns>
+ public static Option<T> Some<T>(T val) => val;
+
+ /// <summary>
+ /// Returns an option without a value.
+ /// </summary>
+ /// <typeparam name="T">value type.</typeparam>
+ /// <returns>Option of T.</returns>
+ public static Option<T> None<T>() => default;
+}
\ No newline at end of file
diff --git a/modules/platforms/dotnet/Apache.Ignite/Sql/IResultSet.cs b/modules/platforms/dotnet/Apache.Ignite/Sql/IResultSet.cs
index f264554a98..8a61bed18c 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Sql/IResultSet.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Sql/IResultSet.cs
@@ -33,7 +33,6 @@ namespace Apache.Ignite.Sql
/// </summary>
/// <typeparam name="T">Row type.</typeparam>
public interface IResultSet<T> : IAsyncEnumerable<T>, IAsyncDisposable, IDisposable
- where T : class // TODO: Remove class constraint (IGNITE-16355)
{
/// <summary>
/// Gets result set metadata when <see cref="HasRowSet"/> is <c>true</c>, otherwise <c>null</c>.
diff --git a/modules/platforms/dotnet/Apache.Ignite/Sql/ISql.cs b/modules/platforms/dotnet/Apache.Ignite/Sql/ISql.cs
index 70fcf0c36b..ab7bbcfdcf 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Sql/ISql.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Sql/ISql.cs
@@ -43,7 +43,6 @@ namespace Apache.Ignite.Sql
/// <param name="args">Arguments for the statement.</param>
/// <typeparam name="T">Row type.</typeparam>
/// <returns>SQL result set.</returns>
- Task<IResultSet<T>> ExecuteAsync<T>(ITransaction? transaction, SqlStatement statement, params object[] args)
- where T : class; // TODO: Remove class constraint (IGNITE-16355)
+ Task<IResultSet<T>> ExecuteAsync<T>(ITransaction? transaction, SqlStatement statement, params object[] args);
}
}
diff --git a/modules/platforms/dotnet/Apache.Ignite/Table/IRecordView.cs b/modules/platforms/dotnet/Apache.Ignite/Table/IRecordView.cs
index 668a0237bc..b13c5295b0 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Table/IRecordView.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Table/IRecordView.cs
@@ -26,7 +26,6 @@ namespace Apache.Ignite.Table
/// </summary>
/// <typeparam name="T">Record type.</typeparam>
public interface IRecordView<T>
- where T : class // TODO: Remove class constraint (IGNITE-16355)
{
/// <summary>
/// Gets a record by key.
@@ -37,7 +36,7 @@ namespace Apache.Ignite.Table
/// A <see cref="Task"/> representing the asynchronous operation.
/// The task result contains a record with all columns.
/// </returns>
- Task<T?> GetAsync(ITransaction? transaction, T key);
+ Task<Option<T>> GetAsync(ITransaction? transaction, T key);
/// <summary>
/// Gets multiple records by keys.
@@ -50,7 +49,7 @@ namespace Apache.Ignite.Table
/// elements is guaranteed to be the same as the order of <paramref name="keys"/>. If a record does not exist,
/// the element at the corresponding index of the resulting collection will be <c>null</c>.
/// </returns>
- Task<IList<T?>> GetAllAsync(ITransaction? transaction, IEnumerable<T> keys);
+ Task<IList<Option<T>>> GetAllAsync(ITransaction? transaction, IEnumerable<T> keys);
/// <summary>
/// Inserts a record into the table if it does not exist or replaces the existing one.
@@ -77,7 +76,7 @@ namespace Apache.Ignite.Table
/// A <see cref="Task"/> representing the asynchronous operation.
/// The task result contains replaced record or null if it did not exist.
/// </returns>
- Task<T?> GetAndUpsertAsync(ITransaction? transaction, T record);
+ Task<Option<T>> GetAndUpsertAsync(ITransaction? transaction, T record);
/// <summary>
/// Inserts a record into the table if it does not exist.
@@ -135,7 +134,7 @@ namespace Apache.Ignite.Table
/// A <see cref="Task"/> representing the asynchronous operation.
/// The task result contains the previous value for the given key, or <c>null</c> if it did not exist.
/// </returns>
- Task<T?> GetAndReplaceAsync(ITransaction? transaction, T record);
+ Task<Option<T>> GetAndReplaceAsync(ITransaction? transaction, T record);
/// <summary>
/// Deletes a record with the specified key.
@@ -168,7 +167,7 @@ namespace Apache.Ignite.Table
/// A <see cref="Task"/> representing the asynchronous operation.
/// The task result contains deleted record or <c>null</c> if it did not exist.
/// </returns>
- Task<T?> GetAndDeleteAsync(ITransaction? transaction, T key);
+ Task<Option<T>> GetAndDeleteAsync(ITransaction? transaction, T key);
/// <summary>
/// Deletes multiple records. If one or more keys do not exist, other records are still deleted.
diff --git a/modules/platforms/dotnet/Apache.Ignite/Table/ITable.cs b/modules/platforms/dotnet/Apache.Ignite/Table/ITable.cs
index 82e75c5c63..e6c49a30ad 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Table/ITable.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Table/ITable.cs
@@ -40,7 +40,6 @@ namespace Apache.Ignite.Table
/// </summary>
/// <typeparam name="T">Record type.</typeparam>
/// <returns>Record view.</returns>
- public IRecordView<T> GetRecordView<T>() // TODO: Custom mapping (IGNITE-16356)
- where T : class;
+ public IRecordView<T> GetRecordView<T>(); // TODO: Custom mapping (IGNITE-16356)
}
}