Skip to content

Commit 3713334

Browse files
committed
Nullability and value-type improvements
1 parent e48dd43 commit 3713334

File tree

9 files changed

+154
-68
lines changed

9 files changed

+154
-68
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ using SQLiteConnection Connection = new(":memory:");
9595
Connection.Orm.RegisterType<Sweet>(
9696
SqliteType.Text,
9797
serialize: (Sweet Sweet) => JsonSerializer.Serialize(Sweet),
98-
deserialize: (SqliteValue Value, Type ClrType) => (Sweet?)JsonSerializer.Deserialize(Value.AsText, ClrType)
98+
deserialize: (SqliteValue Value, Type ClrType) => (Sweet?)JsonSerializer.Deserialize(Value.CastText, ClrType)
9999
);
100100
```
101101

SQLiteSharp.Tests/NullableTest.cs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
namespace SQLiteSharp.Tests;
2+
3+
public class NullableTest {
4+
[Fact]
5+
public void Test1() {
6+
// Open a database connection
7+
using SqliteConnection Connection = new(":memory:");
8+
9+
// Create a table for a class
10+
SqliteTable<NullableItem> NullableItems = Connection.GetTable<NullableItem>("NullableItems");
11+
12+
// Insert items into the table
13+
NullableItems.Insert(new NullableItem() {
14+
NullableIntWithNullValue = null,
15+
NullableIntWithNonNullValue = 3,
16+
NonNullableInt = 4,
17+
});
18+
19+
// Find one item in the table matching a predicate
20+
NullableItem Item = NullableItems.FindAll().First();
21+
Assert.Null(Item.NullableIntWithNullValue);
22+
Assert.Equal(3, Item.NullableIntWithNonNullValue);
23+
Assert.Equal(4, Item.NonNullableInt);
24+
}
25+
[Fact]
26+
public void TestSqliteValue() {
27+
// Test nullable integer with non-null value
28+
SqliteValue NullableIntWithNullValue = (long?)null;
29+
Assert.Throws<NullReferenceException>(() => NullableIntWithNullValue.CastInteger);
30+
Assert.Null(NullableIntWithNullValue.AsInteger);
31+
Assert.True(NullableIntWithNullValue.IsNull);
32+
33+
// Test nullable integer with non-null value
34+
SqliteValue NullableIntWithNonNullValue = (long?)3;
35+
Assert.Equal(3, NullableIntWithNonNullValue.CastInteger);
36+
Assert.Equal(3, NullableIntWithNonNullValue.AsInteger);
37+
Assert.False(NullableIntWithNonNullValue.IsNull);
38+
39+
// Test non-nullable integer
40+
SqliteValue NonNullableInt = (long)4;
41+
Assert.Equal(4, NonNullableInt.CastInteger);
42+
Assert.Equal(4, NonNullableInt.AsInteger);
43+
Assert.False(NonNullableInt.IsNull);
44+
}
45+
}
46+
47+
public struct NullableItem {
48+
public int? NullableIntWithNullValue { get; set; }
49+
public int? NullableIntWithNonNullValue { get; set; }
50+
public int NonNullableInt { get; set; }
51+
}

SQLiteSharp.Tests/ReadMeTest.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using System.Text.Json;
2+
13
namespace SQLiteSharp.Tests;
24

35
public class ReadMeTest {
@@ -41,8 +43,8 @@ public void Test2() {
4143
// Register custom type
4244
Connection.Orm.RegisterType<Sweet>(
4345
SqliteType.Text,
44-
serialize: (Sweet Sweet) => System.Text.Json.JsonSerializer.Serialize(Sweet),
45-
deserialize: (SqliteValue Value, Type ClrType) => (Sweet?)System.Text.Json.JsonSerializer.Deserialize(Value.AsText, ClrType)
46+
serialize: (Sweet Sweet) => JsonSerializer.Serialize(Sweet),
47+
deserialize: (SqliteValue Value, Type ClrType) => (Sweet?)JsonSerializer.Deserialize(Value.CastText, ClrType)
4648
);
4749

4850
// Create a table for a class

SQLiteSharp.Tests/SQLiteSharp.Tests.csproj

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@
77
<IsPackable>false</IsPackable>
88
</PropertyGroup>
99
<ItemGroup>
10-
<PackageReference Include="coverlet.collector" Version="6.0.2" />
10+
<PackageReference Include="coverlet.collector" Version="6.0.3">
11+
<PrivateAssets>all</PrivateAssets>
12+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
13+
</PackageReference>
1114
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
12-
<PackageReference Include="xunit" Version="2.9.2" />
13-
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.0">
15+
<PackageReference Include="xunit" Version="2.9.3" />
16+
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.1">
1417
<PrivateAssets>all</PrivateAssets>
1518
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1619
</PackageReference>

SQLiteSharp.slnx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
<Solution>
22
<Project Path="SQLiteSharp/SQLiteSharp.csproj" />
3+
<Folder Name="/Solution Items/">
4+
<File Path="README.md" />
5+
</Folder>
36
<Project Path="SQLiteSharp.Tests/SQLiteSharp.Tests.csproj" />
47
</Solution>

SQLiteSharp/Orm.cs

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ public SqliteValue Serialize(object? clr) {
108108
/// Gets a type serializer for the given type and deserializes the object from a <see cref="SqliteValue"/>.
109109
/// </summary>
110110
public object? Deserialize(SqliteValue sqlite, Type clrType) {
111-
if (sqlite.SqliteType is SqliteType.Null) {
111+
if (sqlite.IsNull) {
112112
return null;
113113
}
114114
return GetTypeSerializer(clrType).Deserialize(sqlite, clrType);
@@ -158,7 +158,7 @@ private void AddDefaultTypeSerializers() {
158158
RegisterType<object>(
159159
sqliteType: SqliteType.Text,
160160
serialize: (object clr) => JsonSerializer.Serialize(clr, jsonOptions),
161-
deserialize: (SqliteValue sqlite, Type clrType) => JsonSerializer.Deserialize(sqlite.AsText, clrType, jsonOptions)
161+
deserialize: (SqliteValue sqlite, Type clrType) => JsonSerializer.Deserialize(sqlite.CastText, clrType, jsonOptions)!
162162
);
163163

164164
RegisterType<SqliteValue>(
@@ -169,107 +169,107 @@ private void AddDefaultTypeSerializers() {
169169
RegisterType<bool>(
170170
SqliteType.Integer,
171171
serialize: (bool clr) => clr ? 1 : 0,
172-
deserialize: (SqliteValue sqlite, Type clrType) => (int)sqlite.AsInteger != 0
172+
deserialize: (SqliteValue sqlite, Type clrType) => (int)sqlite.CastInteger != 0
173173
);
174174
RegisterType<string>(
175175
SqliteType.Text,
176176
serialize: (string clr) => clr,
177-
deserialize: (SqliteValue sqlite, Type clrType) => sqlite.AsText
177+
deserialize: (SqliteValue sqlite, Type clrType) => sqlite.CastText
178178
);
179179
RegisterType<byte>(
180180
SqliteType.Integer,
181181
serialize: (byte clr) => clr,
182-
deserialize: (SqliteValue sqlite, Type clrType) => (byte)sqlite.AsInteger
182+
deserialize: (SqliteValue sqlite, Type clrType) => (byte)sqlite.CastInteger
183183
);
184184
RegisterType<sbyte>(
185185
SqliteType.Integer,
186186
serialize: (sbyte clr) => clr,
187-
deserialize: (SqliteValue sqlite, Type clrType) => (sbyte)sqlite.AsInteger
187+
deserialize: (SqliteValue sqlite, Type clrType) => (sbyte)sqlite.CastInteger
188188
);
189189
RegisterType<short>(
190190
SqliteType.Integer,
191191
serialize: (short clr) => clr,
192-
deserialize: (SqliteValue sqlite, Type clrType) => (short)sqlite.AsInteger
192+
deserialize: (SqliteValue sqlite, Type clrType) => (short)sqlite.CastInteger
193193
);
194194
RegisterType<ushort>(
195195
SqliteType.Integer,
196196
serialize: (ushort clr) => clr,
197-
deserialize: (SqliteValue sqlite, Type clrType) => (ushort)sqlite.AsInteger
197+
deserialize: (SqliteValue sqlite, Type clrType) => (ushort)sqlite.CastInteger
198198
);
199199
RegisterType<int>(
200200
SqliteType.Integer,
201201
serialize: (int clr) => clr,
202-
deserialize: (SqliteValue sqlite, Type clrType) => (int)sqlite.AsInteger
202+
deserialize: (SqliteValue sqlite, Type clrType) => (int)sqlite.CastInteger
203203
);
204204
RegisterType<uint>(
205205
SqliteType.Integer,
206206
serialize: (uint clr) => clr,
207-
deserialize: (SqliteValue sqlite, Type clrType) => (uint)sqlite.AsInteger
207+
deserialize: (SqliteValue sqlite, Type clrType) => (uint)sqlite.CastInteger
208208
);
209209
RegisterType<long>(
210210
SqliteType.Integer,
211211
serialize: (long clr) => clr,
212-
deserialize: (SqliteValue sqlite, Type clrType) => sqlite.AsInteger
212+
deserialize: (SqliteValue sqlite, Type clrType) => sqlite.CastInteger
213213
);
214214
RegisterType<ulong>(
215215
SqliteType.Integer,
216216
serialize: (ulong clr) => clr,
217-
deserialize: (SqliteValue sqlite, Type clrType) => (ulong)sqlite.AsInteger
217+
deserialize: (SqliteValue sqlite, Type clrType) => (ulong)sqlite.CastInteger
218218
);
219219
RegisterType<char>(
220220
SqliteType.Integer,
221221
serialize: (char clr) => clr,
222-
deserialize: (SqliteValue sqlite, Type clrType) => (char)sqlite.AsInteger
222+
deserialize: (SqliteValue sqlite, Type clrType) => (char)sqlite.CastInteger
223223
);
224224
RegisterType<float>(
225225
SqliteType.Float,
226226
serialize: (float clr) => clr,
227-
deserialize: (SqliteValue sqlite, Type clrType) => (float)sqlite.AsFloat
227+
deserialize: (SqliteValue sqlite, Type clrType) => (float)sqlite.CastFloat
228228
);
229229
RegisterType<double>(
230230
SqliteType.Float,
231231
serialize: (double clr) => clr,
232-
deserialize: (SqliteValue sqlite, Type clrType) => sqlite.AsFloat
232+
deserialize: (SqliteValue sqlite, Type clrType) => sqlite.CastFloat
233233
);
234234
RegisterType<TimeSpan>(
235235
SqliteType.Integer,
236236
serialize: (TimeSpan clr) => clr.Ticks,
237-
deserialize: (SqliteValue sqlite, Type clrType) => new TimeSpan(sqlite.AsInteger)
237+
deserialize: (SqliteValue sqlite, Type clrType) => new TimeSpan(sqlite.CastInteger)
238238
);
239239
RegisterType<DateTime>(
240240
SqliteType.Integer,
241241
serialize: (DateTime clr) => clr.Ticks,
242-
deserialize: (SqliteValue sqlite, Type clrType) => new DateTime(sqlite.AsInteger)
242+
deserialize: (SqliteValue sqlite, Type clrType) => new DateTime(sqlite.CastInteger)
243243
);
244244
RegisterType<Uri>(
245245
SqliteType.Text,
246246
serialize: (Uri clr) => clr.AbsoluteUri,
247-
deserialize: (SqliteValue sqlite, Type clrType) => new Uri(sqlite.AsText)
247+
deserialize: (SqliteValue sqlite, Type clrType) => new Uri(sqlite.CastText)
248248
);
249249
RegisterType<byte[]>(
250250
SqliteType.Blob,
251251
serialize: (byte[] clr) => clr,
252-
deserialize: (SqliteValue sqlite, Type clrType) => sqlite.AsBlob
252+
deserialize: (SqliteValue sqlite, Type clrType) => sqlite.CastBlob
253253
);
254254
RegisterType<IEnumerable<byte>>(
255255
SqliteType.Blob,
256256
serialize: (IEnumerable<byte> clr) => clr.ToArray(),
257-
deserialize: (SqliteValue sqlite, Type clrType) => sqlite.AsBlob
257+
deserialize: (SqliteValue sqlite, Type clrType) => sqlite.CastBlob
258258
);
259259
RegisterType<Enum>(
260260
SqliteType.Integer,
261261
serialize: (Enum clr) => Convert.ToInt64(clr),
262-
deserialize: (SqliteValue sqlite, Type clrType) => (Enum)Enum.ToObject(clrType, sqlite.AsInteger)
262+
deserialize: (SqliteValue sqlite, Type clrType) => (Enum)Enum.ToObject(clrType, sqlite.CastInteger)
263263
);
264264
RegisterType<StringBuilder>(
265265
SqliteType.Text,
266266
serialize: (StringBuilder clr) => clr.ToString(),
267-
deserialize: (SqliteValue sqlite, Type clrType) => new StringBuilder(sqlite.AsText)
267+
deserialize: (SqliteValue sqlite, Type clrType) => new StringBuilder(sqlite.CastText)
268268
);
269269
RegisterType<Guid>(
270270
SqliteType.Text,
271271
serialize: (Guid clr) => clr.ToString(),
272-
deserialize: (SqliteValue sqlite, Type clrType) => Guid.Parse(sqlite.AsText)
272+
deserialize: (SqliteValue sqlite, Type clrType) => Guid.Parse(sqlite.CastText)
273273
);
274274
}
275275
}

SQLiteSharp/SQLiteCommand.cs

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,11 @@ public IEnumerable<T> ExecuteScalars<T>() {
109109

110110
// Read value from found column
111111
object? value = ReadColumn(statement, i, column.ClrType);
112-
column.SetValue(row, value);
112+
113+
// Map read value (boxing value types)
114+
object boxedRow = row;
115+
column.SetValue(boxedRow, value);
116+
row = (T)boxedRow;
113117
}
114118

115119
// Return row object
@@ -152,28 +156,31 @@ private void BindParameter(Sqlite3Statement statement, int index, object? value)
152156
TypeSerializer typeSerializer = Connection.Orm.GetTypeSerializer(value.GetType());
153157
SqliteValue rawValue = typeSerializer.Serialize(value);
154158

155-
switch (rawValue.SqliteType) {
156-
case SqliteType.Null:
157-
SqliteRaw.BindNull(statement, index);
158-
break;
159-
case SqliteType.Integer:
160-
SqliteRaw.BindInt64(statement, index, rawValue.AsInteger);
161-
break;
162-
case SqliteType.Float:
163-
SqliteRaw.BindDouble(statement, index, rawValue.AsFloat);
164-
break;
165-
case SqliteType.Text:
166-
SqliteRaw.BindText(statement, index, rawValue.AsText);
167-
break;
168-
case SqliteType.Blob:
169-
SqliteRaw.BindBlob(statement, index, rawValue.AsBlob);
170-
break;
171-
default:
172-
throw new NotImplementedException($"Cannot bind column type '{rawValue.SqliteType}'");
159+
if (rawValue.IsNull) {
160+
SqliteRaw.BindNull(statement, index);
161+
}
162+
else {
163+
switch (rawValue.SqliteType) {
164+
case SqliteType.Integer:
165+
SqliteRaw.BindInt64(statement, index, rawValue.CastInteger);
166+
break;
167+
case SqliteType.Float:
168+
SqliteRaw.BindDouble(statement, index, rawValue.CastFloat);
169+
break;
170+
case SqliteType.Text:
171+
SqliteRaw.BindText(statement, index, rawValue.CastText);
172+
break;
173+
case SqliteType.Blob:
174+
SqliteRaw.BindBlob(statement, index, rawValue.CastBlob);
175+
break;
176+
default:
177+
throw new NotImplementedException($"Cannot bind column type '{rawValue.SqliteType}'");
178+
}
173179
}
174180
}
175181
private object? ReadColumn(Sqlite3Statement statement, int index, Type type) {
176-
return Connection.Orm.Deserialize(SqliteRaw.GetColumnValue(statement, index), type);
182+
SqliteValue rawValue = SqliteRaw.GetColumnValue(statement, index);
183+
return Connection.Orm.Deserialize(rawValue, type);
177184
}
178185
}
179186

0 commit comments

Comments
 (0)