diff --git a/Dapper.Contrib NET45/SqlMapperExtensionsAsync.cs b/Dapper.Contrib NET45/SqlMapperExtensionsAsync.cs index 52ecef77669dd2ad8a3cc9391205c7ad5b7b7655..85b1e016b3175f75be3363b5822de49e5dd271c3 100644 --- a/Dapper.Contrib NET45/SqlMapperExtensionsAsync.cs +++ b/Dapper.Contrib NET45/SqlMapperExtensionsAsync.cs @@ -63,7 +63,7 @@ public static partial class SqlMapperExtensions foreach (var property in TypePropertiesCache(type)) { var val = res[property.Name]; - property.SetValue(obj, val, null); + property.SetValue(obj, Convert.ChangeType(val, property.PropertyType), null); } ((IProxy)obj).IsDirty = false; //reset change tracking and return @@ -114,7 +114,7 @@ public static partial class SqlMapperExtensions foreach (var property in TypePropertiesCache(type)) { var val = res[property.Name]; - property.SetValue(obj, val, null); + property.SetValue(obj, Convert.ChangeType(val, property.PropertyType), null); } ((IProxy)obj).IsDirty = false; //reset change tracking and return list.Add(obj); @@ -200,8 +200,9 @@ public static partial class SqlMapperExtensions type = type.GetGenericArguments()[0]; var keyProperties = KeyPropertiesCache(type); - if (!keyProperties.Any()) - throw new ArgumentException("Entity must have at least one [Key] property"); + var explicitKeyProperties = ExplicitKeyPropertiesCache(type); + if (!keyProperties.Any() && !explicitKeyProperties.Any()) + throw new ArgumentException("Entity must have at least one [Key] or [ExplicitKey] property"); var name = GetTableName(type); @@ -209,6 +210,7 @@ public static partial class SqlMapperExtensions sb.AppendFormat("update {0} set ", name); var allProperties = TypePropertiesCache(type); + keyProperties.AddRange(explicitKeyProperties); var computedProperties = ComputedPropertiesCache(type); var nonIdProps = allProperties.Except(keyProperties.Union(computedProperties)).ToList(); @@ -249,10 +251,12 @@ public static partial class SqlMapperExtensions type = type.GetGenericArguments()[0]; var keyProperties = KeyPropertiesCache(type); - if (!keyProperties.Any()) - throw new ArgumentException("Entity must have at least one [Key] property"); + var explicitKeyProperties = ExplicitKeyPropertiesCache(type); + if (!keyProperties.Any() && !explicitKeyProperties.Any()) + throw new ArgumentException("Entity must have at least one [Key] or [ExplicitKey] property"); var name = GetTableName(type); + keyProperties.AddRange(explicitKeyProperties); var sb = new StringBuilder(); sb.AppendFormat("delete from {0} where ", name); @@ -294,30 +298,20 @@ public partial class SqlServerAdapter { public async Task InsertAsync(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, String tableName, string columnList, string parameterList, IEnumerable keyProperties, object entityToInsert) { - var cmd = String.Format("insert into {0} ({1}) values ({2});select SCOPE_IDENTITY() id", tableName, columnList, parameterList); var multi = await connection.QueryMultipleAsync(cmd, entityToInsert, transaction, commandTimeout); - var id = (int)multi.Read().First().id; - var propertyInfos = keyProperties as PropertyInfo[] ?? keyProperties.ToArray(); - if (!propertyInfos.Any()) return id; + var first = multi.Read().FirstOrDefault(); + if (first == null || first.id == null) return 0; - var idProperty = propertyInfos.First(); - if (idProperty.PropertyType.Name == "Int16") //for short id/key types issue #196 - idProperty.SetValue(entityToInsert, (Int16)id, null); - else - idProperty.SetValue(entityToInsert, id, null); - return id; + var id = (int)first.id; + var pi = keyProperties as PropertyInfo[] ?? keyProperties.ToArray(); + if (!pi.Any()) return id; + + var idp = pi.First(); + idp.SetValue(entityToInsert, Convert.ChangeType(id, idp.PropertyType), null); - //var cmd = String.Format("insert into {0} ({1}) values ({2})", tableName, columnList, parameterList); - //await connection.ExecuteAsync(cmd, entityToInsert, transaction, commandTimeout).ConfigureAwait(false); - //var r = await connection.QueryAsync("select SCOPE_IDENTITY() id", transaction: transaction, commandTimeout: commandTimeout).ConfigureAwait(false); - - //var id = (int)r.First().id; - //var propertyInfos = keyProperties as PropertyInfo[] ?? keyProperties.ToArray(); - //if (propertyInfos.Any()) - // propertyInfos.First().SetValue(entityToInsert, id, null); - //return id; + return id; } } @@ -326,14 +320,39 @@ public partial class SqlCeServerAdapter public async Task InsertAsync(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, String tableName, string columnList, string parameterList, IEnumerable keyProperties, object entityToInsert) { var cmd = String.Format("insert into {0} ({1}) values ({2})", tableName, columnList, parameterList); - await connection.ExecuteAsync(cmd, entityToInsert, transaction, commandTimeout).ConfigureAwait(false); + var r = (await connection.QueryAsync("select @@IDENTITY id", transaction: transaction, commandTimeout: commandTimeout).ConfigureAwait(false)).ToList(); - var r = await connection.QueryAsync("select @@IDENTITY id", transaction: transaction, commandTimeout: commandTimeout).ConfigureAwait(false); + if (r.First() == null || r.First().id == null) return 0; var id = (int)r.First().id; - var propertyInfos = keyProperties as PropertyInfo[] ?? keyProperties.ToArray(); - if (propertyInfos.Any()) - propertyInfos.First().SetValue(entityToInsert, id, null); + + var pi = keyProperties as PropertyInfo[] ?? keyProperties.ToArray(); + if (!pi.Any()) return id; + + var idp = pi.First(); + idp.SetValue(entityToInsert, Convert.ChangeType(id, idp.PropertyType), null); + + return id; + } +} + +public partial class MySqlAdapter : ISqlAdapter +{ + public async Task InsertAsync(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, string tableName, + string columnList, string parameterList, IEnumerable keyProperties, object entityToInsert) + { + var cmd = String.Format("insert into {0} ({1}) values ({2})", tableName, columnList, parameterList); + await connection.ExecuteAsync(cmd, entityToInsert, transaction, commandTimeout).ConfigureAwait(false); + var r = await connection.QueryAsync("select LAST_INSERT_ID()", transaction: transaction, commandTimeout: commandTimeout).ConfigureAwait(false); + + var id = r.First().id; + if (id == null) return 0; + var pi = keyProperties as PropertyInfo[] ?? keyProperties.ToArray(); + if (!pi.Any()) return id; + + var idp = pi.First(); + idp.SetValue(entityToInsert, Convert.ChangeType(id, idp.PropertyType), null); + return id; } } @@ -383,15 +402,16 @@ public partial class SQLiteAdapter public async Task InsertAsync(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, String tableName, string columnList, string parameterList, IEnumerable keyProperties, object entityToInsert) { - var cmd = String.Format("insert into {0} ({1}) values ({2})", tableName, columnList, parameterList); + var cmd = String.Format("insert into {0} ({1}) values ({2}); select last_insert_rowid() id", tableName, columnList, parameterList); + var multi = await connection.QueryMultipleAsync(cmd, entityToInsert, transaction, commandTimeout); - await connection.ExecuteAsync(cmd, entityToInsert, transaction: transaction, commandTimeout: commandTimeout).ConfigureAwait(false); + var id = (int)multi.Read().First().id; + var pi = keyProperties as PropertyInfo[] ?? keyProperties.ToArray(); + if (!pi.Any()) return id; + + var idp = pi.First(); + idp.SetValue(entityToInsert, Convert.ChangeType(id, idp.PropertyType), null); - var r = await connection.QueryAsync("select last_insert_rowid() id", transaction: transaction, commandTimeout: commandTimeout).ConfigureAwait(false); - var id = (int)r.First().id; - var propertyInfos = keyProperties as PropertyInfo[] ?? keyProperties.ToArray(); - if (propertyInfos.Any()) - propertyInfos.First().SetValue(entityToInsert, id, null); return id; } } diff --git a/Dapper.Contrib.MsSqlTests NET45/App.config b/Dapper.Contrib.MsSqlTests NET45/App.config new file mode 100644 index 0000000000000000000000000000000000000000..fad249e406f0363449addb9c6ecaf61077d5b19b --- /dev/null +++ b/Dapper.Contrib.MsSqlTests NET45/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Dapper.Contrib.MsSqlTests NET45/Dapper.Contrib.MsSqlTests NET45.csproj b/Dapper.Contrib.MsSqlTests NET45/Dapper.Contrib.MsSqlTests NET45.csproj new file mode 100644 index 0000000000000000000000000000000000000000..8a0c30f24aaa7900db0595455095f6451aa36700 --- /dev/null +++ b/Dapper.Contrib.MsSqlTests NET45/Dapper.Contrib.MsSqlTests NET45.csproj @@ -0,0 +1,83 @@ + + + + + Debug + AnyCPU + {E5482A3B-2C2A-4907-9804-C191F18FA622} + Exe + Properties + Dapper.Contrib.Tests + Dapper.Contrib.Tests + v4.5 + 512 + + + AnyCPU + true + full + false + bin\Debug\ + TRACE;DEBUG;MSSQL + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + TestsAsync.cs + + + Assert.cs + + + Tests.cs + + + + + + + + + + {0fff5bc7-0a4b-4d87-835e-4fad70937507} + Dapper NET45 + + + {302ec82f-a81b-48c5-b653-b5c75d2bd103} + Dapper.Contrib NET45 + + + {bf782ef1-2b0f-42fa-9dd0-928454a94c6d} + Dapper.SqlBuilder + + + + + \ No newline at end of file diff --git a/Dapper.Contrib.MsSqlTests NET45/Program.cs b/Dapper.Contrib.MsSqlTests NET45/Program.cs new file mode 100644 index 0000000000000000000000000000000000000000..72344c9906028b839415a6cf07e71be669e12ca4 --- /dev/null +++ b/Dapper.Contrib.MsSqlTests NET45/Program.cs @@ -0,0 +1,98 @@ +using System; +using System.Data.SqlClient; +using System.Data.SqlServerCe; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; + +namespace Dapper.Contrib.Tests +{ + class Program + { + static void Main(string[] args) + { + SetupMsSqlDatabase(); + SetupTables(); + RunTests(); + DropTables(); + SetupTables(); + RunAsyncTests(); + Console.WriteLine("Press any key..."); + Console.ReadKey(); + } + + private static void SetupMsSqlDatabase() + { + using (var connection = new SqlConnection("Data Source = .\\SQLEXPRESS;Initial Catalog=master;Integrated Security=SSPI")) + { + connection.Open(); + var exists = connection.Query("SELECT count(*) FROM master.sys.databases WHERE name = @name", + new { name = "DapperContribMsSqlTests" }).First(); + if (exists > 0) + { + connection.Execute("drop database DapperContribMsSqlTests"); + } + connection.Execute("create database DapperContribMsSqlTests"); + + } + } + + private static void DropTables() + { + + using (var connection = new SqlConnection("Data Source = .\\SQLEXPRESS;Initial Catalog=DapperContribMsSqlTests;Integrated Security=SSPI")) + { + connection.Open(); + connection.Execute("alter database DapperContribMsSqlTests set single_user with rollback immediate"); + connection.Execute(@" drop table Stuff"); + connection.Execute(@" drop table People "); + connection.Execute(@" drop table Users"); + connection.Execute(@" drop table Automobiles "); + connection.Execute(@" drop table Results "); + connection.Execute(@" drop table ObjectX "); + connection.Execute(@" drop table ObjectY "); + } + Console.WriteLine("Created database"); + } + + private static void SetupTables() + { + + using (var connection = new SqlConnection("Data Source = .\\SQLEXPRESS;Initial Catalog=DapperContribMsSqlTests;Integrated Security=SSPI")) + { + connection.Open(); + connection.Execute(@" create table Stuff (TheId int IDENTITY(1,1) not null, Name nvarchar(100) not null, Created DateTime null) "); + connection.Execute(@" create table People (Id int IDENTITY(1,1) not null, Name nvarchar(100) not null) "); + connection.Execute(@" create table Users (Id int IDENTITY(1,1) not null, Name nvarchar(100) not null, Age int not null) "); + connection.Execute(@" create table Automobiles (Id int IDENTITY(1,1) not null, Name nvarchar(100) not null) "); + connection.Execute(@" create table Results (Id int IDENTITY(1,1) not null, Name nvarchar(100) not null, [Order] int not null) "); + connection.Execute(@" create table ObjectX (ObjectXId nvarchar(100) not null, Name nvarchar(100) not null) "); + connection.Execute(@" create table ObjectY (ObjectYId int not null, Name nvarchar(100) not null) "); + } + Console.WriteLine("Created database"); + } + + private static void RunTests() + { + var tester = new Tests(); + foreach (var method in typeof(Tests).GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)) + { + Console.Write("Running " + method.Name); + method.Invoke(tester, null); + Console.WriteLine(" - OK!"); + } + } + + private static void RunAsyncTests() + { + var tester = new TestsAsync(); + foreach (var method in typeof(TestsAsync).GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)) + { + Console.Write("Running " + method.Name); + Task.WaitAll((Task)method.Invoke(tester, null)); + Console.WriteLine(" - OK!"); + } + } + } +} diff --git a/Dapper.Contrib.MsSqlTests NET45/Properties/AssemblyInfo.cs b/Dapper.Contrib.MsSqlTests NET45/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000000000000000000000000000000000..34276d4dcee776a946b7afeefb048267ca5731e0 --- /dev/null +++ b/Dapper.Contrib.MsSqlTests NET45/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +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("Dapper.Contrib.Tests NET45")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Dapper.Contrib.Tests NET45")] +[assembly: AssemblyCopyright("Copyright © 2014")] +[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("2d6cdd3d-b94b-4cd4-842a-60c6e4c93c5c")] + +// 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.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Dapper.Contrib.SqliteTests NET45/Assert.cs b/Dapper.Contrib.SqliteTests NET45/Assert.cs new file mode 100644 index 0000000000000000000000000000000000000000..3b6b75d10e461b9ed0eac59938199a56df09030a --- /dev/null +++ b/Dapper.Contrib.SqliteTests NET45/Assert.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Dapper.Contrib.Tests +{ + /// + /// Assert extensions borrowed from Sam's code in DapperTests + /// + static class Assert + { + public static void IsEqualTo(this T obj, T other) + { + if (!obj.Equals(other)) + { + throw new ApplicationException(string.Format("{0} should be equals to {1}", obj, other)); + } + } + + public static void IsMoreThan(this int obj, int other) + { + if (obj < other) + { + throw new ApplicationException(string.Format("{0} should be larger than {1}", obj, other)); + } + } + + public static void IsMoreThan(this long obj, int other) + { + if (obj < other) + { + throw new ApplicationException(string.Format("{0} should be larger than {1}", obj, other)); + } + } + + public static void IsSequenceEqualTo(this IEnumerable obj, IEnumerable other) + { + if (!obj.SequenceEqual(other)) + { + throw new ApplicationException(string.Format("{0} should be equals to {1}", obj, other)); + } + } + + public static void IsFalse(this bool b) + { + if (b) + { + throw new ApplicationException("Expected false"); + } + } + + public static void IsTrue(this bool b) + { + if (!b) + { + throw new ApplicationException("Expected true"); + } + } + + public static void IsNull(this object obj) + { + if (obj != null) + { + throw new ApplicationException("Expected null"); + } + } + public static void IsNotNull(this object obj) + { + if (obj == null) + { + throw new ApplicationException("Expected not null"); + } + } + + } +} \ No newline at end of file diff --git a/Dapper.Contrib.SqliteTests NET45/Dapper.Contrib.SqliteTests NET45.csproj b/Dapper.Contrib.SqliteTests NET45/Dapper.Contrib.SqliteTests NET45.csproj new file mode 100644 index 0000000000000000000000000000000000000000..bee5ffce8e25f7973714f2be040f3ac02ebd3d04 --- /dev/null +++ b/Dapper.Contrib.SqliteTests NET45/Dapper.Contrib.SqliteTests NET45.csproj @@ -0,0 +1,107 @@ + + + + Debug + x86 + 8.0.30703 + 2.0 + {114C32A1-D726-4F64-B8AF-AFEDE728E0BC} + Exe + Properties + Dapper.Contrib.Tests + Dapper.Contrib.Tests + v4.5 + + + 512 + + + + + + + + + + + x86 + true + full + false + bin\Debug\ + TRACE;DEBUG;SQLITE + prompt + 4 + false + + + x86 + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + + + + False + .\System.Data.SQLite.dll + + + + + + + + + + + + TestsAsync.cs + + + Tests.cs + + + + + + + + {0fff5bc7-0a4b-4d87-835e-4fad70937507} + Dapper NET45 + + + {302ec82f-a81b-48c5-b653-b5c75d2bd103} + Dapper.Contrib NET45 + + + {BF782EF1-2B0F-42FA-9DD0-928454A94C6D} + Dapper.SqlBuilder + + + + + + Always + + + Always + + + + + + + + \ No newline at end of file diff --git a/Dapper.Contrib.SqliteTests NET45/Program.cs b/Dapper.Contrib.SqliteTests NET45/Program.cs new file mode 100644 index 0000000000000000000000000000000000000000..e2cdec3d0b5775f81da82173dbe0c323cf88ebee --- /dev/null +++ b/Dapper.Contrib.SqliteTests NET45/Program.cs @@ -0,0 +1,68 @@ +using System; +using System.Data.SQLite; +using System.IO; +using System.Reflection; +using System.Threading.Tasks; + +namespace Dapper.Contrib.Tests +{ + class Program + { + static void Main(string[] args) + { + Setup(); + RunTests(); + Setup(); + RunAsyncTests(); + Console.WriteLine("Press any key..."); + Console.ReadKey(); + } + + private static void Setup() + { + var projLoc = Assembly.GetAssembly(typeof(Program)).Location; + var projFolder = Path.GetDirectoryName(projLoc); + + if (File.Exists(projFolder + "\\Test.sqlite")) + File.Delete(projFolder + "\\Test.sqlite"); + SQLiteConnection.CreateFile(projFolder + "\\Test.sqlite"); + + var connectionString = "Data Source = " + projFolder + "\\Test.sqlite;"; + using (var connection = new SQLiteConnection(connectionString)) + { + connection.Open(); + connection.Execute(@" create table Stuff (TheId integer primary key autoincrement not null, Name nvarchar(100) not null, Created DateTime null) "); + connection.Execute(@" create table People (Id integer primary key autoincrement not null, Name nvarchar(100) not null) "); + connection.Execute(@" create table Users (Id integer primary key autoincrement not null, Name nvarchar(100) not null, Age int not null) "); + connection.Execute(@" create table Automobiles (Id integer primary key autoincrement not null, Name nvarchar(100) not null) "); + connection.Execute(@" create table Results (Id integer primary key autoincrement not null, Name nvarchar(100) not null, [Order] int not null) "); + connection.Execute(@" create table ObjectX (ObjectXId nvarchar(100) not null, Name nvarchar(100) not null) "); + connection.Execute(@" create table ObjectY (ObjectYId integer not null, Name nvarchar(100) not null) "); + } + Console.WriteLine("Created database"); + } + + private static void RunTests() + { + var tester = new Tests(); + foreach (var method in typeof(Tests).GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)) + { + Console.Write("Running " + method.Name); + method.Invoke(tester, null); + Console.WriteLine(" - OK!"); + } + } + + private static void RunAsyncTests() + { + var tester = new TestsAsync(); + foreach (var method in typeof(TestsAsync).GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)) + { + Console.Write("Running " + method.Name); + Task.WaitAll((Task)method.Invoke(tester, null)); + Console.WriteLine(" - OK!"); + } + } + + } +} diff --git a/Dapper.Contrib.SqliteTests NET45/Properties/AssemblyInfo.cs b/Dapper.Contrib.SqliteTests NET45/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000000000000000000000000000000000..d789c72c991a790638b3b5a3b621906462a35b1c --- /dev/null +++ b/Dapper.Contrib.SqliteTests NET45/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("Dapper.Contrib.Tests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("Dapper.Contrib.Tests")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] +[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("9d5920b6-d6af-41ca-b851-803ac922d933")] + +// 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.40.0.0")] +[assembly: AssemblyFileVersion("1.40.0.0")] diff --git a/Dapper.Contrib.SqliteTests NET45/System.Data.SQLite.dll b/Dapper.Contrib.SqliteTests NET45/System.Data.SQLite.dll new file mode 100644 index 0000000000000000000000000000000000000000..824e09549e6f427049b8fc7cb92d8b6932aae547 Binary files /dev/null and b/Dapper.Contrib.SqliteTests NET45/System.Data.SQLite.dll differ diff --git a/Dapper.Contrib.SqliteTests NET45/app.config b/Dapper.Contrib.SqliteTests NET45/app.config new file mode 100644 index 0000000000000000000000000000000000000000..b7a7ef166010d260dd67729df2865367b5d5ca5c --- /dev/null +++ b/Dapper.Contrib.SqliteTests NET45/app.config @@ -0,0 +1,3 @@ + + + diff --git a/Dapper.Contrib.SqliteTests NET45/x64/SQLite.Interop.dll b/Dapper.Contrib.SqliteTests NET45/x64/SQLite.Interop.dll new file mode 100644 index 0000000000000000000000000000000000000000..8beab949eb14dd78f57df12c98f026ff3ab73559 Binary files /dev/null and b/Dapper.Contrib.SqliteTests NET45/x64/SQLite.Interop.dll differ diff --git a/Dapper.Contrib.SqliteTests NET45/x86/SQLite.Interop.dll b/Dapper.Contrib.SqliteTests NET45/x86/SQLite.Interop.dll new file mode 100644 index 0000000000000000000000000000000000000000..a13fe21d59d0512465caabd6b4ab71e744610426 Binary files /dev/null and b/Dapper.Contrib.SqliteTests NET45/x86/SQLite.Interop.dll differ diff --git a/Dapper.Contrib.Tests NET45/Program.cs b/Dapper.Contrib.Tests NET45/Program.cs index f7cbecd089e7bccd99a52d3800b900d1006ad140..dab25254e33d38dc1851da303facb0db04b32c11 100644 --- a/Dapper.Contrib.Tests NET45/Program.cs +++ b/Dapper.Contrib.Tests NET45/Program.cs @@ -36,6 +36,8 @@ private static void Setup() connection.Execute(@" create table Users (Id int IDENTITY(1,1) not null, Name nvarchar(100) not null, Age int not null) "); connection.Execute(@" create table Automobiles (Id int IDENTITY(1,1) not null, Name nvarchar(100) not null) "); connection.Execute(@" create table Results (Id int IDENTITY(1,1) not null, Name nvarchar(100) not null, [Order] int not null) "); + connection.Execute(@" create table ObjectX (ObjectXId nvarchar(100) not null, Name nvarchar(100) not null) "); + connection.Execute(@" create table ObjectY (ObjectYId int not null, Name nvarchar(100) not null) "); } Console.WriteLine("Created database"); } diff --git a/Dapper.Contrib.Tests NET45/TestsAsync.cs b/Dapper.Contrib.Tests NET45/TestsAsync.cs index 35ffe7897b3f81147c4651b210680e774e3d3294..87fa30eab845e3e3e008ecd302f21150552ac7c6 100644 --- a/Dapper.Contrib.Tests NET45/TestsAsync.cs +++ b/Dapper.Contrib.Tests NET45/TestsAsync.cs @@ -1,6 +1,11 @@ using System; using System.Collections.Generic; using System.Data; +#if SQLITE +using System.Data.SQLite; +#elif MSSQL +using System.Data.SqlClient; +#endif using System.Data.SqlServerCe; using System.IO; using System.Linq; @@ -18,10 +23,55 @@ private IDbConnection GetOpenConnection() var projLoc = Assembly.GetAssembly(GetType()).Location; var projFolder = Path.GetDirectoryName(projLoc); +#if SQLITE + var connection = new SQLiteConnection("Data Source = " + projFolder + "\\Test.sqlite;"); +#elif MSSQL + var connection = new SqlConnection("Data Source = .\\SQLEXPRESS;Initial Catalog=DapperContribMsSqlTests;Integrated Security=SSPI"); +#else var connection = new SqlCeConnection("Data Source = " + projFolder + "\\Test.sdf;"); +#endif connection.Open(); return connection; } + /// + /// Tests for issue #351 + /// + public async Task InsertGetUpdateDeleteWithExplicitKey() + { + + using (var connection = GetOpenConnection()) + { + var guid = Guid.NewGuid().ToString(); + var o1 = new ObjectX { ObjectXId = guid, Name = "Foo" }; + await connection.InsertAsync(o1); + var list1 = (await connection.QueryAsync("select * from objectx")).ToList(); + list1.Count.IsEqualTo(1); + o1 = await connection.GetAsync(guid); + o1.ObjectXId.IsEqualTo(guid); + o1.Name = "Bar"; + await connection.UpdateAsync(o1); + o1 = await connection.GetAsync(guid); + o1.Name.IsEqualTo("Bar"); + await connection.DeleteAsync(o1); + o1 = await connection.GetAsync(guid); + o1.IsNull(); + + const int id = 42; + var o2 = new ObjectY() { ObjectYId = id, Name = "Foo" }; + await connection.InsertAsync(o2); + var list2 = (await connection.QueryAsync("select * from objecty")).ToList(); + list2.Count.IsEqualTo(1); + o2 = await connection.GetAsync(id); + o2.ObjectYId.IsEqualTo(id); + o2.Name = "Bar"; + await connection.UpdateAsync(o2); + o2 = await connection.GetAsync(id); + o2.Name.IsEqualTo("Bar"); + await connection.DeleteAsync(o2); + o2 = await connection.GetAsync(id); + o2.IsNull(); + } + } public async Task TableNameAsync() { @@ -87,7 +137,7 @@ public async Task InsertGetUpdateAsync() (await connection.UpdateAsync(notrackedUser)).IsEqualTo(false); //returns false, user not found - (await connection.InsertAsync(new User { Name = "Adam", Age = 10 }, sqlAdapter: new SqlCeServerAdapter())).IsMoreThan(0); + (await connection.InsertAsync(new User { Name = "Adam", Age = 10 })).IsMoreThan(0); } } @@ -159,7 +209,7 @@ public async Task BuilderTemplateWOCompositionAsync() public async Task InsertListAsync() { - const int numberOfEntities = 100; + const int numberOfEntities = 10; var users = new List(); for (var i = 0; i < numberOfEntities; i++) @@ -178,7 +228,7 @@ public async Task InsertListAsync() public async Task UpdateList() { - const int numberOfEntities = 100; + const int numberOfEntities = 10; var users = new List(); for (var i = 0; i < numberOfEntities; i++) @@ -205,7 +255,7 @@ public async Task UpdateList() public async Task DeleteList() { - const int numberOfEntities = 100; + const int numberOfEntities = 10; var users = new List(); for (var i = 0; i < numberOfEntities; i++) @@ -230,7 +280,7 @@ public async Task DeleteList() public async Task GetAllAsync() { - const int numberOfEntities = 1000; + const int numberOfEntities = 10; var users = new List(); for (var i = 0; i < numberOfEntities; i++) diff --git a/Dapper.Contrib.Tests/Program.cs b/Dapper.Contrib.Tests/Program.cs index a910ec5eff988ef80f057d97f856ede042cd3d81..907cb2286cbff172d96a2d3796f2c2f26ea02a4b 100644 --- a/Dapper.Contrib.Tests/Program.cs +++ b/Dapper.Contrib.Tests/Program.cs @@ -33,6 +33,8 @@ private static void Setup() connection.Execute(@" create table Users (Id int IDENTITY(1,1) not null, Name nvarchar(100) not null, Age int not null) "); connection.Execute(@" create table Automobiles (Id int IDENTITY(1,1) not null, Name nvarchar(100) not null) "); connection.Execute(@" create table Results (Id int IDENTITY(1,1) not null, Name nvarchar(100) not null, [Order] int not null) "); + connection.Execute(@" create table ObjectX (ObjectXId nvarchar(100) not null, Name nvarchar(100) not null) "); + connection.Execute(@" create table ObjectY (ObjectYId int not null, Name nvarchar(100) not null) "); } Console.WriteLine("Created database"); } diff --git a/Dapper.Contrib.Tests/Tests.cs b/Dapper.Contrib.Tests/Tests.cs index 26821d643f3b78f8c4ae337f5f32d060e638c26b..908c648038808e128676d59ac03c84d167addd65 100644 --- a/Dapper.Contrib.Tests/Tests.cs +++ b/Dapper.Contrib.Tests/Tests.cs @@ -1,6 +1,11 @@ using System; using System.Collections.Generic; using System.Data; +#if SQLITE +using System.Data.SQLite; +#elif MSSQL +using System.Data.SqlClient; +#endif using System.Data.SqlServerCe; using System.IO; using System.Linq; @@ -11,6 +16,22 @@ namespace Dapper.Contrib.Tests { + [Table("ObjectX")] + public class ObjectX + { + [ExplicitKey] + public string ObjectXId { get; set; } + public string Name { get; set; } + } + + [Table("ObjectY")] + public class ObjectY + { + [ExplicitKey] + public int ObjectYId { get; set; } + public string Name { get; set; } + } + public interface IUser { [Key] @@ -65,7 +86,13 @@ private IDbConnection GetOpenConnection() var projLoc = Assembly.GetAssembly(GetType()).Location; var projFolder = Path.GetDirectoryName(projLoc); +#if SQLITE + var connection = new SQLiteConnection("Data Source = " + projFolder + "\\Test.sqlite;"); +#elif MSSQL + var connection = new SqlConnection("Data Source = .\\SQLEXPRESS;Initial Catalog=DapperContribMsSqlTests;Integrated Security=SSPI"); +#else var connection = new SqlCeConnection("Data Source = " + projFolder + "\\Test.sdf;"); +#endif connection.Open(); return connection; } @@ -75,10 +102,57 @@ private IDbConnection GetConnection() var projLoc = Assembly.GetAssembly(GetType()).Location; var projFolder = Path.GetDirectoryName(projLoc); +#if SQLITE + var connection = new SQLiteConnection("Data Source = " + projFolder + "\\Test.sqlite;"); +#elif MSSQL + var connection = new SqlConnection("Data Source = .\\SQLEXPRESS;Initial Catalog=DapperContribMsSqlTests;Integrated Security=SSPI"); +#else var connection = new SqlCeConnection("Data Source = " + projFolder + "\\Test.sdf;"); +#endif return connection; } + /// + /// Tests for issue #351 + /// + public void InsertGetUpdateDeleteWithExplicitKey() + { + + using (var connection = GetOpenConnection()) + { + + var guid = Guid.NewGuid().ToString(); + var o1 = new ObjectX { ObjectXId = guid, Name = "Foo" }; + connection.Insert(o1); + var list1 = connection.Query("select * from objectx").ToList(); + list1.Count.IsEqualTo(1); + o1 = connection.Get(guid); + o1.ObjectXId.IsEqualTo(guid); + o1.Name = "Bar"; + connection.Update(o1); + o1 = connection.Get(guid); + o1.Name.IsEqualTo("Bar"); + connection.Delete(o1); + o1 = connection.Get(guid); + o1.IsNull(); + + const int id = 42; + var o2 = new ObjectY() { ObjectYId = id, Name = "Foo" }; + connection.Insert(o2); + var list2 = connection.Query("select * from objecty").ToList(); + list2.Count.IsEqualTo(1); + o2 = connection.Get(id); + o2.ObjectYId.IsEqualTo(id); + o2.Name = "Bar"; + connection.Update(o2); + o2 = connection.Get(id); + o2.Name.IsEqualTo("Bar"); + connection.Delete(o2); + o2 = connection.Get(id); + o2.IsNull(); + } + } + public void ShortIdentity() { using (var connection = GetOpenConnection()) @@ -141,7 +215,7 @@ public void TestClosedConnection() public void InsertList() { - const int numberOfEntities = 100; + const int numberOfEntities = 10; var users = new List(); for (var i = 0; i < numberOfEntities; i++) @@ -161,7 +235,7 @@ public void InsertList() public void UpdateList() { - const int numberOfEntities = 100; + const int numberOfEntities = 10; var users = new List(); for (var i = 0; i < numberOfEntities; i++) @@ -188,7 +262,7 @@ public void UpdateList() public void DeleteList() { - const int numberOfEntities = 100; + const int numberOfEntities = 10; var users = new List(); for (var i = 0; i < numberOfEntities; i++) @@ -247,6 +321,9 @@ public void InsertGetUpdate() connection.Update(notrackedUser).IsEqualTo(false); //returns false, user not found } } +#if SQLITE +#elif MSSQL +#else public void InsertWithCustomDbType() { @@ -265,6 +342,7 @@ public void InsertWithCustomDbType() { sqliteCodeCalled = ex.Message.IndexOf("There was an error parsing the query", StringComparison.InvariantCultureIgnoreCase) >= 0; } +// ReSharper disable once EmptyGeneralCatchClause catch (Exception) { } @@ -276,7 +354,7 @@ public void InsertWithCustomDbType() throw new Exception("Was expecting sqlite code to be called"); } } - +#endif public void InsertWithCustomTableNameMapper() { @@ -308,7 +386,7 @@ public void InsertWithCustomTableNameMapper() public void GetAll() { - const int numberOfEntities = 100; + const int numberOfEntities = 10; var users = new List(); for (var i = 0; i < numberOfEntities; i++) diff --git a/Dapper.Contrib/SqlMapperExtensions.cs b/Dapper.Contrib/SqlMapperExtensions.cs index 26f73c8c0240c68d08f29bb7d242b1e43d4be189..e318bafa72bc9eadaf7fcf14455b67ffca2c5be0 100644 --- a/Dapper.Contrib/SqlMapperExtensions.cs +++ b/Dapper.Contrib/SqlMapperExtensions.cs @@ -32,6 +32,7 @@ public interface ITableNameMapper public delegate string TableNameMapperDelegate(Type type); private static readonly ConcurrentDictionary> KeyProperties = new ConcurrentDictionary>(); + private static readonly ConcurrentDictionary> ExplicitKeyProperties = new ConcurrentDictionary>(); private static readonly ConcurrentDictionary> TypeProperties = new ConcurrentDictionary>(); private static readonly ConcurrentDictionary> ComputedProperties = new ConcurrentDictionary>(); private static readonly ConcurrentDictionary GetQueries = new ConcurrentDictionary(); @@ -41,10 +42,11 @@ public interface ITableNameMapper {"sqlconnection", new SqlServerAdapter()}, {"sqlceconnection", new SqlCeServerAdapter()}, {"npgsqlconnection", new PostgresAdapter()}, - {"sqliteconnection", new SQLiteAdapter()} + {"sqliteconnection", new SQLiteAdapter()}, + {"mysqlconnection", new MySqlAdapter()}, }; - private static IEnumerable ComputedPropertiesCache(Type type) + private static List ComputedPropertiesCache(Type type) { IEnumerable pi; if (ComputedProperties.TryGetValue(type.TypeHandle, out pi)) @@ -58,6 +60,20 @@ private static IEnumerable ComputedPropertiesCache(Type type) return computedProperties; } + private static List ExplicitKeyPropertiesCache(Type type) + { + IEnumerable pi; + if (ExplicitKeyProperties.TryGetValue(type.TypeHandle, out pi)) + { + return pi.ToList(); + } + + var explicitKeyProperties = TypePropertiesCache(type).Where(p => p.GetCustomAttributes(true).Any(a => a is ExplicitKeyAttribute)).ToList(); + + ExplicitKeyProperties[type.TypeHandle] = explicitKeyProperties; + return explicitKeyProperties; + } + private static List KeyPropertiesCache(Type type) { @@ -123,17 +139,18 @@ private static bool IsWriteable(PropertyInfo pi) if (!GetQueries.TryGetValue(type.TypeHandle, out sql)) { var keys = KeyPropertiesCache(type); - if (keys.Count() > 1) - throw new DataException("Get only supports an entity with a single [Key] property"); - if (!keys.Any()) - throw new DataException("Get only supports an entity with a [Key] property"); + var explicitKeys = ExplicitKeyPropertiesCache(type); + if (keys.Count() > 1 || explicitKeys.Count() > 1) + throw new DataException("Get only supports an entity with a single [Key] or [ExplicitKey] property"); + if (!keys.Any() && !explicitKeys.Any()) + throw new DataException("Get only supports an entity with a [Key] or an [ExplicitKey] property"); - var onlyKey = keys.First(); + var key = keys.Any() ? keys.First() : explicitKeys.First(); var name = GetTableName(type); // TODO: query information schema and only select fields that are both in information schema and underlying class / interface - sql = "select * from " + name + " where " + onlyKey.Name + " = @id"; + sql = "select * from " + name + " where " + key.Name + " = @id"; GetQueries[type.TypeHandle] = sql; } @@ -154,7 +171,7 @@ private static bool IsWriteable(PropertyInfo pi) foreach (var property in TypePropertiesCache(type)) { var val = res[property.Name]; - property.SetValue(obj, val, null); + property.SetValue(obj, Convert.ChangeType(val, property.PropertyType), null); } ((IProxy)obj).IsDirty = false; //reset change tracking and return @@ -207,7 +224,7 @@ private static bool IsWriteable(PropertyInfo pi) foreach (var property in TypePropertiesCache(type)) { var val = res[property.Name]; - property.SetValue(obj, val, null); + property.SetValue(obj, Convert.ChangeType(val, property.PropertyType), null); } ((IProxy)obj).IsDirty = false; //reset change tracking and return list.Add(obj); @@ -273,10 +290,12 @@ private static string GetTableName(Type type) var computedProperties = ComputedPropertiesCache(type); var allPropertiesExceptKeyAndComputed = allProperties.Except(keyProperties.Union(computedProperties)).ToList(); + var adapter = GetFormatter(connection); + for (var i = 0; i < allPropertiesExceptKeyAndComputed.Count(); i++) { var property = allPropertiesExceptKeyAndComputed.ElementAt(i); - sbColumnList.AppendFormat("[{0}]", property.Name); + adapter.AppendColumnName(sbColumnList, property.Name); //fix for issue #336 if (i < allPropertiesExceptKeyAndComputed.Count() - 1) sbColumnList.Append(", "); } @@ -296,7 +315,6 @@ private static string GetTableName(Type type) if (!isList) //single entity { - var adapter = GetFormatter(connection); returnVal = adapter.Insert(connection, transaction, commandTimeout, name, sbColumnList.ToString(), sbParameterList.ToString(), keyProperties, entityToInsert); } @@ -331,8 +349,9 @@ private static string GetTableName(Type type) type = type.GetGenericArguments()[0]; var keyProperties = KeyPropertiesCache(type); - if (!keyProperties.Any()) - throw new ArgumentException("Entity must have at least one [Key] property"); + var explicitKeyProperties = ExplicitKeyPropertiesCache(type); + if (!keyProperties.Any() && !explicitKeyProperties.Any()) + throw new ArgumentException("Entity must have at least one [Key] or [ExplicitKey] property"); var name = GetTableName(type); @@ -340,13 +359,16 @@ private static string GetTableName(Type type) sb.AppendFormat("update {0} set ", name); var allProperties = TypePropertiesCache(type); + keyProperties.AddRange(explicitKeyProperties); var computedProperties = ComputedPropertiesCache(type); var nonIdProps = allProperties.Except(keyProperties.Union(computedProperties)).ToList(); + var adapter = GetFormatter(connection); + for (var i = 0; i < nonIdProps.Count(); i++) { var property = nonIdProps.ElementAt(i); - sb.AppendFormat("[{0}] = @{1}", property.Name, property.Name); + adapter.AppendColumnNameEqualsValue(sb, property.Name); //fix for issue #336 if (i < nonIdProps.Count() - 1) sb.AppendFormat(", "); } @@ -354,7 +376,7 @@ private static string GetTableName(Type type) for (var i = 0; i < keyProperties.Count(); i++) { var property = keyProperties.ElementAt(i); - sb.AppendFormat("[{0}] = @{1}", property.Name, property.Name); + adapter.AppendColumnNameEqualsValue(sb, property.Name); //fix for issue #336 if (i < keyProperties.Count() - 1) sb.AppendFormat(" and "); } @@ -380,18 +402,22 @@ private static string GetTableName(Type type) type = type.GetGenericArguments()[0]; var keyProperties = KeyPropertiesCache(type); - if (!keyProperties.Any()) - throw new ArgumentException("Entity must have at least one [Key] property"); + var explicitKeyProperties = ExplicitKeyPropertiesCache(type); + if (!keyProperties.Any() && !explicitKeyProperties.Any()) + throw new ArgumentException("Entity must have at least one [Key] or [ExplicitKey] property"); var name = GetTableName(type); + keyProperties.AddRange(explicitKeyProperties); var sb = new StringBuilder(); sb.AppendFormat("delete from {0} where ", name); + var adapter = GetFormatter(connection); + for (var i = 0; i < keyProperties.Count(); i++) { var property = keyProperties.ElementAt(i); - sb.AppendFormat("[{0}] = @{1}", property.Name, property.Name); + adapter.AppendColumnNameEqualsValue(sb, property.Name); //fix for issue #336 if (i < keyProperties.Count() - 1) sb.AppendFormat(" and "); } @@ -609,6 +635,11 @@ public class KeyAttribute : Attribute { } + [AttributeUsage(AttributeTargets.Property)] + public class ExplicitKeyAttribute : Attribute + { + } + [AttributeUsage(AttributeTargets.Property)] public class WriteAttribute : Attribute { @@ -628,27 +659,41 @@ public class ComputedAttribute : Attribute public partial interface ISqlAdapter { int Insert(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, String tableName, string columnList, string parameterList, IEnumerable keyProperties, object entityToInsert); + + //new methods for issue #336 + void AppendColumnName(StringBuilder sb, string columnName); + void AppendColumnNameEqualsValue(StringBuilder sb, string columnName); } public partial class SqlServerAdapter : ISqlAdapter { public int Insert(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, String tableName, string columnList, string parameterList, IEnumerable keyProperties, object entityToInsert) { - var cmd = String.Format("insert into {0} ({1}) values ({2});select SCOPE_IDENTITY() id", tableName, columnList, parameterList); - var multi = connection.QueryMultiple(cmd,entityToInsert , transaction, commandTimeout); + var multi = connection.QueryMultiple(cmd, entityToInsert, transaction, commandTimeout); - var id = (int)multi.Read().First().id; + var first = multi.Read().FirstOrDefault(); + if (first == null || first.id == null) return 0; + + var id = (int)first.id; var propertyInfos = keyProperties as PropertyInfo[] ?? keyProperties.ToArray(); if (!propertyInfos.Any()) return id; var idProperty = propertyInfos.First(); - if (idProperty.PropertyType.Name == "Int16") //for short id/key types issue #196 - idProperty.SetValue(entityToInsert, (Int16)id, null); - else - idProperty.SetValue(entityToInsert, id, null); + idProperty.SetValue(entityToInsert, Convert.ChangeType(id, idProperty.PropertyType), null); + return id; } + + public void AppendColumnName(StringBuilder sb, string columnName) + { + sb.AppendFormat("[{0}]", columnName); + } + + public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName) + { + sb.AppendFormat("[{0}] = @{1}", columnName, columnName); + } } public partial class SqlCeServerAdapter : ISqlAdapter @@ -657,19 +702,58 @@ public int Insert(IDbConnection connection, IDbTransaction transaction, int? com { var cmd = String.Format("insert into {0} ({1}) values ({2})", tableName, columnList, parameterList); connection.Execute(cmd, entityToInsert, transaction, commandTimeout); - var r = connection.Query("select @@IDENTITY id", transaction: transaction, commandTimeout: commandTimeout); + var r = connection.Query("select @@IDENTITY id", transaction: transaction, commandTimeout: commandTimeout).ToList(); + + if (r.First().id == null) return 0; + var id = (int) r.First().id; + + var propertyInfos = keyProperties as PropertyInfo[] ?? keyProperties.ToArray(); + if (!propertyInfos.Any()) return id; + + var idProperty = propertyInfos.First(); + idProperty.SetValue(entityToInsert, Convert.ChangeType(id, idProperty.PropertyType), null); + + return id; + } + + public void AppendColumnName(StringBuilder sb, string columnName) + { + sb.AppendFormat("[{0}]", columnName); + } + + public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName) + { + sb.AppendFormat("[{0}] = @{1}", columnName, columnName); + } +} + +public partial class MySqlAdapter : ISqlAdapter +{ + public int Insert(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, String tableName, string columnList, string parameterList, IEnumerable keyProperties, object entityToInsert) + { + var cmd = String.Format("insert into {0} ({1}) values ({2})", tableName, columnList, parameterList); + connection.Execute(cmd, entityToInsert, transaction, commandTimeout); + var r = connection.Query("Select LAST_INSERT_ID()", transaction: transaction, commandTimeout: commandTimeout); var id = r.First().id; + if (id == null) return 0; var propertyInfos = keyProperties as PropertyInfo[] ?? keyProperties.ToArray(); - if (propertyInfos.Any()) - { - var idProperty = propertyInfos.First(); - if (idProperty.PropertyType.Name == "Int16") //for short id/key types issue #196 - idProperty.SetValue(entityToInsert, (Int16)id, null); - else - idProperty.SetValue(entityToInsert, (int)id, null); - } - return (int)id; + if (!propertyInfos.Any()) return id; + + var idp = propertyInfos.First(); + idp.SetValue(entityToInsert, Convert.ChangeType(id, idp.PropertyType), null); + + return id; + } + + public void AppendColumnName(StringBuilder sb, string columnName) + { + sb.AppendFormat("`{0}`", columnName); + } + + public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName) + { + sb.AppendFormat("`{0}` = @{1}", columnName, columnName); } } @@ -711,6 +795,16 @@ public int Insert(IDbConnection connection, IDbTransaction transaction, int? com } return id; } + + public void AppendColumnName(StringBuilder sb, string columnName) + { + sb.AppendFormat("\"{0}\"", columnName); + } + + public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName) + { + sb.AppendFormat("\"{0}\" = @{1}", columnName, columnName); + } } public partial class SQLiteAdapter : ISqlAdapter @@ -718,12 +812,25 @@ public partial class SQLiteAdapter : ISqlAdapter public int Insert(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, String tableName, string columnList, string parameterList, IEnumerable keyProperties, object entityToInsert) { var cmd = String.Format("insert into {0} ({1}) values ({2}); select last_insert_rowid() id", tableName, columnList, parameterList); - var r = connection.Query(cmd, entityToInsert, transaction: transaction, commandTimeout: commandTimeout); - var id = (int)r.First().id; + var multi = connection.QueryMultiple(cmd, entityToInsert, transaction, commandTimeout); + + var id = (int)multi.Read().First().id; var propertyInfos = keyProperties as PropertyInfo[] ?? keyProperties.ToArray(); - if (propertyInfos.Any()) - propertyInfos.First().SetValue(entityToInsert, id, null); + if (!propertyInfos.Any()) return id; + + var idProperty = propertyInfos.First(); + idProperty.SetValue(entityToInsert, Convert.ChangeType(id, idProperty.PropertyType), null); + return id; } + public void AppendColumnName(StringBuilder sb, string columnName) + { + sb.AppendFormat("\"{0}\"", columnName); + } + + public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName) + { + sb.AppendFormat("\"{0}\" = @{1}", columnName, columnName); + } } diff --git a/Dapper.sln b/Dapper.sln index e533306948bded73844f0216ceedf68118e6dfd6..2faff2fd581b330842a8fd06a2a00f79a265b934 100644 --- a/Dapper.sln +++ b/Dapper.sln @@ -61,8 +61,13 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapper.EntityFramework NET45 SNK", "Dapper.EntityFramework NET45 SNK\Dapper.EntityFramework NET45 SNK.csproj", "{7169A2C1-F57E-4288-B22D-52394DD2EC06}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapper.SqlBuilder NET45", "Dapper.SqlBuilder NET45\Dapper.SqlBuilder NET45.csproj", "{B83D86B2-79C0-46AA-B51B-03730256FAAC}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapper.SqlBuilder NET35", "Dapper.SqlBuilder NET35\Dapper.SqlBuilder NET35.csproj", "{13A52642-B160-4050-A101-F64FABE7AF9D}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapper.Contrib.MsSqlTests NET45", "Dapper.Contrib.MsSqlTests NET45\Dapper.Contrib.MsSqlTests NET45.csproj", "{E5482A3B-2C2A-4907-9804-C191F18FA622}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapper.Contrib.SqliteTests NET45", "Dapper.Contrib.SqliteTests NET45\Dapper.Contrib.SqliteTests NET45.csproj", "{114C32A1-D726-4F64-B8AF-AFEDE728E0BC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -293,6 +298,26 @@ Global {13A52642-B160-4050-A101-F64FABE7AF9D}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {13A52642-B160-4050-A101-F64FABE7AF9D}.Release|Mixed Platforms.Build.0 = Release|Any CPU {13A52642-B160-4050-A101-F64FABE7AF9D}.Release|x86.ActiveCfg = Release|Any CPU + {E5482A3B-2C2A-4907-9804-C191F18FA622}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E5482A3B-2C2A-4907-9804-C191F18FA622}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E5482A3B-2C2A-4907-9804-C191F18FA622}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {E5482A3B-2C2A-4907-9804-C191F18FA622}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {E5482A3B-2C2A-4907-9804-C191F18FA622}.Debug|x86.ActiveCfg = Debug|Any CPU + {E5482A3B-2C2A-4907-9804-C191F18FA622}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E5482A3B-2C2A-4907-9804-C191F18FA622}.Release|Any CPU.Build.0 = Release|Any CPU + {E5482A3B-2C2A-4907-9804-C191F18FA622}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {E5482A3B-2C2A-4907-9804-C191F18FA622}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {E5482A3B-2C2A-4907-9804-C191F18FA622}.Release|x86.ActiveCfg = Release|Any CPU + {114C32A1-D726-4F64-B8AF-AFEDE728E0BC}.Debug|Any CPU.ActiveCfg = Debug|x86 + {114C32A1-D726-4F64-B8AF-AFEDE728E0BC}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {114C32A1-D726-4F64-B8AF-AFEDE728E0BC}.Debug|Mixed Platforms.Build.0 = Debug|x86 + {114C32A1-D726-4F64-B8AF-AFEDE728E0BC}.Debug|x86.ActiveCfg = Debug|x86 + {114C32A1-D726-4F64-B8AF-AFEDE728E0BC}.Debug|x86.Build.0 = Debug|x86 + {114C32A1-D726-4F64-B8AF-AFEDE728E0BC}.Release|Any CPU.ActiveCfg = Release|x86 + {114C32A1-D726-4F64-B8AF-AFEDE728E0BC}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {114C32A1-D726-4F64-B8AF-AFEDE728E0BC}.Release|Mixed Platforms.Build.0 = Release|x86 + {114C32A1-D726-4F64-B8AF-AFEDE728E0BC}.Release|x86.ActiveCfg = Release|x86 + {114C32A1-D726-4F64-B8AF-AFEDE728E0BC}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE