diff --git a/nuget/SQLite-net-base/SQLite-net-base.csproj b/nuget/SQLite-net-base/SQLite-net-base.csproj index 26074ed9..fd1602fd 100644 --- a/nuget/SQLite-net-base/SQLite-net-base.csproj +++ b/nuget/SQLite-net-base/SQLite-net-base.csproj @@ -5,6 +5,7 @@ SQLite-net 1.0.0 SQLite-net Official .NET Standard Base Library + 1.1.0.0 Light weight library providing easy SQLite database storage Krueger Systems, Inc. diff --git a/nuget/SQLite-net-std/SQLite-net-std.csproj b/nuget/SQLite-net-std/SQLite-net-std.csproj index 1e2f21a6..f7f36179 100644 --- a/nuget/SQLite-net-std/SQLite-net-std.csproj +++ b/nuget/SQLite-net-std/SQLite-net-std.csproj @@ -5,6 +5,7 @@ SQLite-net 1.0.0 SQLite-net Official .NET Standard Library + 1.1.0.0 Light weight library providing easy SQLite database storage Krueger Systems, Inc. diff --git a/src/SQLite.cs b/src/SQLite.cs index c276066f..ed9c4b5c 100644 --- a/src/SQLite.cs +++ b/src/SQLite.cs @@ -207,6 +207,11 @@ public partial class SQLiteConnection : IDisposable /// public bool StoreDateTimeAsTicks { get; private set; } + /// + /// Whether to store Guid properties as strings (false) or blobs (true). The default is (false) + /// + public bool StoreGuidsAsBlobs { get; private set; } + #if USE_SQLITEPCL_RAW && !NO_SQLITEPCL_RAW_BATTERIES static SQLiteConnection () { @@ -228,8 +233,9 @@ static SQLiteConnection () /// If you use DateTimeOffset properties, it will be always stored as ticks regardingless /// the storeDateTimeAsTicks parameter. /// - public SQLiteConnection (string databasePath, bool storeDateTimeAsTicks = true) - : this (databasePath, SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create, storeDateTimeAsTicks) + /// + public SQLiteConnection (string databasePath, bool storeDateTimeAsTicks = true, bool storeGuidsAsBlobs = false) + : this (databasePath, SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create, storeDateTimeAsTicks, storeGuidsAsBlobs) { } @@ -250,7 +256,8 @@ public SQLiteConnection (string databasePath, bool storeDateTimeAsTicks = true) /// If you use DateTimeOffset properties, it will be always stored as ticks regardingless /// the storeDateTimeAsTicks parameter. /// - public SQLiteConnection (string databasePath, SQLiteOpenFlags openFlags, bool storeDateTimeAsTicks = true) + /// + public SQLiteConnection (string databasePath, SQLiteOpenFlags openFlags, bool storeDateTimeAsTicks = true, bool storeGuidsAsBlobs = false) { if (databasePath==null) throw new ArgumentException ("Must be specified", nameof(databasePath)); @@ -282,6 +289,7 @@ public SQLiteConnection (string databasePath, SQLiteOpenFlags openFlags, bool st _open = true; StoreDateTimeAsTicks = storeDateTimeAsTicks; + StoreGuidsAsBlobs = storeGuidsAsBlobs; BusyTimeout = TimeSpan.FromSeconds (0.1); if (openFlags.HasFlag (SQLiteOpenFlags.ReadWrite)) { @@ -511,7 +519,7 @@ public CreateTableResult CreateTable (Type ty, CreateFlags createFlags = CreateF // Build query. var query = "create " + @virtual + "table if not exists \"" + map.TableName + "\" " + @using + "(\n"; - var decls = map.Columns.Select (p => Orm.SqlDecl (p, StoreDateTimeAsTicks)); + var decls = map.Columns.Select (p => Orm.SqlDecl (p, StoreDateTimeAsTicks, StoreGuidsAsBlobs)); var decl = string.Join (",\n", decls.ToArray ()); query += decl; query += ")"; @@ -777,7 +785,7 @@ void MigrateTable (TableMapping map, List existingCols) } foreach (var p in toBeAdded) { - var addCol = "alter table \"" + map.TableName + "\" add column " + Orm.SqlDecl (p, StoreDateTimeAsTicks); + var addCol = "alter table \"" + map.TableName + "\" add column " + Orm.SqlDecl (p, StoreDateTimeAsTicks, StoreGuidsAsBlobs); Execute (addCol); } } @@ -2021,6 +2029,7 @@ public class SQLiteConnectionString public string ConnectionString { get; private set; } public string DatabasePath { get; private set; } public bool StoreDateTimeAsTicks { get; private set; } + public bool StoreGuidsAsBlobs { get; private set; } #if NETFX_CORE static readonly string MetroStyleDataPath = Windows.Storage.ApplicationData.Current.LocalFolder.Path; @@ -2038,10 +2047,11 @@ public static bool IsInMemoryPath(string databasePath) #endif - public SQLiteConnectionString (string databasePath, bool storeDateTimeAsTicks) + public SQLiteConnectionString (string databasePath, bool storeDateTimeAsTicks, bool storeGuidsAsBlobs) { ConnectionString = databasePath; StoreDateTimeAsTicks = storeDateTimeAsTicks; + StoreGuidsAsBlobs = storeGuidsAsBlobs; #if NETFX_CORE DatabasePath = IsInMemoryPath(databasePath) @@ -2432,9 +2442,9 @@ public static Type GetType (object obj) return obj.GetType (); } - public static string SqlDecl (TableMapping.Column p, bool storeDateTimeAsTicks) + public static string SqlDecl (TableMapping.Column p, bool storeDateTimeAsTicks, bool storeGuidsAsBlobs) { - string decl = "\"" + p.Name + "\" " + SqlType (p, storeDateTimeAsTicks) + " "; + string decl = "\"" + p.Name + "\" " + SqlType (p, storeDateTimeAsTicks, storeGuidsAsBlobs) + " "; if (p.IsPK) { decl += "primary key "; @@ -2452,7 +2462,7 @@ public static string SqlDecl (TableMapping.Column p, bool storeDateTimeAsTicks) return decl; } - public static string SqlType (TableMapping.Column p, bool storeDateTimeAsTicks) + public static string SqlType (TableMapping.Column p, bool storeDateTimeAsTicks, bool storeGuidsAsBlobs) { var clrType = p.ColumnType; if (clrType == typeof (Boolean) || clrType == typeof (Byte) || clrType == typeof (UInt16) || clrType == typeof (SByte) || clrType == typeof (Int16) || clrType == typeof (Int32) || clrType == typeof (UInt32) || clrType == typeof (Int64)) { @@ -2488,6 +2498,9 @@ public static string SqlType (TableMapping.Column p, bool storeDateTimeAsTicks) return "blob"; } else if (clrType == typeof (Guid)) { + if (storeGuidsAsBlobs) + return "blob"; + return "varchar(36)"; } else { @@ -2758,7 +2771,7 @@ void BindAll (Sqlite3Statement stmt) b.Index = nextIdx++; } - BindParameter (stmt, b.Index, b.Value, _conn.StoreDateTimeAsTicks); + BindParameter (stmt, b.Index, b.Value, _conn.StoreDateTimeAsTicks, _conn.StoreGuidsAsBlobs); } } @@ -2766,7 +2779,9 @@ void BindAll (Sqlite3Statement stmt) const string DateTimeExactStoreFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff"; - internal static void BindParameter (Sqlite3Statement stmt, int index, object value, bool storeDateTimeAsTicks) + internal static void BindParameter (Sqlite3Statement stmt, int index, object value, + bool storeDateTimeAsTicks, + bool connStoreGuidsAsBlobs) { if (value == null) { SQLite3.BindNull (stmt, index); @@ -2808,7 +2823,13 @@ internal static void BindParameter (Sqlite3Statement stmt, int index, object val SQLite3.BindBlob (stmt, index, (byte[])value, ((byte[])value).Length, NegativePointer); } else if (value is Guid) { - SQLite3.BindText (stmt, index, ((Guid)value).ToString (), 72, NegativePointer); + if (connStoreGuidsAsBlobs) { + var rawGuid = ((Guid)value).ToByteArray (); + SQLite3.BindBlob (stmt, index, rawGuid, rawGuid.Length, NegativePointer); + } + else { + SQLite3.BindText (stmt, index, ((Guid)value).ToString (), 72, NegativePointer); + } } else if (value is Uri) { SQLite3.BindText (stmt, index, ((Uri)value).ToString (), -1, NegativePointer); @@ -2920,6 +2941,10 @@ object ReadCol (Sqlite3Statement stmt, int index, SQLite3.ColType type, Type clr return SQLite3.ColumnByteArray (stmt, index); } else if (clrType == typeof (Guid)) { + if (_conn.StoreGuidsAsBlobs) { + var array = SQLite3.ColumnByteArray (stmt, index); + return new Guid (array); + } var text = SQLite3.ColumnString (stmt, index); return new Guid (text); } @@ -2982,7 +3007,7 @@ public int ExecuteNonQuery (object[] source) //bind the values. if (source != null) { for (int i = 0; i < source.Length; i++) { - SQLiteCommand.BindParameter (Statement, i + 1, source[i], Connection.StoreDateTimeAsTicks); + SQLiteCommand.BindParameter (Statement, i + 1, source[i], Connection.StoreDateTimeAsTicks, Connection.StoreGuidsAsBlobs); } } r = SQLite3.Step (Statement); diff --git a/src/SQLiteAsync.cs b/src/SQLiteAsync.cs index 2823748a..2132c488 100644 --- a/src/SQLiteAsync.cs +++ b/src/SQLiteAsync.cs @@ -55,7 +55,27 @@ public partial class SQLiteAsyncConnection /// the storeDateTimeAsTicks parameter. /// public SQLiteAsyncConnection (string databasePath, bool storeDateTimeAsTicks = true) - : this (databasePath, SQLiteOpenFlags.FullMutex | SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create, storeDateTimeAsTicks) + : this (databasePath, storeDateTimeAsTicks, true) + { + } + + /// + /// Constructs a new SQLiteAsyncConnection and opens a pooled SQLite database specified by databasePath. + /// + /// + /// Specifies the path to the database file. + /// + /// + /// Specifies whether to store DateTime properties as ticks (true) or strings (false). You + /// absolutely do want to store them as Ticks in all new projects. The value of false is + /// only here for backwards compatibility. There is a *significant* speed advantage, with no + /// down sides, when setting storeDateTimeAsTicks = true. + /// If you use DateTimeOffset properties, it will be always stored as ticks regardingless + /// the storeDateTimeAsTicks parameter. + /// + /// + public SQLiteAsyncConnection (string databasePath, bool storeDateTimeAsTicks = true, bool storeGuidsAsBlobs = false) + : this (databasePath, SQLiteOpenFlags.FullMutex | SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create, storeDateTimeAsTicks, storeGuidsAsBlobs) { } @@ -76,11 +96,12 @@ public SQLiteAsyncConnection (string databasePath, bool storeDateTimeAsTicks = t /// If you use DateTimeOffset properties, it will be always stored as ticks regardingless /// the storeDateTimeAsTicks parameter. /// - public SQLiteAsyncConnection (string databasePath, SQLiteOpenFlags openFlags, bool storeDateTimeAsTicks = true) + /// + public SQLiteAsyncConnection (string databasePath, SQLiteOpenFlags openFlags, bool storeDateTimeAsTicks = true, bool storeGuidsAsBlobs = false) { _openFlags = openFlags; isFullMutex = _openFlags.HasFlag (SQLiteOpenFlags.FullMutex); - _connectionString = new SQLiteConnectionString (databasePath, storeDateTimeAsTicks); + _connectionString = new SQLiteConnectionString (databasePath, storeDateTimeAsTicks, storeGuidsAsBlobs); if(isFullMutex) _fullMutexReadConnection = new SQLiteConnectionWithLock (_connectionString, openFlags) { SkipLock = true }; } @@ -1377,7 +1398,8 @@ public void CloseConnection (SQLiteConnectionString connectionString, SQLiteOpen } } - entry.Close (); + if(entry != null) + entry.Close (); } /// @@ -1411,7 +1433,7 @@ public class SQLiteConnectionWithLock : SQLiteConnection /// Connection string containing the DatabasePath. /// Open flags. public SQLiteConnectionWithLock (SQLiteConnectionString connectionString, SQLiteOpenFlags openFlags) - : base (connectionString.DatabasePath, openFlags, connectionString.StoreDateTimeAsTicks) + : base (connectionString.DatabasePath, openFlags, connectionString.StoreDateTimeAsTicks, connectionString.StoreGuidsAsBlobs) { } diff --git a/tests/AsyncTests.cs b/tests/AsyncTests.cs index c76b231f..4cb1ef91 100644 --- a/tests/AsyncTests.cs +++ b/tests/AsyncTests.cs @@ -153,6 +153,8 @@ public void SetUp() #else _connectionString = Path.Combine (Path.GetTempPath (), DatabaseName); _path = _connectionString; + GC.Collect (); + GC.WaitForPendingFinalizers (); System.IO.File.Delete (_path); #endif } @@ -797,8 +799,7 @@ public void TestAsyncTableElementAtAsync () // check... Assert.AreEqual ("7", loaded.FirstName); } - - + [Test] public void TestAsyncGetWithExpression() { @@ -857,7 +858,5 @@ public void CloseAsync () conn.CloseAsync ().Wait (); } - - } } diff --git a/tests/GuidAsBlobsTests.cs b/tests/GuidAsBlobsTests.cs new file mode 100644 index 00000000..4c7ed55c --- /dev/null +++ b/tests/GuidAsBlobsTests.cs @@ -0,0 +1,195 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +#if NETFX_CORE +using Microsoft.VisualStudio.TestPlatform.UnitTestFramework; +using SetUp = Microsoft.VisualStudio.TestPlatform.UnitTestFramework.TestInitializeAttribute; +using TestFixture = Microsoft.VisualStudio.TestPlatform.UnitTestFramework.TestClassAttribute; +using Test = Microsoft.VisualStudio.TestPlatform.UnitTestFramework.TestMethodAttribute; +#else +using NUnit.Framework; +#endif + + +namespace SQLite.Tests { + [TestFixture] + public class GuidAsBlobsTests { + public class TestObj { + [PrimaryKey] + public Guid Id { get; set; } + public String Text { get; set; } + + public override string ToString() { + return string.Format("[TestObj: Id={0}, Text={1}]", Id, Text); + } + + } + + public class TestDb : SQLiteConnection + { + public TestDb (String path) + : base (path, storeGuidsAsBlobs: true) { + CreateTable(); + } + } + + [Test] + public void ShouldPersistAndReadGuid() { + var db = new TestDb(TestPath.GetTempFileName()); + + var obj1 = new TestObj() { Id=new Guid("36473164-C9E4-4CDF-B266-A0B287C85623"), Text = "First Guid Object" }; + var obj2 = new TestObj() { Id=new Guid("BC5C4C4A-CA57-4B61-8B53-9FD4673528B6"), Text = "Second Guid Object" }; + + var numIn1 = db.Insert(obj1); + var numIn2 = db.Insert(obj2); + Assert.AreEqual(1, numIn1); + Assert.AreEqual(1, numIn2); + + var result = db.Query("select * from TestObj").ToList(); + Assert.AreEqual(2, result.Count); + Assert.AreEqual(obj1.Text, result[0].Text); + Assert.AreEqual(obj2.Text, result[1].Text); + + Assert.AreEqual(obj1.Id, result[0].Id); + Assert.AreEqual(obj2.Id, result[1].Id); + + db.Close(); + } + + [Test] + public void ShouldQueryGuidCorrectly () { + var db = new TestDb (TestPath.GetTempFileName ()); + + var obj1 = new TestObj () { Id = new Guid ("36473164-C9E4-4CDF-B266-A0B287C85623"), Text = "First Guid Object" }; + var obj2 = new TestObj () { Id = new Guid ("BC5C4C4A-CA57-4B61-8B53-9FD4673528B6"), Text = "Second Guid Object" }; + + var numIn1 = db.Insert (obj1); + var numIn2 = db.Insert (obj2); + Assert.AreEqual (1, numIn1); + Assert.AreEqual (1, numIn2); + + var result = db.Query ("select * from TestObj where id = ?", obj2.Id).ToList (); + Assert.AreEqual (1, result.Count); + Assert.AreEqual (obj2.Text, result[0].Text); + + Assert.AreEqual (obj2.Id, result[0].Id); + + db.Close (); + } + + [Test] + public void ShouldQueryGuidCorrectlyUsingLinq () { + var db = new TestDb (TestPath.GetTempFileName ()); + + var obj1 = new TestObj () { Id = new Guid ("36473164-C9E4-4CDF-B266-A0B287C85623"), Text = "First Guid Object" }; + var obj2 = new TestObj () { Id = new Guid ("BC5C4C4A-CA57-4B61-8B53-9FD4673528B6"), Text = "Second Guid Object" }; + + var numIn1 = db.Insert (obj1); + var numIn2 = db.Insert (obj2); + Assert.AreEqual (1, numIn1); + Assert.AreEqual (1, numIn2); + + var result = db.Table ().Where (to => to.Id == obj2.Id).ToList (); + Assert.AreEqual (1, result.Count); + Assert.AreEqual (obj2.Text, result[0].Text); + + Assert.AreEqual (obj2.Id, result[0].Id); + + db.Close (); + } + + + [Test] + public async Task ShouldQueryGuidCorrectlyAsync () { + + var db = new SQLiteAsyncConnection (TestPath.GetTempFileName (), storeGuidsAsBlobs: true); + await db.CreateTableAsync (CreateFlags.AutoIncPK); + + var obj1 = new TestObj () { Id = new Guid ("36473164-C9E4-4CDF-B266-A0B287C85623"), Text = "First Guid Object" }; + var obj2 = new TestObj () { Id = new Guid ("BC5C4C4A-CA57-4B61-8B53-9FD4673528B6"), Text = "Second Guid Object" }; + + var numIn1 = await db.InsertAsync (obj1); + var numIn2 = await db.InsertAsync (obj2); + Assert.AreEqual (1, numIn1); + Assert.AreEqual (1, numIn2); + + var result = (await db.QueryAsync ("select * from TestObj where id = ?", obj2.Id)).ToList (); + Assert.AreEqual (1, result.Count); + Assert.AreEqual (obj2.Text, result[0].Text); + + Assert.AreEqual (obj2.Id, result[0].Id); + + await db.CloseAsync (); + } + + [Test] + public async Task ShouldQueryGuidCorrectlyUsingLinqAsync () { + + var db = new SQLiteAsyncConnection (TestPath.GetTempFileName (), storeGuidsAsBlobs: true); + await db.CreateTableAsync (CreateFlags.AutoIncPK); + + var obj1 = new TestObj () { Id = new Guid ("36473164-C9E4-4CDF-B266-A0B287C85623"), Text = "First Guid Object" }; + var obj2 = new TestObj () { Id = new Guid ("BC5C4C4A-CA57-4B61-8B53-9FD4673528B6"), Text = "Second Guid Object" }; + + var numIn1 = await db.InsertAsync (obj1); + var numIn2 = await db.InsertAsync (obj2); + Assert.AreEqual (1, numIn1); + Assert.AreEqual (1, numIn2); + + var result = await db.Table().Where (to => to.Id == obj2.Id).ToListAsync (); + Assert.AreEqual (1, result.Count); + Assert.AreEqual (obj2.Text, result[0].Text); + + Assert.AreEqual (obj2.Id, result[0].Id); + + await db.CloseAsync (); + } + + [Test] + public void AutoGuid_HasGuid() + { + var db = new SQLiteConnection(TestPath.GetTempFileName(), storeGuidsAsBlobs: true); + db.CreateTable(CreateFlags.AutoIncPK); + + var guid1 = new Guid("36473164-C9E4-4CDF-B266-A0B287C85623"); + var guid2 = new Guid("BC5C4C4A-CA57-4B61-8B53-9FD4673528B6"); + + var obj1 = new TestObj() { Id = guid1, Text = "First Guid Object" }; + var obj2 = new TestObj() { Id = guid2, Text = "Second Guid Object" }; + + var numIn1 = db.Insert(obj1); + var numIn2 = db.Insert(obj2); + Assert.AreEqual(guid1, obj1.Id); + Assert.AreEqual(guid2, obj2.Id); + + db.Close(); + } + + [Test] + public void AutoGuid_EmptyGuid() + { + var db = new SQLiteConnection(TestPath.GetTempFileName(), storeGuidsAsBlobs: true); + db.CreateTable(CreateFlags.AutoIncPK); + + var guid1 = new Guid("36473164-C9E4-4CDF-B266-A0B287C85623"); + var guid2 = new Guid("BC5C4C4A-CA57-4B61-8B53-9FD4673528B6"); + + var obj1 = new TestObj() { Text = "First Guid Object" }; + var obj2 = new TestObj() { Text = "Second Guid Object" }; + + Assert.AreEqual(Guid.Empty, obj1.Id); + Assert.AreEqual(Guid.Empty, obj2.Id); + + var numIn1 = db.Insert(obj1); + var numIn2 = db.Insert(obj2); + Assert.AreNotEqual(Guid.Empty, obj1.Id); + Assert.AreNotEqual(Guid.Empty, obj2.Id); + Assert.AreNotEqual(obj1.Id, obj2.Id); + + db.Close(); + } + } +} diff --git a/tests/SQLite.Tests.csproj b/tests/SQLite.Tests.csproj index 674f1cbe..0dff3b13 100644 --- a/tests/SQLite.Tests.csproj +++ b/tests/SQLite.Tests.csproj @@ -10,13 +10,15 @@ SQLite.Tests SQLite.Tests v4.5 + + True full False bin\Debug - DEBUG + DEBUG;USE_SQLITEPCL_RAW prompt 4 False @@ -34,6 +36,18 @@ ..\packages\NUnit.2.6.4\lib\nunit.framework.dll True + + ..\packages\SQLitePCLRaw.bundle_green.1.1.11\lib\net45\SQLitePCLRaw.batteries_green.dll + + + ..\packages\SQLitePCLRaw.bundle_green.1.1.11\lib\net45\SQLitePCLRaw.batteries_v2.dll + + + ..\packages\SQLitePCLRaw.core.1.1.11\lib\net45\SQLitePCLRaw.core.dll + + + ..\packages\SQLitePCLRaw.provider.e_sqlite3.net45.1.1.11\lib\net45\SQLitePCLRaw.provider.e_sqlite3.dll + @@ -48,6 +62,7 @@ + @@ -83,6 +98,19 @@ - + + Designer + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + \ No newline at end of file diff --git a/tests/packages.config b/tests/packages.config index c714ef3a..ecc5b76e 100644 --- a/tests/packages.config +++ b/tests/packages.config @@ -1,4 +1,10 @@  + + + + + + \ No newline at end of file