diff --git a/Local.testsettings b/Local.testsettings new file mode 100644 index 0000000..b25fff1 --- /dev/null +++ b/Local.testsettings @@ -0,0 +1,10 @@ + + + These are default test settings for a local test run. + + + + + + + \ No newline at end of file diff --git a/OpenNETCF.ORM.FFx.sln b/OpenNETCF.ORM.FFx.sln index c5e52be..2d329a8 100644 --- a/OpenNETCF.ORM.FFx.sln +++ b/OpenNETCF.ORM.FFx.sln @@ -7,9 +7,22 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenNETCF.ORM.SqlCe.FFx", " EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EntityGenerator", "EntityGenerator\EntityGenerator.csproj", "{5BAFE144-30A1-4D9A-A267-1BEE62E45D2C}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C64CCC7B-9F5F-4D99-8154-BDF0727FE98F}" + ProjectSection(SolutionItems) = preProject + Local.testsettings = Local.testsettings + OpenNETCF.ORM.FFx.vsmdi = OpenNETCF.ORM.FFx.vsmdi + TraceAndTestImpact.testsettings = TraceAndTestImpact.testsettings + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenNETCF.ORM.SqlCE.Integration.Test", "OpenNETCF.ORM.SqlCE.Integration.Test\OpenNETCF.ORM.SqlCE.Integration.Test.csproj", "{5A05D8DE-100E-4443-9FBA-BEA87A070732}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenNETCF.ORM.SQLite.FFx", "OpenNETCF.ORM.SQLite\OpenNETCF.ORM.SQLite.FFx.csproj", "{750ED0AF-FE89-4EFC-BF7A-C29CBDD8AA7F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenNETCF.ORM.SQLite.Integration.Test", "OpenNETCF.ORM.SQLite.Integration.Test\OpenNETCF.ORM.SQLite.Integration.Test.csproj", "{97A3ADC5-EED5-4BC8-BE5B-1FD8DF86A1C3}" +EndProject Global GlobalSection(TeamFoundationVersionControl) = preSolution - SccNumberOfProjects = 4 + SccNumberOfProjects = 7 SccEnterpriseProvider = {4CA58AB2-18FA-4F8D-95D4-32DDF27D184C} SccTeamFoundationServer = https://tfs.codeplex.com/tfs/tfs01 SccLocalPath0 = . @@ -22,6 +35,18 @@ Global SccProjectUniqueName3 = EntityGenerator\\EntityGenerator.csproj SccProjectName3 = EntityGenerator SccLocalPath3 = EntityGenerator + SccProjectUniqueName4 = OpenNETCF.ORM.SqlCE.Integration.Test\\OpenNETCF.ORM.SqlCE.Integration.Test.csproj + SccProjectName4 = OpenNETCF.ORM.SqlCE.Integration.Test + SccLocalPath4 = OpenNETCF.ORM.SqlCE.Integration.Test + SccProjectUniqueName5 = OpenNETCF.ORM.SQLite\\OpenNETCF.ORM.SQLite.FFx.csproj + SccProjectName5 = OpenNETCF.ORM.SQLite + SccLocalPath5 = OpenNETCF.ORM.SQLite + SccProjectUniqueName6 = OpenNETCF.ORM.SQLite.Integration.Test\\OpenNETCF.ORM.SQLite.Integration.Test.csproj + SccProjectName6 = OpenNETCF.ORM.SQLite.Integration.Test + SccLocalPath6 = OpenNETCF.ORM.SQLite.Integration.Test + EndGlobalSection + GlobalSection(TestCaseManagementSettings) = postSolution + CategoryFile = OpenNETCF.ORM.FFx.vsmdi EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -40,6 +65,18 @@ Global {5BAFE144-30A1-4D9A-A267-1BEE62E45D2C}.Debug|Any CPU.Build.0 = Debug|Any CPU {5BAFE144-30A1-4D9A-A267-1BEE62E45D2C}.Release|Any CPU.ActiveCfg = Release|Any CPU {5BAFE144-30A1-4D9A-A267-1BEE62E45D2C}.Release|Any CPU.Build.0 = Release|Any CPU + {5A05D8DE-100E-4443-9FBA-BEA87A070732}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5A05D8DE-100E-4443-9FBA-BEA87A070732}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5A05D8DE-100E-4443-9FBA-BEA87A070732}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5A05D8DE-100E-4443-9FBA-BEA87A070732}.Release|Any CPU.Build.0 = Release|Any CPU + {750ED0AF-FE89-4EFC-BF7A-C29CBDD8AA7F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {750ED0AF-FE89-4EFC-BF7A-C29CBDD8AA7F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {750ED0AF-FE89-4EFC-BF7A-C29CBDD8AA7F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {750ED0AF-FE89-4EFC-BF7A-C29CBDD8AA7F}.Release|Any CPU.Build.0 = Release|Any CPU + {97A3ADC5-EED5-4BC8-BE5B-1FD8DF86A1C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {97A3ADC5-EED5-4BC8-BE5B-1FD8DF86A1C3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {97A3ADC5-EED5-4BC8-BE5B-1FD8DF86A1C3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {97A3ADC5-EED5-4BC8-BE5B-1FD8DF86A1C3}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/OpenNETCF.ORM.FFx.vsmdi b/OpenNETCF.ORM.FFx.vsmdi new file mode 100644 index 0000000..2441ca8 --- /dev/null +++ b/OpenNETCF.ORM.FFx.vsmdi @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/OpenNETCF.ORM.SQLite.Integration.Test/OpenNETCF.ORM.SQLite.Integration.Test.csproj b/OpenNETCF.ORM.SQLite.Integration.Test/OpenNETCF.ORM.SQLite.Integration.Test.csproj new file mode 100644 index 0000000..2799e90 --- /dev/null +++ b/OpenNETCF.ORM.SQLite.Integration.Test/OpenNETCF.ORM.SQLite.Integration.Test.csproj @@ -0,0 +1,81 @@ + + + + Debug + AnyCPU + + + 2.0 + {97A3ADC5-EED5-4BC8-BE5B-1FD8DF86A1C3} + Library + Properties + OpenNETCF.ORM.SQLite.Integration.Test + OpenNETCF.ORM.SQLite.Integration.Test + v4.0 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + SAK + SAK + SAK + SAK + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + 3.5 + + + + + False + ..\OpenNETCF.ORM.SQLite\references\FFx\x64\System.Data.SQLite.dll + + + + + + + False + + + + + + + + + {750ED0AF-FE89-4EFC-BF7A-C29CBDD8AA7F} + OpenNETCF.ORM.SQLite.FFx + + + {6E9C7DA3-AD60-4E44-9D3A-7BBD3A20D79F} + OpenNETCF.ORM.FFx + + + + + \ No newline at end of file diff --git a/OpenNETCF.ORM.SQLite.Integration.Test/OpenNETCF.ORM.SQLite.Integration.Test.csproj.vspscc b/OpenNETCF.ORM.SQLite.Integration.Test/OpenNETCF.ORM.SQLite.Integration.Test.csproj.vspscc new file mode 100644 index 0000000..feffdec --- /dev/null +++ b/OpenNETCF.ORM.SQLite.Integration.Test/OpenNETCF.ORM.SQLite.Integration.Test.csproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/OpenNETCF.ORM.SQLite.Integration.Test/Properties/AssemblyInfo.cs b/OpenNETCF.ORM.SQLite.Integration.Test/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..7eb9199 --- /dev/null +++ b/OpenNETCF.ORM.SQLite.Integration.Test/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("OpenNETCF.ORM.SQLite.Integration.Test")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("OpenNETCF.ORM.SQLite.Integration.Test")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2012")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("f7a90bd8-a393-4720-bb1d-705ca58e2f00")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/OpenNETCF.ORM.SQLite.Integration.Test/SQLiteDataStoreTest.cs b/OpenNETCF.ORM.SQLite.Integration.Test/SQLiteDataStoreTest.cs new file mode 100644 index 0000000..b729e2a --- /dev/null +++ b/OpenNETCF.ORM.SQLite.Integration.Test/SQLiteDataStoreTest.cs @@ -0,0 +1,46 @@ +using OpenNETCF.ORM.SQLite; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; + +namespace OpenNETCF.ORM.SQLite.Integration.Test +{ + [TestClass()] + public class SQLiteDataStoreTest + { + public TestContext TestContext { get; set; } + [TestMethod()] + [DeploymentItem("OpenNETCF.ORM.SqlCe.dll")] + public void SelectTest() + { + var store = new SQLiteDataStore("test.db"); + store.AddType(); + store.CreateStore(); + + store.Insert(new TestItem("ItemA")); + //store.Insert(new TestItem("ItemB")); + //store.Insert(new TestItem("ItemC")); + + //var item = store.Select("Name", "ItemB").FirstOrDefault(); + //item = store.Select(2); + } + } + + [Entity(KeyScheme=KeyScheme.Identity)] + public class TestItem + { + public TestItem() + { + } + + public TestItem(string name) + { + Name = name; + } + + [Field(IsPrimaryKey=true)] + int ID { get; set; } + + [Field] + string Name { get; set; } + } +} diff --git a/OpenNETCF.ORM.SQLite/OpenNETCF.ORM.SQLite.FFx.csproj b/OpenNETCF.ORM.SQLite/OpenNETCF.ORM.SQLite.FFx.csproj new file mode 100644 index 0000000..6ceefc6 --- /dev/null +++ b/OpenNETCF.ORM.SQLite/OpenNETCF.ORM.SQLite.FFx.csproj @@ -0,0 +1,74 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {750ED0AF-FE89-4EFC-BF7A-C29CBDD8AA7F} + Library + Properties + OpenNETCF.ORM.SQLite.FFx + OpenNETCF.ORM.SQLite.FFx + v3.5 + 512 + SAK + SAK + SAK + SAK + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + False + references\FFx\x32\System.Data.SQLite.dll + + + + + + + + + + + + + + + {6E9C7DA3-AD60-4E44-9D3A-7BBD3A20D79F} + OpenNETCF.ORM.FFx + + + + + PreserveNewest + + + + + \ No newline at end of file diff --git a/OpenNETCF.ORM.SQLite/OpenNETCF.ORM.SQLite.FFx.csproj.vspscc b/OpenNETCF.ORM.SQLite/OpenNETCF.ORM.SQLite.FFx.csproj.vspscc new file mode 100644 index 0000000..feffdec --- /dev/null +++ b/OpenNETCF.ORM.SQLite/OpenNETCF.ORM.SQLite.FFx.csproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/OpenNETCF.ORM.SQLite/SQLite.Interop.dll b/OpenNETCF.ORM.SQLite/SQLite.Interop.dll new file mode 100644 index 0000000..6446d92 Binary files /dev/null and b/OpenNETCF.ORM.SQLite/SQLite.Interop.dll differ diff --git a/OpenNETCF.ORM.SQLite/SQLiteDataStore.cs b/OpenNETCF.ORM.SQLite/SQLiteDataStore.cs index 96067d7..e7afd6c 100644 --- a/OpenNETCF.ORM.SQLite/SQLiteDataStore.cs +++ b/OpenNETCF.ORM.SQLite/SQLiteDataStore.cs @@ -1,313 +1,307 @@ using System; using System.Net; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Documents; -using System.Windows.Ink; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Animation; -using System.Windows.Shapes; -using Community.CsharpSqlite; using System.IO; using System.Diagnostics; using System.Text; using System.Linq; using System.Data; +using System.Data.SQLite; +using System.Data.Common; namespace OpenNETCF.ORM.SQLite { - public class SQLiteDataStore : DataStore, IDisposable + public class SQLiteDataStore : SQLStoreBase, IDisposable { - private Sqlite3.sqlite3 m_store; - private string m_storeName; + private string m_connectionString; - public int DefaultStringFieldSize { get; set; } - public int DefaultNumericFieldPrecision { get; set; } + public string FileName { get; protected set; } - public SQLiteDataStore(string databaseName) + protected SQLiteDataStore() + : base() { - if (string.IsNullOrEmpty(databaseName)) - { - throw new ArgumentException(); - } - - m_storeName = databaseName; - DefaultStringFieldSize = 200; - DefaultNumericFieldPrecision = 16; - - OpenStore(); - } - - public override void CreateStore() - { - foreach (var entity in this.Entities) - { - CreateTable(entity); - } +// UseCommandCache = true; } - public override void DeleteStore() + public SQLiteDataStore(string fileName) + : this() { - if (m_store != null) - { - CloseStore(); - } - - if (StoreExists) + if (string.IsNullOrEmpty(fileName)) { - File.Delete(m_storeName); + throw new ArgumentException(); } - } - - public override bool StoreExists - { - get { return File.Exists(m_storeName); } - } - - public override void Insert(object item, bool insertReferences) - { - throw new NotImplementedException(); - } - - public override T[] Select() - { - throw new NotImplementedException(); - } - - public override T Select(object primaryKey) - { - throw new NotImplementedException(); - } - - public override T[] Select(string searchFieldName, object matchValue) - { - throw new NotImplementedException(); - } - - public override void Update(object item) - { - throw new NotImplementedException(); - } - - public override void Delete(object item) - { - throw new NotImplementedException(); - } - public override void Delete(object primaryKey) - { - throw new NotImplementedException(); + FileName = fileName; } - public override void FillReferences(object instance) + private string ConnectionString { - throw new NotImplementedException(); - } - - public override T[] Fetch(int fetchCount) - { - throw new NotImplementedException(); - } + get + { + if (m_connectionString == null) + { + m_connectionString = string.Format("Data Source={0}", FileName); - public override T[] Fetch(int fetchCount, int firstRowOffset) - { - throw new NotImplementedException(); + } + return m_connectionString; + } } - public override int Count() + protected override System.Data.Common.DbCommand GetNewCommandObject() { - throw new NotImplementedException(); + return new SQLiteCommand(); } - public override void Delete() + protected override System.Data.Common.DbConnection GetNewConnectionObject() { - throw new NotImplementedException(); + return new SQLiteConnection(ConnectionString); } - public override void Delete(string fieldName, object matchValue) + protected override string AutoIncrementFieldIdentifier { - throw new NotImplementedException(); + get { return "AUTOINCREMENT"; } } - public override bool Contains(object item) - { - throw new NotImplementedException(); - } - - public void Dispose() + public override void CreateStore() { - if (m_store != null) + if (StoreExists) { - CloseStore(); + throw new StoreAlreadyExistsException(); } - } - private void OpenStore() - { - m_store = new Sqlite3.sqlite3(); + SQLiteConnection.CreateFile(FileName); - var result = Sqlite3.sqlite3_open(m_storeName, ref m_store); - - if (result != Sqlite3.SQLITE_OK) + var connection = GetConnection(true); + try + { + foreach (var entity in this.Entities) + { + CreateTable(connection, entity); + } + } + finally { - throw new SQLiteException(Sqlite3.sqlite3_errmsg(m_store)); + DoneWithConnection(connection, true); } } - private void CloseStore() + public override void DeleteStore() { - var result = Sqlite3.sqlite3_close(m_store); - if (result != Sqlite3.SQLITE_OK) + if (StoreExists) { - throw new SQLiteException(Sqlite3.sqlite3_errmsg(m_store)); + File.Delete(FileName); } - m_store = null; } - private void CreateTable(EntityInfo entity) + public override bool StoreExists { - StringBuilder sql = new StringBuilder(); - - if (ReservedWords.Contains(entity.EntityName, StringComparer.InvariantCultureIgnoreCase)) - { - throw new ReservedWordException(entity.EntityName); - } + get { return File.Exists(FileName); } + } - sql.AppendFormat("CREATE TABLE {0} (", entity.EntityName); + private SQLiteCommand GetInsertCommand(string entityName) + { + // TODO: support command caching to improve bulk insert speeds + // simply use a dictionary keyed by entityname + var keyScheme = Entities[entityName].EntityAttribute.KeyScheme; + var insertCommand = new SQLiteCommand(); - int count = entity.Fields.Count; + var sbFields = new StringBuilder(string.Format("INSERT INTO {0} (", entityName)); + var sbParams = new StringBuilder( " VALUES ("); - foreach (var field in entity.Fields) + foreach (var field in Entities[entityName].Fields) { - if (ReservedWords.Contains(field.FieldName, StringComparer.InvariantCultureIgnoreCase)) + // skip auto-increments + if ((field.IsPrimaryKey) && (keyScheme == KeyScheme.Identity)) { - throw new ReservedWordException(field.FieldName); + continue; } + sbFields.Append("[" + field.FieldName + "],"); + sbParams.Append("?,"); - sql.AppendFormat("[{0}] {1} {2}", - field.FieldName, - GetFieldDataTypeString(entity.EntityName, field), - GetFieldCreationAttributes(entity.EntityAttribute, field)); - - if (--count > 0) sql.Append(", "); + insertCommand.Parameters.Add(new SQLiteParameter(field.FieldName)); } - sql.Append(")"); + // replace trailing commas + sbFields[sbFields.Length - 1] = ')'; + sbParams[sbParams.Length - 1] = ')'; - Debug.WriteLine(sql); - string error = string.Empty; - var result = Sqlite3.sqlite3_exec(m_store, sql.ToString(), CommandCallback, null, ref error); - if (result != Sqlite3.SQLITE_OK) - { - throw new SQLiteException(Sqlite3.sqlite3_errmsg(m_store)); - } - } + insertCommand.CommandText = sbFields.ToString() + sbParams.ToString(); - private int CommandCallback(object pArg, long nArg, object azArgs, object azCols) - { - return 0; + return insertCommand; } - private string GetFieldDataTypeString(string entityName, FieldAttribute field) + /// + /// Inserts the provided entity instance into the underlying data store. + /// + /// + /// + /// If the entity has an identity field, calling Insert will populate that field with the identity vale vefore returning + /// + public override void Insert(object item, bool insertReferences) { - // the SQL RowVersion is a special case - if (field.IsRowVersion) - { - switch (field.DataType) - { - case DbType.UInt64: - case DbType.Int64: - // no error - break; - default: - throw new FieldDefinitionException(entityName, field.FieldName, "rowversion fields must be an 8-byte data type (In64 or UInt64)"); - } + var itemType = item.GetType(); + string entityName = m_entities.GetNameForType(itemType); - return "rowversion"; + if (entityName == null) + { + throw new EntityNotFoundException(item.GetType()); } - return field.DataType.ToSqlTypeString(); - } + var connection = GetConnection(false); + try + { + // CheckOrdinals(entityName); - private string GetFieldCreationAttributes(EntityAttribute attribute, FieldAttribute field) - { - StringBuilder sb = new StringBuilder(); + FieldAttribute identity = null; + var command = GetInsertCommand(entityName); + command.Connection = connection as SQLiteConnection; - switch (field.DataType) - { - case DbType.String: - if (field.Length > 0) + var keyScheme = Entities[entityName].EntityAttribute.KeyScheme; + + // TODO: fill the parameters + foreach (var field in Entities[entityName].Fields) + { + if ((field.IsPrimaryKey) && (keyScheme == KeyScheme.Identity)) + { + identity = field; + continue; + } + else if (field.DataType == DbType.Object) { - sb.AppendFormat("({0}) ", field.Length); + // get serializer + var serializer = GetSerializer(itemType); + + if (serializer == null) + { + throw new MissingMethodException( + string.Format("The field '{0}' requires a custom serializer/deserializer method pair in the '{1}' Entity", + field.FieldName, entityName)); + } + var value = serializer.Invoke(item, new object[] { field.FieldName }); + if (value == null) + { + command.Parameters[field.FieldName].Value = DBNull.Value; + } + else + { + command.Parameters[field.FieldName].Value = value; + } + } + else if (field.IsRowVersion) + { + // read-only, so do nothing + } + else if (field.PropertyInfo.PropertyType.UnderlyingTypeIs()) + { + // SQL Compact doesn't support Time, so we're convert to a DateTime both directions + var value = field.PropertyInfo.GetValue(item, null); + + if (value == null) + { + command.Parameters[field.FieldName].Value = DBNull.Value; + } + else + { + var timespanTicks = ((TimeSpan)value).Ticks; + command.Parameters[field.FieldName].Value = timespanTicks; + } } else { - sb.AppendFormat("({0}) ", DefaultStringFieldSize); + var value = field.PropertyInfo.GetValue(item, null); + command.Parameters[field.FieldName].Value = value; } - break; - case DbType.Decimal: - int p = field.Precision == 0 ? DefaultNumericFieldPrecision : field.Precision; - sb.AppendFormat("({0},{1}) ", p, field.Scale); - break; - } + } - if (field.IsPrimaryKey) - { - sb.Append("PRIMARY KEY "); + command.ExecuteNonQuery(); - if (attribute.KeyScheme == KeyScheme.Identity) + // did we have an identity field? If so, we need to update that value in the item + if (identity != null) { - switch (field.DataType) + var id = GetIdentity(connection); + identity.PropertyInfo.SetValue(item, id, null); + } + + if (insertReferences) + { + // cascade insert any References + // do this last because we need the PK from above + foreach (var reference in Entities[entityName].References) { - case DbType.Int32: - case DbType.UInt32: - sb.Append("IDENTITY "); - break; - case DbType.Guid: - sb.Append("ROWGUIDCOL "); - break; - default: - throw new FieldDefinitionException(attribute.NameInStore, field.FieldName, - string.Format("Data Type '{0}' cannot be marked as an Identity field", field.DataType)); + var valueArray = reference.PropertyInfo.GetValue(item, null); + if (valueArray == null) continue; + + var fk = Entities[entityName].Fields[reference.ReferenceField].PropertyInfo.GetValue(item, null); + + string et = null; + + // we've already enforced this to be an array when creating the store + foreach (var element in valueArray as Array) + { + if (et == null) + { + et = m_entities.GetNameForType(element.GetType()); + } + + // get the FK value + var keyValue = Entities[et].Fields.KeyField.PropertyInfo.GetValue(element, null); + + bool isNew = false; + + + // only do an insert if the value is new (i.e. need to look for existing reference items) + // not certain how this will work right now, so for now we ask the caller to know what they're doing + switch (keyScheme) + { + case KeyScheme.Identity: + // TODO: see if PK field value == -1 + isNew = keyValue.Equals(-1); + break; + case KeyScheme.GUID: + // TODO: see if PK field value == null + isNew = keyValue.Equals(null); + break; + } + + if (isNew) + { + Entities[et].Fields[reference.ReferenceField].PropertyInfo.SetValue(element, fk, null); + Insert(element); + } + } } } } - - if (!field.AllowsNulls) + finally { - sb.Append("NOT NULL "); + DoneWithConnection(connection, false); } + } - if (field.RequireUniqueValue) + private int GetIdentity(DbConnection connection) + { + using (var command = new SQLiteCommand("SELECT last_insert_rowid()", connection as SQLiteConnection)) { - sb.Append("UNIQUE "); + object id = command.ExecuteScalar(); + return Convert.ToInt32(id); } - - return sb.ToString(); } - static string[] ReservedWords = new string[] - { - // TODO: add SQLite reserved words here - }; - public override void EnsureCompatibility() { throw new NotImplementedException(); } - public override T[] Select(System.Collections.Generic.IEnumerable filters) + public override T[] Select() { throw new NotImplementedException(); } - public override object[] Select(Type entityType) + public override T[] Select(bool fillReferences) { throw new NotImplementedException(); } - public override T[] Select(bool fillReferences) + public override T Select(object primaryKey) { throw new NotImplementedException(); } @@ -317,21 +311,41 @@ public override T Select(object primaryKey, bool fillReferences) throw new NotImplementedException(); } + public override T[] Select(string searchFieldName, object matchValue) + { + throw new NotImplementedException(); + } + public override T[] Select(string searchFieldName, object matchValue, bool fillReferences) { throw new NotImplementedException(); } + public override T[] Select(System.Collections.Generic.IEnumerable filters) + { + throw new NotImplementedException(); + } + public override T[] Select(System.Collections.Generic.IEnumerable filters, bool fillReferences) { throw new NotImplementedException(); } + public override object[] Select(Type entityType) + { + throw new NotImplementedException(); + } + public override object[] Select(Type entityType, bool fillReferences) { throw new NotImplementedException(); } + public override void Update(object item) + { + throw new NotImplementedException(); + } + public override void Update(object item, bool cascadeUpdates, string fieldName) { throw new NotImplementedException(); @@ -342,6 +356,31 @@ public override void Update(object item, string fieldName) throw new NotImplementedException(); } + public override void Delete(object item) + { + throw new NotImplementedException(); + } + + public override void Delete(object primaryKey) + { + throw new NotImplementedException(); + } + + public override void FillReferences(object instance) + { + throw new NotImplementedException(); + } + + public override T[] Fetch(int fetchCount) + { + throw new NotImplementedException(); + } + + public override T[] Fetch(int fetchCount, int firstRowOffset) + { + throw new NotImplementedException(); + } + public override T[] Fetch(int fetchCount, int firstRowOffset, string sortField) { throw new NotImplementedException(); @@ -352,9 +391,30 @@ public override T[] Fetch(int fetchCount, int firstRowOffset, string sortFiel throw new NotImplementedException(); } + public override int Count() + { + throw new NotImplementedException(); + } + public override int Count(System.Collections.Generic.IEnumerable filters) { throw new NotImplementedException(); } + + public override void Delete() + { + throw new NotImplementedException(); + } + + public override void Delete(string fieldName, object matchValue) + { + throw new NotImplementedException(); + } + + public override bool Contains(object item) + { + throw new NotImplementedException(); + } + } } diff --git a/OpenNETCF.ORM.SQLite/SQLiteEntityInfo.cs b/OpenNETCF.ORM.SQLite/SQLiteEntityInfo.cs index 9b71ed6..1a2398d 100644 --- a/OpenNETCF.ORM.SQLite/SQLiteEntityInfo.cs +++ b/OpenNETCF.ORM.SQLite/SQLiteEntityInfo.cs @@ -1,13 +1,5 @@ using System; using System.Net; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Documents; -using System.Windows.Ink; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Animation; -using System.Windows.Shapes; namespace OpenNETCF.ORM { diff --git a/OpenNETCF.ORM.SQLite/SQLiteException.cs b/OpenNETCF.ORM.SQLite/SQLiteException.cs index 35a668b..a9da66e 100644 --- a/OpenNETCF.ORM.SQLite/SQLiteException.cs +++ b/OpenNETCF.ORM.SQLite/SQLiteException.cs @@ -1,13 +1,5 @@ using System; using System.Net; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Documents; -using System.Windows.Ink; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Animation; -using System.Windows.Shapes; namespace OpenNETCF.ORM.SQLite { diff --git a/OpenNETCF.ORM.SqlCE.Integration.Test/OpenNETCF.ORM.SqlCE.Integration.Test.csproj b/OpenNETCF.ORM.SqlCE.Integration.Test/OpenNETCF.ORM.SqlCE.Integration.Test.csproj new file mode 100644 index 0000000..dbd4164 --- /dev/null +++ b/OpenNETCF.ORM.SqlCE.Integration.Test/OpenNETCF.ORM.SqlCE.Integration.Test.csproj @@ -0,0 +1,81 @@ + + + + Debug + AnyCPU + + + 2.0 + {5A05D8DE-100E-4443-9FBA-BEA87A070732} + Library + Properties + OpenNETCF.ORM.SqlCE.Integration.Test + OpenNETCF.ORM.SqlCE.Integration.Test + v4.0 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + SAK + SAK + SAK + SAK + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + 3.5 + + + + + + + + + + False + + + + + + + + + + + + {B63F2DF8-E247-402D-A250-2514EF4A08FD} + OpenNETCF.ORM.SqlCe.FFx + + + {6E9C7DA3-AD60-4E44-9D3A-7BBD3A20D79F} + OpenNETCF.ORM.FFx + + + + + \ No newline at end of file diff --git a/OpenNETCF.ORM.SqlCE.Integration.Test/OpenNETCF.ORM.SqlCE.Integration.Test.csproj.vspscc b/OpenNETCF.ORM.SqlCE.Integration.Test/OpenNETCF.ORM.SqlCE.Integration.Test.csproj.vspscc new file mode 100644 index 0000000..feffdec --- /dev/null +++ b/OpenNETCF.ORM.SqlCE.Integration.Test/OpenNETCF.ORM.SqlCE.Integration.Test.csproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/OpenNETCF.ORM.SqlCE.Integration.Test/Properties/AssemblyInfo.cs b/OpenNETCF.ORM.SqlCE.Integration.Test/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..b0b0162 --- /dev/null +++ b/OpenNETCF.ORM.SqlCE.Integration.Test/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("OpenNETCF.ORM.SqlCE.Integration.Test")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("OpenNETCF.ORM.SqlCE.Integration.Test")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2012")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("2d9ff38c-8a55-4640-94db-5b1fc7f975ef")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/OpenNETCF.ORM.SqlCE.Integration.Test/SqlCeDataStoreTest.cs b/OpenNETCF.ORM.SqlCE.Integration.Test/SqlCeDataStoreTest.cs new file mode 100644 index 0000000..00cefcb --- /dev/null +++ b/OpenNETCF.ORM.SqlCE.Integration.Test/SqlCeDataStoreTest.cs @@ -0,0 +1,46 @@ +using OpenNETCF.ORM; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Linq; + +namespace OpenNETCF.ORM.SqlCE.Integration.Test +{ + [TestClass()] + public class SqlCeDataStoreTest + { + public TestContext TestContext { get; set; } + [TestMethod()] + [DeploymentItem("OpenNETCF.ORM.SqlCe.dll")] + public void SelectTest() + { + var store = new SqlCeDataStore("test.sdf"); + store.AddType(); + store.CreateStore(); + + store.Insert(new TestItem("ItemA")); + store.Insert(new TestItem("ItemB")); + store.Insert(new TestItem("ItemC")); + + var item = store.Select("Name", "ItemB").FirstOrDefault(); + item = store.Select(2); + } + } + + [Entity(KeyScheme=KeyScheme.Identity)] + public class TestItem + { + public TestItem() + { + } + + public TestItem(string name) + { + Name = name; + } + + [Field(IsPrimaryKey=true)] + int ID { get; set; } + + [Field] + string Name { get; set; } + } +} diff --git a/OpenNETCF.ORM.SqlCE.Integration.Test/Test References/OpenNETCF.ORM.SqlCe.accessor b/OpenNETCF.ORM.SqlCE.Integration.Test/Test References/OpenNETCF.ORM.SqlCe.accessor new file mode 100644 index 0000000..b17b8f3 --- /dev/null +++ b/OpenNETCF.ORM.SqlCE.Integration.Test/Test References/OpenNETCF.ORM.SqlCe.accessor @@ -0,0 +1,2 @@ +OpenNETCF.ORM.SqlCe.dll +Desktop diff --git a/OpenNETCF.ORM.SqlCe/SqlCeDataStore.cs b/OpenNETCF.ORM.SqlCe/SqlCeDataStore.cs index ebded44..b5fdba3 100644 --- a/OpenNETCF.ORM.SqlCe/SqlCeDataStore.cs +++ b/OpenNETCF.ORM.SqlCe/SqlCeDataStore.cs @@ -19,14 +19,13 @@ public partial class SqlCeDataStore : SQLStoreBase private int m_maxSize = 128; // Max Database Size defaults to 128MB private Dictionary m_referenceCache = new Dictionary(); - private Dictionary m_serializerCache = new Dictionary(); - private Dictionary m_deserializerCache = new Dictionary(); private string Password { get; set; } public string FileName { get; protected set; } protected SqlCeDataStore() + : base() { UseCommandCache = true; } @@ -56,6 +55,11 @@ protected override DbCommand GetNewCommandObject() return new SqlCeCommand(); } + protected override string AutoIncrementFieldIdentifier + { + get { return "IDENTITY"; } + } + /// /// Deletes the underlying DataStore /// @@ -121,36 +125,6 @@ public override void EnsureCompatibility() } } - private MethodInfo GetSerializer(Type itemType) - { - if (m_serializerCache.ContainsKey(itemType)) - { - return m_serializerCache[itemType]; - } - - var serializer = itemType.GetMethod("Serialize", BindingFlags.Public | BindingFlags.Instance); - - if (serializer == null) return null; - - m_serializerCache.Add(itemType, serializer); - return serializer; - } - - private MethodInfo GetDeserializer(Type itemType) - { - if (m_deserializerCache.ContainsKey(itemType)) - { - return m_deserializerCache[itemType]; - } - - var deserializer = itemType.GetMethod("Deserialize", BindingFlags.Public | BindingFlags.Instance); - - if (deserializer == null) return null; - - m_deserializerCache.Add(itemType, deserializer); - return deserializer; - } - /// /// Determines if the specified object already exists in the Store (by primary key value) /// diff --git a/OpenNETCF.ORM/Extensions.cs b/OpenNETCF.ORM/Extensions.cs index f086827..073724f 100644 --- a/OpenNETCF.ORM/Extensions.cs +++ b/OpenNETCF.ORM/Extensions.cs @@ -166,7 +166,7 @@ public static string ToSqlTypeString(this DbType type) return "bigint"; case DbType.Int32: case DbType.UInt32: - return "int"; + return "integer"; case DbType.Int16: case DbType.UInt16: return "smallint"; diff --git a/OpenNETCF.ORM/SQL Store Base/SQLStoreBase.cs b/OpenNETCF.ORM/SQL Store Base/SQLStoreBase.cs index 26e77fc..486fc38 100644 --- a/OpenNETCF.ORM/SQL Store Base/SQLStoreBase.cs +++ b/OpenNETCF.ORM/SQL Store Base/SQLStoreBase.cs @@ -5,6 +5,7 @@ using System.Diagnostics; using System.Data; using System.Data.Common; +using System.Reflection; namespace OpenNETCF.ORM { @@ -13,10 +14,13 @@ public abstract class SQLStoreBase : DataStore, IDispo { private List m_indexNameCache = new List(); private DbConnection m_connection; + private Dictionary m_serializerCache = new Dictionary(); + private Dictionary m_deserializerCache = new Dictionary(); public int DefaultStringFieldSize { get; set; } public int DefaultNumericFieldPrecision { get; set; } public int DefaultVarBinaryLength { get; set; } + protected abstract string AutoIncrementFieldIdentifier { get; } public ConnectionBehavior ConnectionBehavior { get; set; } @@ -429,7 +433,7 @@ protected virtual string GetFieldCreationAttributes(EntityAttribute attribute, F { case DbType.Int32: case DbType.UInt32: - sb.Append("IDENTITY "); + sb.Append(AutoIncrementFieldIdentifier + " "); break; case DbType.Guid: sb.Append("ROWGUIDCOL "); @@ -454,5 +458,34 @@ protected virtual string GetFieldCreationAttributes(EntityAttribute attribute, F return sb.ToString(); } + protected virtual MethodInfo GetSerializer(Type itemType) + { + if (m_serializerCache.ContainsKey(itemType)) + { + return m_serializerCache[itemType]; + } + + var serializer = itemType.GetMethod("Serialize", BindingFlags.Public | BindingFlags.Instance); + + if (serializer == null) return null; + + m_serializerCache.Add(itemType, serializer); + return serializer; + } + + protected virtual MethodInfo GetDeserializer(Type itemType) + { + if (m_deserializerCache.ContainsKey(itemType)) + { + return m_deserializerCache[itemType]; + } + + var deserializer = itemType.GetMethod("Deserialize", BindingFlags.Public | BindingFlags.Instance); + + if (deserializer == null) return null; + + m_deserializerCache.Add(itemType, deserializer); + return deserializer; + } } } diff --git a/TraceAndTestImpact.testsettings b/TraceAndTestImpact.testsettings new file mode 100644 index 0000000..3bf3433 --- /dev/null +++ b/TraceAndTestImpact.testsettings @@ -0,0 +1,21 @@ + + + These are test settings for Trace and Test Impact. + + + + + + + + + + + + + + + + + + \ No newline at end of file