Skip to content

Commit a49db3a

Browse files
authored
Merge pull request #479 from GeertvanHorrik/pr/enum-performance
Enum performance improvements to solve #458
2 parents 31da82b + e342123 commit a49db3a

File tree

5 files changed

+252
-17
lines changed

5 files changed

+252
-17
lines changed

src/SQLite.cs

Lines changed: 74 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2014,7 +2014,63 @@ public object GetValue (object obj)
20142014
return _prop.GetValue (obj, null);
20152015
}
20162016
}
2017-
}
2017+
}
2018+
2019+
internal class EnumCacheInfo
2020+
{
2021+
public EnumCacheInfo(Type type)
2022+
{
2023+
#if !USE_NEW_REFLECTION_API
2024+
IsEnum = type.IsEnum;
2025+
#else
2026+
IsEnum = type.GetTypeInfo().IsEnum;
2027+
#endif
2028+
2029+
if (IsEnum)
2030+
{
2031+
// This is a big assumption, but for now support ints only as key, otherwise we still
2032+
// have to pay the price for boxing
2033+
EnumValues = Enum.GetValues(type).Cast<int>().ToDictionary(x => x, x => x.ToString());
2034+
2035+
#if !USE_NEW_REFLECTION_API
2036+
StoreAsText = type.GetCustomAttribute(typeof(StoreAsTextAttribute), false) != null;
2037+
#else
2038+
StoreAsText = type.GetTypeInfo().GetCustomAttribute(typeof(StoreAsTextAttribute), false) != null;
2039+
#endif
2040+
}
2041+
}
2042+
2043+
public bool IsEnum { get; private set; }
2044+
2045+
public bool StoreAsText { get; private set; }
2046+
2047+
public Dictionary<int, string> EnumValues { get; private set; }
2048+
}
2049+
2050+
internal static class EnumCache
2051+
{
2052+
private static readonly Dictionary<Type, EnumCacheInfo> Cache = new Dictionary<Type, EnumCacheInfo>();
2053+
2054+
public static EnumCacheInfo GetInfo<T>()
2055+
{
2056+
return GetInfo(typeof(T));
2057+
}
2058+
2059+
public static EnumCacheInfo GetInfo(Type type)
2060+
{
2061+
lock (Cache)
2062+
{
2063+
EnumCacheInfo info = null;
2064+
if (!Cache.TryGetValue(type, out info))
2065+
{
2066+
info = new EnumCacheInfo(type);
2067+
Cache[type] = info;
2068+
}
2069+
2070+
return info;
2071+
}
2072+
}
2073+
}
20182074

20192075
public static class Orm
20202076
{
@@ -2369,22 +2425,24 @@ internal static void BindParameter (Sqlite3Statement stmt, int index, object val
23692425
}
23702426
} else if (value is DateTimeOffset) {
23712427
SQLite3.BindInt64 (stmt, index, ((DateTimeOffset)value).UtcTicks);
2372-
#if !USE_NEW_REFLECTION_API
2373-
} else if (value.GetType().IsEnum) {
2374-
#else
2375-
} else if (value.GetType().GetTypeInfo().IsEnum) {
2376-
#endif
2377-
if (value.GetType().GetTypeInfo().GetCustomAttribute(typeof(StoreAsTextAttribute), false) != null)
2378-
SQLite3.BindText(stmt, index, value.ToString(), -1, NegativePointer);
2379-
else
2380-
SQLite3.BindInt (stmt, index, Convert.ToInt32 (value));
2381-
} else if (value is byte[]){
2382-
SQLite3.BindBlob(stmt, index, (byte[]) value, ((byte[]) value).Length, NegativePointer);
2383-
} else if (value is Guid) {
2384-
SQLite3.BindText(stmt, index, ((Guid)value).ToString(), 72, NegativePointer);
23852428
} else {
2386-
throw new NotSupportedException("Cannot store type: " + value.GetType());
2387-
}
2429+
// Now we could possibly get an enum, retrieve cached info
2430+
var valueType = value.GetType();
2431+
var enumInfo = EnumCache.GetInfo(valueType);
2432+
if (enumInfo.IsEnum) {
2433+
var enumIntValue = Convert.ToInt32(value);
2434+
if (enumInfo.StoreAsText)
2435+
SQLite3.BindText(stmt, index, enumInfo.EnumValues[enumIntValue], -1, NegativePointer);
2436+
else
2437+
SQLite3.BindInt(stmt, index, enumIntValue);
2438+
} else if (value is byte[]){
2439+
SQLite3.BindBlob(stmt, index, (byte[]) value, ((byte[]) value).Length, NegativePointer);
2440+
} else if (value is Guid) {
2441+
SQLite3.BindText(stmt, index, ((Guid)value).ToString(), 72, NegativePointer);
2442+
} else {
2443+
throw new NotSupportedException("Cannot store type: " + value.GetType());
2444+
}
2445+
}
23882446
}
23892447
}
23902448

tests/EnumCacheTest.cs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Text;
6+
7+
#if NETFX_CORE
8+
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
9+
using SetUp = Microsoft.VisualStudio.TestPlatform.UnitTestFramework.TestInitializeAttribute;
10+
using TestFixture = Microsoft.VisualStudio.TestPlatform.UnitTestFramework.TestClassAttribute;
11+
using Test = Microsoft.VisualStudio.TestPlatform.UnitTestFramework.TestMethodAttribute;
12+
#else
13+
using NUnit.Framework;
14+
#endif
15+
16+
namespace SQLite.Tests
17+
{
18+
[TestFixture]
19+
public class EnumCacheTests
20+
{
21+
[StoreAsText]
22+
public enum TestEnumStoreAsText
23+
{
24+
Value1,
25+
26+
Value2,
27+
28+
Value3
29+
}
30+
31+
public enum TestEnumStoreAsInt
32+
{
33+
Value1,
34+
35+
Value2,
36+
37+
Value3
38+
}
39+
40+
public class TestClassThusNotEnum
41+
{
42+
43+
}
44+
45+
[Test]
46+
public void ShouldReturnTrueForEnumStoreAsText()
47+
{
48+
var info = EnumCache.GetInfo<TestEnumStoreAsText>();
49+
50+
Assert.IsTrue(info.IsEnum);
51+
Assert.IsTrue(info.StoreAsText);
52+
Assert.IsNotNull(info.EnumValues);
53+
54+
var values = Enum.GetValues(typeof(TestEnumStoreAsText)).Cast<int>().ToList();
55+
56+
for (int i = 0; i < values.Count; i++)
57+
{
58+
Assert.AreEqual(values[i].ToString(), info.EnumValues[i]);
59+
}
60+
}
61+
62+
[Test]
63+
public void ShouldReturnTrueForEnumStoreAsInt()
64+
{
65+
var info = EnumCache.GetInfo<TestEnumStoreAsInt>();
66+
67+
Assert.IsTrue(info.IsEnum);
68+
Assert.IsFalse(info.StoreAsText);
69+
Assert.IsNotNull(info.EnumValues);
70+
71+
var values = Enum.GetValues(typeof(TestEnumStoreAsInt)).Cast<int>().ToList();
72+
73+
for (int i = 0; i < values.Count; i++)
74+
{
75+
Assert.AreEqual(values[i].ToString(), info.EnumValues[i]);
76+
}
77+
}
78+
79+
[Test]
80+
public void ShouldReturnFalseForClass()
81+
{
82+
var info = EnumCache.GetInfo<TestClassThusNotEnum>();
83+
84+
Assert.IsFalse(info.IsEnum);
85+
Assert.IsFalse(info.StoreAsText);
86+
Assert.IsNull(info.EnumValues);
87+
}
88+
}
89+
}

tests/EnumTest.cs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Text;
6+
7+
#if NETFX_CORE
8+
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
9+
using SetUp = Microsoft.VisualStudio.TestPlatform.UnitTestFramework.TestInitializeAttribute;
10+
using TestFixture = Microsoft.VisualStudio.TestPlatform.UnitTestFramework.TestClassAttribute;
11+
using Test = Microsoft.VisualStudio.TestPlatform.UnitTestFramework.TestMethodAttribute;
12+
#else
13+
using NUnit.Framework;
14+
#endif
15+
16+
namespace SQLite.Tests
17+
{
18+
[TestFixture]
19+
public class EnumTests
20+
{
21+
public enum TestEnum
22+
{
23+
Value1,
24+
25+
Value2,
26+
27+
Value3
28+
}
29+
30+
public class TestObj
31+
{
32+
[PrimaryKey]
33+
public int Id { get; set; }
34+
public TestEnum Value { get; set; }
35+
36+
public override string ToString()
37+
{
38+
return string.Format("[TestObj: Id={0}, Value={1}]", Id, Value);
39+
}
40+
41+
}
42+
43+
public class TestDb : SQLiteConnection
44+
{
45+
public TestDb(String path)
46+
: base(path)
47+
{
48+
CreateTable<TestObj>();
49+
}
50+
}
51+
52+
[Test]
53+
public void ShouldPersistAndReadEnum()
54+
{
55+
var db = new TestDb(TestPath.GetTempFileName());
56+
57+
var obj1 = new TestObj() { Id = 1, Value = TestEnum.Value2 };
58+
var obj2 = new TestObj() { Id = 2, Value = TestEnum.Value3 };
59+
60+
var numIn1 = db.Insert(obj1);
61+
var numIn2 = db.Insert(obj2);
62+
Assert.AreEqual(1, numIn1);
63+
Assert.AreEqual(1, numIn2);
64+
65+
var result = db.Query<TestObj>("select * from TestObj").ToList();
66+
Assert.AreEqual(2, result.Count);
67+
Assert.AreEqual(obj1.Value, result[0].Value);
68+
Assert.AreEqual(obj2.Value, result[1].Value);
69+
70+
Assert.AreEqual(obj1.Id, result[0].Id);
71+
Assert.AreEqual(obj2.Id, result[1].Id);
72+
73+
db.Close();
74+
}
75+
}
76+
}

tests/SQLite.Tests.csproj

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,21 @@
3030
<ConsolePause>False</ConsolePause>
3131
</PropertyGroup>
3232
<ItemGroup>
33+
<Reference Include="nunit.framework, Version=2.6.4.14350, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
34+
<HintPath>..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath>
35+
<Private>True</Private>
36+
</Reference>
3337
<Reference Include="System" />
3438
<Reference Include="System.Core" />
3539
<Reference Include="Microsoft.VisualBasic" />
36-
<Reference Include="nunit.framework" />
3740
</ItemGroup>
3841
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
3942
<ItemGroup>
4043
<Compile Include="BooleanTest.cs" />
4144
<Compile Include="..\src\SQLite.cs" />
4245
<Compile Include="ByteArrayTest.cs" />
46+
<Compile Include="EnumCacheTest.cs" />
47+
<Compile Include="EnumTest.cs" />
4348
<Compile Include="ExceptionAssert.cs" />
4449
<Compile Include="InsertTest.cs" />
4550
<Compile Include="CreateTableTest.cs" />
@@ -74,4 +79,7 @@
7479
<Compile Include="TableChangedTest.cs" />
7580
<Compile Include="IgnoreTest.cs" />
7681
</ItemGroup>
82+
<ItemGroup>
83+
<None Include="packages.config" />
84+
</ItemGroup>
7785
</Project>

tests/packages.config

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<packages>
3+
<package id="NUnit" version="2.6.4" targetFramework="net45" />
4+
</packages>

0 commit comments

Comments
 (0)